Skip to main content

yo_resp/dispatch/
mod.rs

1//! From a decoded command to a written reply.
2//!
3//! This is the layer Y23 exists to keep thin. The wire and the embedded API
4//! both have to reach the same code, or there are two implementations of `INCR`
5//! and one of them is wrong. So `yo-kv` holds one method per command taking
6//! ordinary Rust values, and everything here is about the part that is only
7//! true on a socket: which keyword goes where, which combinations a real server
8//! refuses, and which of the two protocols the answer is spelled in.
9//!
10//! # What runs a command
11//!
12//! [`Server`] holds the databases. [`Session`] holds what one connection has
13//! chosen: which database, which name it gave itself, what its id is. The
14//! protocol version lives in the [`Out`] because that is what needs it, and
15//! `HELLO` changes it there.
16//!
17//! ```
18//! use yo_resp::{Argv, Limits, Out, Proto};
19//! use yo_resp::dispatch::{Args, Flow, Server, Session, execute};
20//!
21//! let mut server = Server::new();
22//! let mut session = Session::new(1);
23//! let mut out = Out::new(Proto::Resp2);
24//!
25//! let wire = b"*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n";
26//! let mut argv = Argv::new();
27//! argv.decode(wire, &Limits::default())?;
28//! let flow = execute(&mut server, &mut session, Args::new(&argv, wire), &mut out);
29//!
30//! assert_eq!(flow, Flow::Continue);
31//! assert_eq!(out.as_slice(), b"+OK\r\n");
32//! # Ok::<(), yo_resp::ProtocolError>(())
33//! ```
34//!
35//! # Errors are values until the last moment
36//!
37//! A command body returns a [`Result`], and this module turns the error into
38//! the line that goes on the wire. That is what keeps the same body usable from
39//! the embedded API, where an error is a value with a [`Code`] on it and not a
40//! sentence to be parsed.
41//!
42//! The reply buffer is rolled back to where it was before a failing command
43//! wrote anything, so a body that checks its arguments halfway through cannot
44//! leave half a reply in front of the error.
45//!
46//! # Nothing here allocates
47//!
48//! Arguments are slices of the connection's read buffer, keywords are compared
49//! in place, numbers are written straight into the reply, and the pairs of
50//! `MSET` reach the store as an iterator rather than a `Vec`. The two places
51//! that do allocate, an error message and the text of `INFO`, say so and wrap
52//! it, because a shard thread that allocates aborts.
53
54mod args;
55mod arrays;
56mod backup;
57mod bits;
58mod blocking;
59mod bloom;
60mod cms;
61mod cpu;
62mod cuckoo;
63mod geo;
64mod graph;
65mod hashes;
66mod himport;
67mod hll;
68mod indexing;
69mod json;
70mod keyspace;
71mod lists;
72mod migrate;
73mod scan;
74mod scripting;
75mod search;
76mod server;
77mod sets;
78mod streams;
79mod strings;
80pub mod table;
81mod tdigest;
82mod topk;
83mod ts;
84mod vectors;
85mod vfilter;
86mod zsets;
87
88pub use args::Args;
89pub use blocking::{Parked, Waiters};
90pub use server::parse_memory;
91pub use table::{COMMANDS, Spec, arity_ok, lookup};
92
93use crate::reply::Out;
94use std::cell::Cell;
95use std::path::{Path, PathBuf};
96use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
97use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize};
98use yo_common::lock::{Held, Lock};
99use yo_common::{Code, Error};
100use yo_kv::cold::Blocks;
101use yo_kv::{Clock, Db, Keyspace};
102use yo_search::Registry;
103
104/// How many databases a server has.
105///
106/// Redis's default is sixteen and its `databases` setting can change it. Ours
107/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
108/// constant. Nothing in the design needs the number to be fixed; nothing yet
109/// needs it not to be.
110pub const DATABASES: usize = 16;
111
112/// Every database's bit in [`Server::dirty`], which is what a fresh server
113/// starts on so that the first maintenance turn asks all of them.
114///
115/// A `u64` holds sixteen bits with room to spare, and the assertion below is
116/// what turns raising [`DATABASES`] past sixty four into a build failure rather
117/// than a shift that silently drops the databases past the end.
118const ALL_DATABASES: u64 = if DATABASES == 64 {
119    u64::MAX
120} else {
121    (1u64 << DATABASES) - 1
122};
123const _: () = assert!(DATABASES <= 64);
124
125/// How many keys one command throws away before it leaves the rest to the next.
126///
127/// A bound and not a loop to the end, because this runs in front of a client
128/// that is waiting for its reply, and a server a long way over its limit would
129/// otherwise hold that client for as long as it took to walk all the way back
130/// under. Sixty four is a batch's worth of commands, so a server that went over
131/// by what one batch allocated comes back under in one command, and a server
132/// whose limit was just cut in half works through it over the next few thousand
133/// rather than in one long stall. Redis bounds the same loop by a time slice
134/// instead of a count and hands the rest to a timer; there is no timer here, so
135/// the rest goes to the next command that runs.
136const EVICT_BUDGET: usize = 64;
137
138/// The `maxstore` a server with no storage limit carries.
139///
140/// Sixteen exabytes, which is every disk there is and then some, so a server
141/// that set a limit this high and a server that set none behave the same way and
142/// the only difference is what `CONFIG GET maxstore` says. Zero cannot be the
143/// sentinel because zero is a limit with a meaning: nothing may live on the
144/// file.
145const NO_MAXSTORE: u64 = u64::MAX;
146
147/// What a server says to a command that would allocate when it has no room.
148///
149/// Redis's `shared.oomerr`, word for word including the full stop, because
150/// clients match on the `OOM` prefix and people match on the sentence.
151const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
152
153/// What the connection should do after a command.
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum Flow {
156    /// Read the next command.
157    Continue,
158    /// Write what is buffered and then close, which is what `QUIT` asks for.
159    Close,
160    /// Nothing was written and nothing is owed yet.
161    ///
162    /// The client is on the waiter list and its reply comes when a key it named
163    /// has something in it or when its deadline passes, whichever happens first.
164    /// Until then the connection stops reading commands, because a client that
165    /// is waiting for an answer is not a client that has sent another question.
166    Block,
167}
168
169/// A number one thread adds to and any thread may read.
170///
171/// The add is a load, an add and a store rather than a fetch and add, which on
172/// x86 is three ordinary instructions instead of one locked one. That is sound
173/// because every counter here has exactly one writer, which is what the slots
174/// below are for: two threads never hold the same counter, so nothing can be
175/// lost between the load and the store. A reader can be a command or two behind,
176/// and `INFO` on a running server is behind by the time the reply reaches the
177/// client anyway.
178#[derive(Debug, Default)]
179pub struct Counter(AtomicU64);
180
181impl Counter {
182    /// One more.
183    fn bump(&self) {
184        self.0.store(self.get().wrapping_add(1), Relaxed);
185    }
186
187    /// One fewer, stopping at zero.
188    ///
189    /// The floor is for the gauge, which is the number of open connections: a
190    /// close that arrives without its open, which nothing can do now and a
191    /// misplaced call could, is a number that stays at zero rather than one
192    /// that wraps to eighteen quintillion clients.
193    fn drop_one(&self) {
194        self.0.store(self.get().saturating_sub(1), Relaxed);
195    }
196
197    /// What it says.
198    fn get(&self) -> u64 {
199        self.0.load(Relaxed)
200    }
201
202    /// Back to zero, which is `CONFIG RESETSTAT`.
203    fn zero(&self) {
204        self.0.store(0, Relaxed);
205    }
206}
207
208/// The numbers `INFO` reports that this layer cannot see for itself.
209///
210/// The reactor owns the sockets, so the reactor is what knows how many clients
211/// there are. It counts them here and nothing else does anything with them
212/// except report them.
213#[derive(Debug, Default)]
214pub struct Stats {
215    /// Connections open right now.
216    clients: Counter,
217    /// Connections accepted since the server started.
218    connections: Counter,
219    /// Commands run since the server started, which this layer counts itself.
220    commands: Counter,
221}
222
223impl Stats {
224    /// A connection arrived.
225    pub fn opened(&self) {
226        self.clients.bump();
227        self.connections.bump();
228    }
229
230    /// A connection went away.
231    pub fn closed(&self) {
232        self.clients.drop_one();
233    }
234}
235
236/// Every thread's [`Stats`] added together, which is what `INFO` answers.
237#[derive(Debug, Clone, Copy, Default)]
238pub struct Totals {
239    /// Connections open right now.
240    pub clients: u64,
241    /// Connections accepted since the server started.
242    pub connections: u64,
243    /// Commands run since the server started.
244    pub commands: u64,
245}
246
247thread_local! {
248    /// Which set of counters the running thread writes into.
249    ///
250    /// Claimed the first time a thread counts anything and kept for as long as
251    /// the thread runs. It is a number rather than a pointer, so a thread that
252    /// has counted on one server and then counts on another lands in the same
253    /// place in both, and a process with two servers in it shares the numbering
254    /// between them. That is the tests and it is not `yodb`, which has one.
255    static SLOT: Cell<usize> = const { Cell::new(usize::MAX) };
256}
257
258/// What one thread keeps to itself.
259///
260/// One of these per thread and not one per server, because a number every
261/// thread writes to is a cache line every thread has to own to write to it, and
262/// at a few million commands a second that one line is the server. So each
263/// thread writes into its own and whoever needs the whole picture, which is
264/// `INFO` and the maintenance turn, puts the pieces together when it asks.
265///
266/// A cache line apart for the same reason, so that two threads writing at once
267/// are not two threads passing one line back and forth.
268#[derive(Debug, Default)]
269#[repr(align(64))]
270struct Local {
271    /// What the reactor counts.
272    stats: Stats,
273    /// A counter per command, for `INFO commandstats`.
274    cmdstats: CommandStats,
275    /// Which databases this thread has run a command against since the
276    /// maintenance turn last took the mask.
277    ///
278    /// One bit per database. The thread ors into it and the turn takes the whole
279    /// of it with a swap, which is what keeps a mark that lands during the swap
280    /// from being lost: the worst that can happen is a bit the turn has already
281    /// taken being set again, and that costs one more look at a database with
282    /// nothing to collect.
283    dirty: AtomicU64,
284}
285
286impl Local {
287    /// Note that a command has run against these databases.
288    fn mark(&self, dbs: u64) {
289        self.dirty.store(self.dirty.load(Relaxed) | dbs, Relaxed);
290    }
291}
292
293/// Room for one thread, which is what a server starts with.
294fn one_thread() -> Box<[Local]> {
295    slots(1)
296}
297
298/// Room for `threads` of them.
299fn slots(threads: usize) -> Box<[Local]> {
300    (0..threads.max(1)).map(|_| Local::default()).collect()
301}
302
303/// Where the process was started, which is what `dir` defaults to.
304///
305/// A dot if the working directory cannot be read, which happens when it has
306/// been deleted out from under a running process. That is not a reason to
307/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
308/// from the filesystem if anybody asks for one.
309fn working_dir() -> PathBuf {
310    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
311}
312
313/// One command's counters, for `INFO commandstats`.
314///
315/// Three of Redis's five. `usec` and `usec_per_call` are not here because
316/// nothing times a command, and timing one means two clock reads around a call
317/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
318/// has room for it; this does not, and a zero under a name that says microseconds
319/// is worse than an absent field, which is the same rule the rest of `INFO`
320/// follows.
321#[derive(Debug, Clone, Copy, Default)]
322pub struct CommandStat {
323    /// Times the command ran, whatever it answered.
324    pub calls: u64,
325    /// Times it was turned away before it ran, which is the wrong number of
326    /// arguments or no room under `maxmemory`.
327    pub rejected: u64,
328    /// Times it ran and answered with an error.
329    pub failed: u64,
330}
331
332impl CommandStat {
333    /// Whether this command has ever been seen.
334    ///
335    /// A row that has not is left out of the reply, which is what Redis does and
336    /// is why the section is a handful of lines on a working server rather than
337    /// one line per command in the table.
338    const fn seen(&self) -> bool {
339        self.calls != 0 || self.rejected != 0 || self.failed != 0
340    }
341}
342
343/// One command's counters as one thread keeps them.
344///
345/// The same three numbers as [`CommandStat`], which is what they add up to when
346/// `INFO` asks. This is the written form and that is the read one.
347#[derive(Debug, Default)]
348struct Row {
349    /// Times the command ran.
350    calls: Counter,
351    /// Times it was turned away before it ran.
352    rejected: Counter,
353    /// Times it ran and answered with an error.
354    failed: Counter,
355}
356
357/// A counter per command, indexed the way [`table::index_of`] says.
358///
359/// A flat array and not a map, because the dispatcher is already holding the
360/// spec and the spec's position in the table is two addresses subtracted. That
361/// makes the counting a load, an add and a store on a row the previous command
362/// of the same name has already pulled into cache.
363#[derive(Debug)]
364struct CommandStats(Box<[Row]>);
365
366impl Default for CommandStats {
367    fn default() -> CommandStats {
368        CommandStats((0..table::count()).map(|_| Row::default()).collect())
369    }
370}
371
372impl CommandStats {
373    /// The row for one command.
374    fn at(&self, spec: &'static Spec) -> &Row {
375        &self.0[table::index_of(spec)]
376    }
377}
378
379/// Where a database gets its store from, asked by database number.
380///
381/// `None` means that database cannot have one. The caller owns whatever the
382/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
383/// database, and this crate never learns what any of that is.
384pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
385
386/// Everything a server holds.
387///
388/// One of these per shard thread, not one per process: the databases inside are
389/// not `Sync` and are reached by sending their thread a command. What makes
390/// this a server rather than a shard is that it is the whole of what a
391/// connection can address.
392pub struct Server {
393    dbs: Vec<Db>,
394    /// How many stripes each database is cut into, the same for all of them.
395    ///
396    /// Kept here as well as in each database so that the flat slot arithmetic
397    /// below is a multiply and a divide against a field on the server rather
398    /// than a walk asking each database how wide it is.
399    width: usize,
400    clock: Clock,
401    started_ms: u64,
402    /// Where the next maintenance turn starts looking, so that a database
403    /// under constant write load cannot hold the other fifteen's space.
404    next_db: usize,
405    /// One bit per database, set when a command ran against it.
406    ///
407    /// The maintenance turn after every batch used to ask all sixteen
408    /// databases whether they had anything to collect, and asking costs a load
409    /// and a store in each one. Fifteen of those are cold lines on a server
410    /// where every client is on database zero, which is every server, and the
411    /// answer is no every time. This is the cheap half of the question: a
412    /// database nobody has touched since it last said no cannot have started
413    /// saying yes.
414    ///
415    /// The maintenance turn's own mask and not a shared one. Threads mark what
416    /// they have touched in [`Local::dirty`] and the turn takes those with
417    /// [`Server::collect_marks`] before it reads this, so nothing on a command
418    /// path writes here.
419    dirty: u64,
420    /// What the connections are holding, kept by the engine.
421    conn_bytes: usize,
422    /// The `maxmemory` limit in bytes, zero when there is not one.
423    ///
424    /// Zero is the default and it is the whole reason the check in front of
425    /// every write is one comparison against a field that is already warm. It
426    /// is read by every command on every thread and written by a client that
427    /// sends `CONFIG SET`, so it is a number the threads can share rather than
428    /// a field one of them owns.
429    maxmemory: AtomicU64,
430    /// Where a database gets a store from the first time it needs one.
431    ///
432    /// A closure and not a store, because there are sixteen databases and a
433    /// server that fills memory on database zero should not have opened
434    /// anything for the other fifteen. Nothing is asked of this until a memory
435    /// limit is actually reached, so a server that never fills memory never
436    /// opens a file, and a server that has no file never has one of these.
437    ///
438    /// `None` from the closure means that database cannot have one, which is
439    /// how the caller says the file it opened has no more room for logs.
440    store: Option<Box<StoreSource>>,
441    /// The `maxstore` limit in bytes, `None` when there is not one.
442    ///
443    /// The storage limit, and the other half of the inversion `14` section 4.1
444    /// describes. `maxmemory` is a limit on memory and the right answer to a
445    /// memory limit on a system with a file under it is to move data to the
446    /// file, not to delete it. Deleting is the right answer to a limit on the
447    /// file, and this is that limit.
448    ///
449    /// Zero is not "no limit" here, which is the one place this reads
450    /// differently from `maxmemory` and is the difference that makes a drop in
451    /// cache possible. A storage budget of zero bytes means nothing may live on
452    /// the file, so migration cannot make room and eviction is the only thing
453    /// left, which is Redis exactly. `None` is no limit and is the default,
454    /// which with `noeviction` means the database grows until the disk is full
455    /// and then writes fail, which is what a database does.
456    ///
457    /// Shared between the threads the same way `maxmemory` is, and no limit is
458    /// [`NO_MAXSTORE`] rather than a second field saying whether the first one
459    /// counts. Two fields cannot be read as one, and a limit that was on when
460    /// the bytes were read and off by the time the number was is a limit that
461    /// answers from a server that never existed.
462    maxstore: AtomicU64,
463    /// What [`Server::memory_bytes`] said at the last maintenance turn.
464    ///
465    /// The reading is a walk over every collection in every database and cannot
466    /// go on a command path, so the command path reads this instead and is at
467    /// most one batch behind. What that costs is overshoot: a server can end a
468    /// batch holding one batch's worth of allocation more than its limit before
469    /// anything notices. A batch is 64 commands, so that is bounded by what 64
470    /// commands can allocate and not by how long the server runs.
471    ///
472    /// Only kept up to date when there is a limit to judge it against. A server
473    /// with no `maxmemory` never reads it and never pays for it.
474    used: usize,
475    /// Which database the next eviction draws from.
476    ///
477    /// Its own cursor and not [`Server::next_db`], because eviction and
478    /// compaction move at different rates and sharing one would make the
479    /// database that gets compacted depend on how many keys were evicted.
480    evict_db: usize,
481    /// Which database the next active expiry sweep starts at.
482    ///
483    /// A third cursor for the same reason there is a second one. A sweep runs on
484    /// every turn of the loop and compaction runs when there is dead space, so
485    /// sharing a cursor would make which database gets swept depend on which one
486    /// was last collected.
487    expire_db: usize,
488    /// The millisecond the last active expiry sweep ran on, so the next one on
489    /// the same millisecond does not bother.
490    expire_ms: u64,
491    /// Clients parked on a blocking command.
492    ///
493    /// Behind a lock because a client parks on the thread that ran its command
494    /// and is woken by whichever thread later puts something under a key it
495    /// named, and those are not the same thread. The lock is only ever taken to
496    /// park somebody, to serve somebody or to forget a connection that has gone,
497    /// so a command that does not block never touches it.
498    waiters: Lock<Waiters>,
499    /// How many clients are parked.
500    ///
501    /// Beside the list rather than read out of it, because every command asks
502    /// whether anybody is waiting and nearly every answer is no. Taking a lock
503    /// to be told no would be a cache line every thread has to own to ask, which
504    /// is the cost the list was put behind a lock to avoid.
505    ///
506    /// Written under the lock, by whoever changed the list, so the number and
507    /// the list agree except while a change is in progress. A reader that asks
508    /// during one is told about the moment before it, and the worst that costs
509    /// is a walk of the list that serves nobody or one that has not started yet
510    /// and happens on the next command instead.
511    parked: AtomicUsize,
512    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
513    ///
514    /// Empty on a server nobody has migrated a key out of, which is nearly all
515    /// of them, and it costs a vector's three words to be empty.
516    ///
517    /// Behind a lock because a socket cannot be written by two threads at once
518    /// and a cache of them cannot be searched by one while another is taking an
519    /// entry out. It is held for the whole of a migration, which is a round trip
520    /// to another server, so two threads migrating at the same time take turns.
521    /// That is the right way round: the alternative is a socket per thread per
522    /// peer, and a `MIGRATE` is not what a server spends its time on.
523    peers: Lock<migrate::Peers>,
524    /// What each thread that runs commands here keeps to itself.
525    ///
526    /// A fixed list, because a thread reading its own entry must not have the
527    /// list move under it, and how many threads there will be is known before
528    /// any of them starts. A server nobody told otherwise has one.
529    locals: Box<[Local]>,
530    /// How many entries have been handed out.
531    claimed: AtomicUsize,
532    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
533    ///
534    /// Absolute, and resolved once when the server is built rather than every
535    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
536    /// entitled to hand one of them to a copy tool, so a relative path that
537    /// meant something different after a `chdir` would be a path that stops
538    /// working for reasons nobody could see.
539    dir: PathBuf,
540    /// What backup is running, if one is.
541    ///
542    /// On the server and not on a session, because a backup outlives the
543    /// connection that asked for it and any other connection can seal it.
544    ///
545    /// Behind a lock because there is one backup at a time and any thread can be
546    /// the one that starts, seals or abandons it. It is held while the base file
547    /// is written, which is what keeps two `BACKUP START` commands from writing
548    /// over each other's files.
549    backup: Lock<backup::State>,
550    /// Whether a sealed backup is sitting on disk.
551    ///
552    /// Beside the state rather than read out of it, because every batch of
553    /// commands asks whether there is a backup old enough to sweep away and on
554    /// nearly every server the answer is that there is no backup at all. A load
555    /// answers that. Written under the lock by whoever moved the phase, so a
556    /// reader that asks mid-change sees the moment before and sweeps one batch
557    /// later, which is a file staying on disk for a few microseconds longer than
558    /// it had to.
559    sealed: AtomicBool,
560    /// The search indexes and the names pointing at them.
561    ///
562    /// On the server and not on a database, which is the one collection in this
563    /// build that is. A real server keeps its indexes in the search module, the
564    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
565    /// indexes made on database zero. `search.rs` has the rest of why.
566    ///
567    /// A server nobody has made an index on holds two empty vectors here, which
568    /// is six words and no allocation.
569    ///
570    /// Behind a lock because an index is made and dropped by whichever thread
571    /// ran the command, and the table it goes in is one table. Only the `FT`
572    /// commands take it, so nothing a working server spends its time on comes
573    /// through here.
574    search: Lock<Registry>,
575    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
576    ///
577    /// A flag rather than an exit, because the command layer is not what owns
578    /// the process. It runs inside a batch that has other commands behind it
579    /// and inside a driver that has a socket file to take away and a file to
580    /// close, and a server that calls `exit` from a command handler skips all
581    /// of that. So the command says stop and the driver stops, on the same turn
582    /// and through the same door a signal uses.
583    stopping: AtomicBool,
584}
585
586impl Server {
587    /// A server with [`DATABASES`] empty databases on the system clock.
588    #[must_use]
589    pub fn new() -> Server {
590        let clock = Clock::system();
591        Server {
592            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
593            width: 1,
594            clock,
595            started_ms: clock.now_ms(),
596            next_db: 0,
597            dirty: ALL_DATABASES,
598            conn_bytes: 0,
599            maxmemory: AtomicU64::new(0),
600            store: None,
601            maxstore: AtomicU64::new(NO_MAXSTORE),
602            used: 0,
603            evict_db: 0,
604            expire_db: 0,
605            expire_ms: 0,
606            waiters: Lock::default(),
607            parked: AtomicUsize::new(0),
608            peers: Lock::default(),
609            locals: one_thread(),
610            claimed: AtomicUsize::new(0),
611            dir: working_dir(),
612            backup: Lock::default(),
613            sealed: AtomicBool::new(false),
614            search: Lock::new(Registry::new()),
615            stopping: AtomicBool::new(false),
616        }
617    }
618
619    /// A server whose databases are cut into `width` stripes each.
620    ///
621    /// Not reachable from the command line yet. Every command group answers on
622    /// a server of any width now and so does everything that walks a whole
623    /// database, and the tests run each group at a width of one and a width of
624    /// eight and check the two agree.
625    ///
626    /// What is left before this is what `--threads` sets is the engine. A
627    /// database being several objects is what makes more than one thread
628    /// possible, and it is not what makes more than one thread happen.
629    #[must_use]
630    pub fn with_width(width: usize) -> Server {
631        let clock = Clock::system();
632        let mut server = Server::new();
633        server.dbs = (0..DATABASES)
634            .map(|_| Db::with_clock(clock, width))
635            .collect();
636        server.width = server.dbs[0].width();
637        server
638    }
639
640    /// A server on a clock the caller moves by hand, for tests.
641    #[must_use]
642    pub fn with_clock(clock: Clock) -> Server {
643        Server {
644            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
645            width: 1,
646            clock,
647            started_ms: clock.now_ms(),
648            next_db: 0,
649            dirty: ALL_DATABASES,
650            conn_bytes: 0,
651            maxmemory: AtomicU64::new(0),
652            store: None,
653            maxstore: AtomicU64::new(NO_MAXSTORE),
654            used: 0,
655            evict_db: 0,
656            expire_db: 0,
657            expire_ms: 0,
658            waiters: Lock::default(),
659            parked: AtomicUsize::new(0),
660            peers: Lock::default(),
661            locals: one_thread(),
662            claimed: AtomicUsize::new(0),
663            dir: working_dir(),
664            backup: Lock::default(),
665            sealed: AtomicBool::new(false),
666            search: Lock::new(Registry::new()),
667            stopping: AtomicBool::new(false),
668        }
669    }
670
671    /// One database, by index.
672    ///
673    /// A caller that knows which key it wants names the one stripe the key is
674    /// on rather than working over the whole thing, which is what `at` and its
675    /// neighbours on [`Db`] are for. A caller that is about a database rather
676    /// than about a key, which is the snapshot walk and a setting, works over
677    /// all of them.
678    ///
679    /// The database is marked as having had something run against it, which is
680    /// what this does that [`Server::striped_ref`] does not. Anything that only
681    /// reads asks for that one and leaves the mark alone.
682    ///
683    /// The borrow is shared, and what makes that enough is that a database is
684    /// several stripes behind a lock each. A caller that wants to change
685    /// something holds the stripe it is changing, so two threads working on two
686    /// keys work at once and two working on one key take turns, which is the
687    /// whole point of cutting a database up.
688    ///
689    /// # Panics
690    ///
691    /// If `i` is not a database. `SELECT` is the only way a client changes the
692    /// index and it checks, so an index that is out of range here is a bug in
693    /// the caller and not something a client can ask for.
694    pub fn striped(&self, i: usize) -> &Db {
695        self.mine().mark(1u64 << i);
696        &self.dbs[i]
697    }
698
699    /// Every keyspace on the server, which is every stripe of every database.
700    ///
701    /// What the aggregates walk. A total over the whole server is a total over
702    /// all of these and the stripe boundaries do not appear in it, which is
703    /// what makes the numbers `INFO` reports the same numbers whatever the
704    /// server was cut into.
705    fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
706        self.dbs
707            .iter()
708            .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
709    }
710
711    /// The same, mutably.
712    fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
713        self.dbs.iter_mut().flat_map(Db::stripes_mut)
714    }
715
716    /// How many keyspaces there are, counting every stripe of every database.
717    ///
718    /// The maintenance turns walk these rather than the databases, because a
719    /// stripe is the thing that holds an arena and a deadline heap and so it is
720    /// the thing that has anything to collect.
721    const fn slots(&self) -> usize {
722        DATABASES * self.width
723    }
724
725    /// Which database slot `i` belongs to.
726    const fn slot_db(&self, i: usize) -> usize {
727        i / self.width
728    }
729
730    /// Keyspace `i` of [`Server::slots`].
731    fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
732        let (db, stripe) = (i / self.width, i % self.width);
733        self.dbs[db].stripe_mut(stripe)
734    }
735
736    /// The same, without taking it mutably.
737    fn slot(&self, i: usize) -> Held<'_, Keyspace> {
738        let (db, stripe) = (i / self.width, i % self.width);
739        self.dbs[db].hold_stripe(stripe)
740    }
741
742    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
743    #[must_use]
744    pub fn dir(&self) -> &Path {
745        &self.dir
746    }
747
748    /// Point the server at a different directory, which `yodb serve --dir` does.
749    ///
750    /// Only before it is serving. There is no `CONFIG SET dir` here and there
751    /// is none on a real server either without turning protected configs on,
752    /// for the good reason that moving it out from under a running backup would
753    /// leave files nothing can find again.
754    pub fn set_dir(&mut self, dir: PathBuf) {
755        self.dir = dir;
756    }
757
758    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
759    ///
760    /// Once per batch, from the same maintenance turn that collects the arena.
761    /// It reads two fields and returns on a server that has never taken a
762    /// backup, which is nearly all of them.
763    pub fn backup_expire(&self) {
764        backup::expire(self);
765    }
766
767    /// Ask for the server to stop, which is what `SHUTDOWN` does.
768    ///
769    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
770    /// or ends the process, because none of those belong to this layer, and a
771    /// batch that is halfway through still has to finish and be written out.
772    pub fn stop(&self) {
773        self.stopping.store(true, Release);
774    }
775
776    /// Whether somebody has asked the server to stop.
777    ///
778    /// Read once per turn by the loop, next to the flag a signal sets. The two
779    /// mean the same thing and are separate only because one arrives from the
780    /// operating system and the other from a client.
781    #[must_use]
782    pub fn stopping(&self) -> bool {
783        self.stopping.load(Acquire)
784    }
785
786    /// One database, by index, without taking it mutably.
787    ///
788    /// What the prefetch stage needs. It runs for all 64 commands in a batch
789    /// before any of them executes, so it cannot hold the mutable borrow `run`
790    /// is about to want, and it does not need one: warming a cache line reads
791    /// nothing and changes nothing.
792    #[must_use]
793    pub fn striped_ref(&self, i: usize) -> &Db {
794        &self.dbs[i]
795    }
796
797    /// The stripe that answers for a database when a setting is read back.
798    ///
799    /// A ladder setting and an eviction policy are one number on a real server,
800    /// and the fact that every stripe of every database carries a copy of it is
801    /// ours rather than the client's problem. A write puts the same value on
802    /// every one of them, so any stripe answers for all of them and this is the
803    /// first one.
804    fn settings(&self) -> Held<'_, Keyspace> {
805        self.dbs[0].hold_stripe(0)
806    }
807
808    /// Take a new clock reading and give it to every database.
809    ///
810    /// Once per turn of the event loop, which is the only place time moves. A
811    /// command asking what the time is gets the answer the whole batch got, so
812    /// two keys written by the same batch expire together (`04` section 3).
813    pub fn refresh_clock(&mut self) {
814        self.clock.refresh();
815        let now = self.clock.now_ms();
816        for db in &mut self.dbs {
817            db.set_clock_ms(now);
818        }
819    }
820
821    /// Move every clock here on by `ms`, for tests about expiry.
822    ///
823    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
824    /// except that it moves from wherever the clock is rather than to a stated
825    /// moment, which is what a test that wants a key to have expired asks for.
826    pub fn advance_clock_ms(&mut self, ms: u64) {
827        let now = self.clock.now_ms() + ms;
828        self.set_clock_ms(now);
829    }
830
831    /// Move every clock here to `ms` by hand, for tests about expiry.
832    ///
833    /// A test cannot wait a hundred seconds and a test that waits a hundred
834    /// milliseconds is a test that fails on a loaded machine, so time moves on
835    /// request. The system clock underneath will overwrite this on the next
836    /// [`Server::refresh_clock`], which is why this is only useful in a test
837    /// that drives commands directly rather than through the event loop.
838    pub fn set_clock_ms(&mut self, ms: u64) {
839        self.clock.set(ms);
840        for db in &mut self.dbs {
841            db.set_clock_ms(ms);
842        }
843    }
844
845    /// Seconds since this server was built.
846    #[must_use]
847    pub fn uptime_secs(&self) -> u64 {
848        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
849    }
850
851    /// Bytes held by every database's index and arena, plus the read and reply
852    /// buffers of every connection.
853    ///
854    /// The buffers are in here because they are real and because Redis counts
855    /// its own, so leaving them out would make the one number people compare
856    /// flattering rather than true. They are not a database, so nothing in the
857    /// keyspace can change them and the engine has to say when they move.
858    #[must_use]
859    pub fn memory_bytes(&self) -> usize {
860        self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes
861    }
862
863    /// What the keyspace itself is holding, live records only.
864    ///
865    /// `used_memory` minus this is what the store costs to run: the index, the
866    /// space dead records are sitting in until compaction gets to them, and the
867    /// connections' buffers.
868    #[must_use]
869    pub fn dataset_bytes(&self) -> usize {
870        self.keyspaces()
871            .map(|db| db.map().arena().live_bytes() as usize)
872            .sum()
873    }
874
875    /// Bytes the arenas are holding, live and dead together.
876    #[must_use]
877    pub fn arena_bytes(&self) -> usize {
878        self.keyspaces()
879            .map(|db| db.map().arena().reserved_bytes() as usize)
880            .sum()
881    }
882
883    /// Bytes the indexes are holding.
884    #[must_use]
885    pub fn index_bytes(&self) -> usize {
886        self.keyspaces()
887            .map(|db| db.map().index().memory_bytes())
888            .sum()
889    }
890
891    /// What arena compaction has cost, across every database.
892    ///
893    /// The write amplification of value separation, which is invisible from the
894    /// outside otherwise: a client that writes a megabyte can leave the store
895    /// copying several more, and the only sign of it without these is that the
896    /// writes got slower.
897    #[must_use]
898    pub fn compaction(&self) -> yo_kv::Compaction {
899        self.keyspaces().map(|db| db.map().compaction()).fold(
900            yo_kv::Compaction::default(),
901            |a, b| yo_kv::Compaction {
902                walked: a.walked + b.walked,
903                moved: a.moved + b.moved,
904                bytes: a.bytes + b.bytes,
905            },
906        )
907    }
908
909    /// Arena segments whose pages are real, across every database.
910    #[must_use]
911    pub fn segment_count(&self) -> usize {
912        self.keyspaces()
913            .map(|db| db.map().arena().resident_segments())
914            .sum()
915    }
916
917    /// What the connections' read and reply buffers are holding.
918    #[must_use]
919    pub const fn conn_bytes(&self) -> usize {
920        self.conn_bytes
921    }
922
923    /// Note that the connections are holding `delta` bytes more than they were,
924    /// or fewer when it is negative.
925    ///
926    /// A delta and not a total because the alternative is a walk over every
927    /// connection, and the walk would have to happen on a turn of the loop
928    /// rather than when `INFO` asks, which puts the cost of a report on the
929    /// command path of a server nobody is asking.
930    pub fn note_conn_bytes(&mut self, delta: isize) {
931        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
932    }
933
934    /// Keys reclaimed by running into them after their deadline.
935    #[must_use]
936    pub fn expired_keys(&self) -> u64 {
937        self.keyspaces().map(|db| db.expired_keys()).sum()
938    }
939
940    /// Keys thrown away to make room, which is the other number entirely.
941    #[must_use]
942    pub fn evicted_keys(&self) -> u64 {
943        self.keyspaces().map(|db| db.evicted_keys()).sum()
944    }
945
946    /// Every command that has been seen, with its counters.
947    ///
948    /// Only the ones that have. A server reports a handful of lines rather than
949    /// one per command in the table, which is what Redis does and is the
950    /// difference between a section a person can read and one they cannot.
951    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
952        (0..table::count())
953            .map(|at| (table::name_at(at), self.command_stat(at)))
954            .filter(|(_, row)| row.seen())
955    }
956
957    /// One command's counters, added up over every thread.
958    fn command_stat(&self, at: usize) -> CommandStat {
959        let mut sum = CommandStat::default();
960        for thread in &self.locals {
961            let row = &thread.cmdstats.0[at];
962            sum.calls += row.calls.get();
963            sum.rejected += row.rejected.get();
964            sum.failed += row.failed.get();
965        }
966        sum
967    }
968
969    /// The counters the calling thread writes into.
970    ///
971    /// The first call on a thread claims a set and every call after it is a
972    /// thread local read and an index. A server asked to count from more threads
973    /// than it was built for wraps round and shares a set, which loses the odd
974    /// count between two threads and cannot happen to a server `yodb serve`
975    /// built, because that one is told how many threads it will have before it
976    /// starts any of them.
977    pub fn counted(&self) -> &Stats {
978        &self.mine().stats
979    }
980
981    /// Everything the calling thread keeps to itself.
982    fn mine(&self) -> &Local {
983        let mut slot = SLOT.get();
984        if slot == usize::MAX {
985            slot = self.claimed.fetch_add(1, Relaxed);
986            SLOT.set(slot);
987        }
988        &self.locals[slot % self.locals.len()]
989    }
990
991    /// Every thread's numbers added together, which is what `INFO` reports.
992    #[must_use]
993    pub fn totals(&self) -> Totals {
994        let mut sum = Totals::default();
995        for thread in &self.locals {
996            sum.clients += thread.stats.clients.get();
997            sum.connections += thread.stats.connections.get();
998            sum.commands += thread.stats.commands.get();
999        }
1000        sum
1001    }
1002
1003    /// Put the totals back to zero, which is `CONFIG RESETSTAT`.
1004    ///
1005    /// Every thread's set and not only the one asking, since the number the
1006    /// client is resetting is the sum it was just shown. The open connections
1007    /// are left alone because that is a gauge and not a total: the connections
1008    /// are still open.
1009    pub fn reset_stats(&self) {
1010        for thread in &self.locals {
1011            thread.stats.connections.zero();
1012            thread.stats.commands.zero();
1013        }
1014    }
1015
1016    /// Say how many threads will run commands here, before any of them does.
1017    ///
1018    /// What it changes is how many sets of counters there are. Called once at
1019    /// startup by whoever is about to start the threads, and calling it on a
1020    /// running server throws away what has been counted so far, which is why it
1021    /// wants the server to itself.
1022    pub fn set_threads(&mut self, threads: usize) {
1023        self.locals = slots(threads);
1024        self.claimed = AtomicUsize::new(0);
1025    }
1026
1027    /// The `maxmemory` limit in bytes, zero when there is not one.
1028    #[must_use]
1029    pub fn maxmemory(&self) -> u64 {
1030        self.maxmemory.load(Relaxed)
1031    }
1032
1033    /// Set the limit, and take a reading straight away.
1034    ///
1035    /// The reading is here rather than left to the next maintenance turn because
1036    /// a client that sets the limit and sends a write in the same batch expects
1037    /// the write to be judged against the limit it just set, and because the
1038    /// cached number is meaningless until the first time there is a limit to
1039    /// compare it with.
1040    ///
1041    /// Turning the limit on also turns on the running total every slab keeps of
1042    /// what its collections hold, and turning it off turns that back off, so a
1043    /// server with no limit is not paying to count something nobody reads. The
1044    /// first reading after switching it on is the walk that the total starts
1045    /// from, and it is the only walk.
1046    pub fn set_maxmemory(&mut self, bytes: u64) {
1047        self.maxmemory.store(bytes, Relaxed);
1048        for db in &mut self.dbs {
1049            db.track_memory(bytes != 0);
1050        }
1051        self.used = self.settled_memory();
1052    }
1053
1054    /// Say where a database should get its store from when it needs one.
1055    ///
1056    /// This is what turns the eviction inversion on. Until it is called every
1057    /// database answers a memory limit by evicting, which is Redis, and after it
1058    /// is called a database under memory pressure moves values to whatever the
1059    /// closure hands back instead of throwing keys away.
1060    ///
1061    /// Called at most once per database and only under pressure, so a server
1062    /// that is given a file and never fills memory never touches it.
1063    pub fn set_store_source(
1064        &mut self,
1065        source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
1066    ) {
1067        self.store = Some(Box::new(source));
1068    }
1069
1070    /// Whether this server has been given somewhere to put cold values.
1071    #[must_use]
1072    pub const fn has_store_source(&self) -> bool {
1073        self.store.is_some()
1074    }
1075
1076    /// Open database `at`'s store, if it has not got one and there is one to be
1077    /// had.
1078    ///
1079    /// A store that will not open leaves the database where it was, which is
1080    /// evicting, because a memory limit that cannot be answered by moving data
1081    /// still has to be answered.
1082    fn attach_store(&mut self, at: usize) {
1083        if self.slot(at).store_bytes().is_some() {
1084            return;
1085        }
1086        let Some(source) = self.store.as_mut() else {
1087            return;
1088        };
1089        if let Some(blocks) = source(at) {
1090            self.slot_mut(at).attach(blocks);
1091        }
1092    }
1093
1094    /// The `maxstore` limit in bytes, `None` when there is not one.
1095    #[must_use]
1096    pub fn maxstore(&self) -> Option<u64> {
1097        match self.maxstore.load(Relaxed) {
1098            NO_MAXSTORE => None,
1099            bytes => Some(bytes),
1100        }
1101    }
1102
1103    /// Set the storage limit, or clear it with `None`.
1104    ///
1105    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
1106    /// total, because this limit is compared against a number the store keeps
1107    /// and answers on demand, not against a walk.
1108    pub fn set_maxstore(&self, bytes: Option<u64>) {
1109        self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1110    }
1111
1112    /// What every attached store is holding, for `INFO memory`.
1113    ///
1114    /// Zero on a server with nothing attached, which is not the same as a server
1115    /// whose file is empty, and [`Server::regime`] is the field that tells those
1116    /// two apart.
1117    #[must_use]
1118    pub fn store_bytes(&self) -> u64 {
1119        self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1120    }
1121
1122    /// What the file has been asked to do, added up over every database.
1123    ///
1124    /// Counters and not levels, so they only ever go up and a run is the
1125    /// difference between two readings. G9 is a ratio over these: the faults a
1126    /// run took, divided by the point reads it issued, has to come out at 1.05
1127    /// or less with a working set ten times memory. There is no way to work that
1128    /// out from outside the server, so it is reported rather than inferred.
1129    ///
1130    /// A fault is a read that went to the store. Whether it also went to the
1131    /// device depends on the store: a log serves a read out of a resident page
1132    /// without touching anything. At ten times memory almost every fault is a
1133    /// real read, which is why the gate is written against this number, but the
1134    /// two are not the same thing and a run tight against the bar should be
1135    /// checked against what the operating system says.
1136    #[must_use]
1137    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1138        let mut total = yo_kv::tier::Stats::default();
1139        for db in self.keyspaces() {
1140            let Some(tier) = db.tier() else { continue };
1141            let s = tier.stats();
1142            total.demoted += s.demoted;
1143            total.promoted += s.promoted;
1144            total.faults += s.faults;
1145            total.served += s.served;
1146            total.bytes_out += s.bytes_out;
1147            total.bytes_in += s.bytes_in;
1148        }
1149        total
1150    }
1151
1152    /// Which way this server answers a memory limit, in one word for `INFO`.
1153    ///
1154    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
1155    /// inversion: a memory limit moves values to the file and nothing stored is
1156    /// lost. A server reports one word rather than leaving an operator to work
1157    /// it out from a limit, a setting and whether a file happens to be open.
1158    #[must_use]
1159    pub fn regime(&self) -> &'static str {
1160        if (0..self.slots()).any(|at| self.migrates(at)) {
1161            "migrate"
1162        } else {
1163            "evict"
1164        }
1165    }
1166
1167    /// Whether database `at` answers a memory limit by moving values to the
1168    /// file rather than by throwing keys away.
1169    ///
1170    /// Three things have to hold. There has to be somewhere to move them, which
1171    /// is a store attached to that database or a source that can open one, and
1172    /// on a server that was never given a file this is false everywhere and
1173    /// every database behaves exactly as it did.
1174    /// The storage budget has to be more than nothing, which is what
1175    /// `maxstore 0` says it is not. And the file has to be under that budget,
1176    /// because a full file is a storage limit reached and eviction is the right
1177    /// answer to a storage limit.
1178    fn migrates(&self, at: usize) -> bool {
1179        let cap = self.maxstore();
1180        if cap == Some(0) {
1181            return false;
1182        }
1183        // Out of the stripe first. A match keeps whatever it is looking at
1184        // alive for the whole of itself, and that would be this stripe held
1185        // across the arms for no reason.
1186        let bytes = self.slot(at).store_bytes();
1187        match bytes {
1188            Some(held) => cap.is_none_or(|cap| held < cap),
1189            // Nothing attached, but somewhere to get one from the moment this
1190            // database needs it, which is what makes the answer yes rather than
1191            // no. Opening it here would mean `INFO` opened files.
1192            None => self.store.is_some(),
1193        }
1194    }
1195
1196    /// Take a fresh memory reading, which the maintenance turn does once a batch.
1197    ///
1198    /// Nothing at all when there is no limit, which is the default and is every
1199    /// server that has not asked for one.
1200    pub fn refresh_memory(&mut self) {
1201        if self.maxmemory() != 0 {
1202            self.used = self.settled_memory();
1203        }
1204    }
1205
1206    /// [`Server::memory_bytes`], asked the cheap way.
1207    ///
1208    /// The same number. The difference is that this asks each database only
1209    /// about the collections that could have moved since the last time, which is
1210    /// what a batch touched rather than what the server holds, so it can be
1211    /// asked once a batch and again on every command that is over the limit.
1212    fn settled_memory(&mut self) -> usize {
1213        self.keyspaces_mut()
1214            .map(Keyspace::settled_memory_bytes)
1215            .sum::<usize>()
1216            + self.conn_bytes
1217    }
1218
1219    /// Make room under the `maxmemory` limit, throwing keys away if that is what
1220    /// it takes. Answers whether there is anything left it could throw away.
1221    ///
1222    /// Redis runs the same thing from `processCommand` before every command and
1223    /// so does this: a client that writes has to be judged at the moment it
1224    /// writes, not a batch later, or the limit is a suggestion.
1225    ///
1226    /// Three things happen in the loop and all three are needed. Eviction picks
1227    /// a key and drops it. Compaction gives the pages back, because dropping a
1228    /// key marks its record dead and returns nothing on its own, so a loop that
1229    /// only evicted would throw the whole keyspace away and watch the number
1230    /// stay where it was. The reading is taken again each time round, because
1231    /// the two of them together are the only thing that moves it.
1232    ///
1233    /// # Why running out of budget is not a no
1234    ///
1235    /// `false` means there was nothing left to evict, which is `noeviction`, or
1236    /// a `volatile` policy on a database where nothing has a deadline, or a
1237    /// keyspace that is already empty. It does not mean the server is still over
1238    /// its limit, and that difference is Redis's: `performEvictions` answers
1239    /// `EVICT_FAIL` only when it has run out of things to delete, and
1240    /// `processCommand` refuses the client on that and on nothing else. Running
1241    /// out of time part way through a job it is doing well comes back as
1242    /// `EVICT_RUNNING` and the command goes through, because a server that is
1243    /// evicting steadily and refusing every write while it does it is worse for
1244    /// the client than a little overshoot.
1245    ///
1246    /// # What the limit is worth
1247    ///
1248    /// Space comes back a segment at a time and a segment is two megabytes, so
1249    /// this holds a server to its limit give or take a segment. A `maxmemory` of
1250    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
1251    /// megabytes is asking for a precision this store does not have.
1252    pub fn make_room(&mut self) -> bool {
1253        let limit = self.maxmemory();
1254        if limit == 0 || self.used as u64 <= limit {
1255            return true;
1256        }
1257        // The cached reading is a batch old and the batch may have compacted
1258        // since, so take a fresh one before throwing anything away. It is the
1259        // settled reading and not the walk, so what this costs is the handful of
1260        // collections the last batch touched and not the whole database.
1261        self.used = self.settled_memory();
1262        let mut budget = EVICT_BUDGET;
1263        while self.used as u64 > limit {
1264            let over = self.used - limit as usize;
1265            if !self.relieve_step(over) {
1266                return false;
1267            }
1268            self.compact_hard_step();
1269            self.used = self.settled_memory();
1270            budget -= 1;
1271            if budget == 0 {
1272                break;
1273            }
1274        }
1275        true
1276    }
1277
1278    /// Give back `over` bytes from whichever database can, by moving values to
1279    /// the file where there is one and by throwing keys away where there is not.
1280    ///
1281    /// The two answers are the eviction inversion and which one a database gets
1282    /// is [`Server::migrates`]. Answers whether anything was given back at all,
1283    /// and `false` is what refuses the client's write.
1284    ///
1285    /// A store that will not take the bytes counts as nothing given back, so the
1286    /// write is refused rather than turned into a deletion. A disk that is
1287    /// misbehaving is a reason to stop accepting writes and it is not a reason
1288    /// to start losing data that was accepted already.
1289    ///
1290    /// Round robin from a cursor rather than always starting at database zero,
1291    /// so a server using more than one of them does not empty the first before
1292    /// touching the second. Almost every server is on database zero only, where
1293    /// this is one call that answers and fifteen that say the map is empty.
1294    fn relieve_step(&mut self, over: usize) -> bool {
1295        for turn in 0..self.slots() {
1296            let i = (self.evict_db + turn) % self.slots();
1297            // An empty keyspace has nothing to move and opening a log for one
1298            // would cost a resident page window to find that out.
1299            let used = !self.slot(i).is_empty();
1300            let gave = if used && self.migrates(i) {
1301                self.attach_store(i);
1302                // Whether it made room and not whether it moved a key. A round
1303                // that demoted nothing and handed back a segment is a round
1304                // that made room, and reading only the count refuses the write
1305                // that provoked it.
1306                self.slot_mut(i)
1307                    .relieve(over)
1308                    .is_ok_and(yo_kv::tier::Relief::made_room)
1309            } else {
1310                self.slot_mut(i).evict_one()
1311            };
1312            if gave {
1313                self.evict_db = (i + 1) % self.slots();
1314                self.dirty |= 1u64 << self.slot_db(i);
1315                return true;
1316            }
1317        }
1318        false
1319    }
1320
1321    /// The sweep the shard loop calls, at most once a millisecond.
1322    ///
1323    /// The gate is the whole difference between this and [`Server::expire_step`].
1324    /// A maintenance slice runs on every turn of the loop and a turn is a
1325    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
1326    /// thousand times per millisecond and spend a real share of the shard on
1327    /// looking for keys that cannot have died since the last look. Nothing in a
1328    /// database changes fast enough to be worth asking about more often than the
1329    /// clock can tell the difference, and the clock here is milliseconds.
1330    ///
1331    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
1332    /// hertz, so this is not the thing that decides how promptly memory comes
1333    /// back. What it decides is that an idle server sweeps a thousand times a
1334    /// second rather than a million.
1335    pub fn expire_slice(&mut self, budget: usize) -> usize {
1336        let now = self.clock.now_ms();
1337        if now == self.expire_ms {
1338            return 0;
1339        }
1340        self.expire_ms = now;
1341        self.expire_step(budget)
1342    }
1343
1344    /// Sweep dead keys out of the databases, spending at most `budget` looks.
1345    ///
1346    /// Answers what it spent, so the caller can charge its maintenance slice for
1347    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
1348    ///
1349    /// Round robin from its own cursor, and every database gets offered whatever
1350    /// is left of the budget rather than a sixteenth of it each, so a server on
1351    /// database zero only, which is nearly every server, spends the whole slice
1352    /// where the keys are. The fifteen empty ones cost a comparison apiece
1353    /// because a database with no key carrying a deadline says so without
1354    /// drawing anything.
1355    ///
1356    /// The cursor moves to the database after whichever one did the work, so two
1357    /// busy databases take turns instead of the lower numbered one starving the
1358    /// other.
1359    pub fn expire_step(&mut self, budget: usize) -> usize {
1360        let mut spent = 0;
1361        for turn in 0..self.slots() {
1362            if spent >= budget {
1363                break;
1364            }
1365            let i = (self.expire_db + turn) % self.slots();
1366            let c = self.slot_mut(i).expire_cycle(budget - spent);
1367            spent += c.examined;
1368            if c.expired > 0 {
1369                self.expire_db = (i + 1) % self.slots();
1370                self.dirty |= 1u64 << self.slot_db(i);
1371            }
1372        }
1373        spent
1374    }
1375
1376    /// One slice of compaction for a server that is over its limit.
1377    ///
1378    /// Takes the databases in the same order [`Server::compact_step`] does and
1379    /// stops at the first one that had something to move, and it asks with the
1380    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
1381    fn compact_hard_step(&mut self) -> Option<usize> {
1382        for turn in 0..self.slots() {
1383            let i = (self.next_db + turn) % self.slots();
1384            if let Some(moved) = self.slot_mut(i).compact_hard() {
1385                self.next_db = (i + 1) % self.slots();
1386                return Some(moved);
1387            }
1388        }
1389        None
1390    }
1391
1392    /// Take what every thread has marked and add it to the turn's own mask.
1393    ///
1394    /// The mask the turn works from is its own and not a shared one, because a
1395    /// mask it read in place and then cleared a bit of would be a mask that lost
1396    /// whatever another thread marked in between. A swap cannot lose a mark: a
1397    /// thread that ors while the swap happens either gets its bit in before the
1398    /// swap or leaves it there afterwards, and the second one costs one look at
1399    /// a database the turn has already been through.
1400    fn collect_marks(&mut self) {
1401        let mut marked = 0;
1402        for thread in &self.locals {
1403            marked |= thread.dirty.swap(0, Relaxed);
1404        }
1405        self.dirty |= marked;
1406    }
1407
1408    /// Give one database's dead space back, if any database has enough of it to
1409    /// be worth the move. `None` when no database had a candidate.
1410    ///
1411    /// Once per batch, next to the clock. Overwriting a key writes a new record
1412    /// and counts the old one dead, so without this a server holds everything
1413    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
1414    /// a key against Redis at 144 for the same load, and the whole difference
1415    /// was dead records nothing ever came back for.
1416    ///
1417    /// At most one segment moves per call and the search starts one database
1418    /// further along each time, so the cost of asking is a comparison per
1419    /// database and the cost of acting is bounded by a segment.
1420    pub fn compact_step(&mut self) -> Option<usize> {
1421        self.collect_marks();
1422        for turn in 0..self.slots() {
1423            let i = (self.next_db + turn) % self.slots();
1424            // Nothing has run against this database since it last said it had
1425            // nothing to collect, so it still has nothing to collect and the
1426            // line it lives on stays where it is.
1427            let at = self.slot_db(i);
1428            if self.dirty & (1 << at) == 0 {
1429                continue;
1430            }
1431            if let Some(moved) = self.slot_mut(i).compact_step() {
1432                self.next_db = (i + 1) % self.slots();
1433                return Some(moved);
1434            }
1435            // Only once every stripe of the database has said it has nothing,
1436            // since the bit is per database and one stripe answering for all of
1437            // them would stop the others being asked at all.
1438            if i % self.width == self.width - 1 {
1439                self.dirty &= !(1u64 << at);
1440            }
1441        }
1442        None
1443    }
1444}
1445
1446impl Default for Server {
1447    fn default() -> Server {
1448        Server::new()
1449    }
1450}
1451
1452/// What one connection has chosen.
1453pub struct Session {
1454    db: usize,
1455    id: u64,
1456    name: Vec<u8>,
1457    /// The `HIMPORT` fieldsets this connection has prepared.
1458    ///
1459    /// Connection state and not keyspace state, which is the reference's design
1460    /// and not a shortcut: a fieldset is invisible to every other connection and
1461    /// the keys built from one outlive it.
1462    sets: himport::Fieldsets,
1463}
1464
1465impl Session {
1466    /// A new connection, on database zero with no name.
1467    #[must_use]
1468    pub fn new(id: u64) -> Session {
1469        Session {
1470            db: 0,
1471            id,
1472            name: Vec::new(),
1473            sets: himport::Fieldsets::default(),
1474        }
1475    }
1476
1477    /// The connection id, which `HELLO` reports and `CLIENT` will.
1478    #[must_use]
1479    pub const fn id(&self) -> u64 {
1480        self.id
1481    }
1482
1483    /// Which database this connection is working in.
1484    #[must_use]
1485    pub const fn db(&self) -> usize {
1486        self.db
1487    }
1488
1489    /// The name the client gave itself, empty if it gave none.
1490    #[must_use]
1491    pub fn name(&self) -> &[u8] {
1492        &self.name
1493    }
1494
1495    /// Put everything back the way it was when the connection was opened.
1496    ///
1497    /// The protocol is not here because it is not here: it lives in the reply
1498    /// buffer, and `RESET` sets it back there.
1499    pub fn reset(&mut self) {
1500        self.db = 0;
1501        self.name.clear();
1502        // `SELECT` leaves these alone and `RESET` does not, both checked
1503        // against 8.10.1, which is the one pair of answers you could not guess
1504        // from what the command is for.
1505        self.sets.clear();
1506    }
1507
1508    /// Record the name from `HELLO ... SETNAME`.
1509    fn set_name(&mut self, name: &[u8]) {
1510        yo_alloc::allow(|| {
1511            self.name.clear();
1512            self.name.extend_from_slice(name);
1513        });
1514    }
1515}
1516
1517/// Run one command and write its reply.
1518///
1519/// The name is looked up and the arity is checked here, once, so that no body
1520/// has to. Everything after that is the command's own.
1521pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1522    // The decoder never produces a command with no name. If one ever arrives,
1523    // it is not something to answer.
1524    if args.is_empty() {
1525        return Flow::Continue;
1526    }
1527    resolved(server, session, lookup(args.name()), args, out)
1528}
1529
1530/// The same, for a caller that has already found the command.
1531///
1532/// The engine frames a command before it runs it, and between those two it also
1533/// asks which key the command touches so the record can be prefetched. That is
1534/// two more chances to look the name up, and looking it up three times to run it
1535/// once is three times the cost of the cheapest thing in the path. So the engine
1536/// resolves the name where it frames the command, carries the answer on the
1537/// framed command, and both the other two take it from there.
1538///
1539/// `spec` is `None` for a name that is not a command, which is the same thing
1540/// [`lookup`] says and lands in the same reply.
1541pub fn resolved(
1542    server: &mut Server,
1543    session: &mut Session,
1544    spec: Option<&'static Spec>,
1545    args: Args<'_>,
1546    out: &mut Out,
1547) -> Flow {
1548    if args.is_empty() {
1549        return Flow::Continue;
1550    }
1551    server.mine().stats.commands.bump();
1552
1553    let Some(spec) = spec else {
1554        write_error(out, &args::unknown_command(args));
1555        return Flow::Continue;
1556    };
1557    if !arity_ok(spec, args.len()) {
1558        server.mine().cmdstats.at(spec).rejected.bump();
1559        write_error(out, &args::wrong_arity(spec.name));
1560        return Flow::Continue;
1561    }
1562
1563    // The limit first, so a server with no `maxmemory`, which is the default and
1564    // is nearly all of them, pays one comparison against a field that is already
1565    // warm. Every command and not only the writes, because that is where Redis
1566    // puts it: making room is the server's job whatever the client asked for,
1567    // and the flag only decides who gets told no when there is no room to make.
1568    //
1569    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1570    // Redis's list, so a command that only frees is let through with nothing
1571    // left, which is what lets a client dig itself out with `DEL`.
1572    if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1573        server.mine().cmdstats.at(spec).rejected.bump();
1574        out.error_line(b"OOM ", OOM);
1575        return Flow::Continue;
1576    }
1577
1578    // Which databases the maintenance turn after this batch has to ask. Marked
1579    // for every command and not only for the writes, because a read can make
1580    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1581    // record it dropped is exactly the kind of thing the collector is for.
1582    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1583    // two groups that hold them mark all of them rather than the session's.
1584    server.mine().mark(match spec.group {
1585        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1586        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1587            1u64 << session.db
1588        }
1589        _ => ALL_DATABASES,
1590    });
1591
1592    let mark = out.len();
1593    // Before the group, because the five that block are list commands and would
1594    // otherwise land in `lists`, which is handed one database and nothing that
1595    // could park a client. The flag is the right thing to branch on rather than
1596    // a list of names: it is what `COMMAND INFO` reports about exactly these
1597    // commands, and the sorted set and stream ones that arrive later carry it
1598    // too.
1599    let done = if spec.flags.contains(&"blocking") {
1600        blocking::execute(server, session, spec, args, out)
1601    } else {
1602        match spec.group {
1603            "string" => {
1604                let db = session.db;
1605                strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1606            }
1607            // Its own group and its own file, and the same values underneath:
1608            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1609            // something a `SET` left behind works.
1610            "bitmap" => {
1611                let db = session.db;
1612                bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1613            }
1614            // The same again: a sketch is a string with a documented layout, so
1615            // `GET` hands one to a client and `SET` takes it back.
1616            "hyperloglog" => {
1617                let db = session.db;
1618                hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1619            }
1620            "set" => {
1621                let db = session.db;
1622                sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1623            }
1624            // The one hash command whose state is not in the keyspace. A
1625            // fieldset belongs to the connection, so this is handed the session
1626            // as well as the database, the same exception `MIGRATE` gets in the
1627            // keyspace group for the socket it keeps.
1628            "hash" if spec.name == "himport" => {
1629                let db = session.db;
1630                himport::execute(&server.dbs[db], &mut session.sets, args, out)
1631                    .map(|()| Flow::Continue)
1632            }
1633            // The one group that reaches back into the server after it has
1634            // written its reply, because a hash is what a search index is
1635            // made of. What comes back is what the indexes have to be told,
1636            // which is not the same as whether the command was a write.
1637            "hash" => {
1638                let db = session.db;
1639                let changed = hashes::execute(&server.dbs[db], spec, args, out);
1640                changed.map(|changed| {
1641                    indexing::changed(server, db, args.get(1), changed);
1642                    Flow::Continue
1643                })
1644            }
1645            "list" => {
1646                let db = session.db;
1647                lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1648            }
1649            "zset" => {
1650                let db = session.db;
1651                zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1652            }
1653            // A geo key is a sorted set and these are sorted set commands with
1654            // arithmetic on the way in and on the way out, so a client can ZREM
1655            // a place out of one and ZCARD it to count them.
1656            "geo" => {
1657                let db = session.db;
1658                geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1659            }
1660            "array" => {
1661                let db = session.db;
1662                arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1663            }
1664            "graph" => {
1665                let db = session.db;
1666                graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1667            }
1668            // A document under a key, reached by a path. The group is Redis's
1669            // module surface and the storage is ours, the same trade the vector
1670            // set group makes.
1671            "json" => {
1672                let db = session.db;
1673                json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1674            }
1675            "vector" => {
1676                let db = session.db;
1677                vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1678            }
1679            "bloom" => {
1680                let db = session.db;
1681                bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1682            }
1683            "cuckoo" => {
1684                let db = session.db;
1685                cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1686            }
1687            "cms" => {
1688                let db = session.db;
1689                cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1690            }
1691            "topk" => {
1692                let db = session.db;
1693                topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1694            }
1695            "tdigest" => {
1696                let db = session.db;
1697                tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1698            }
1699            "ts" => {
1700                let db = session.db;
1701                ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1702            }
1703            // The clock is read before the database is borrowed, because every
1704            // stream command needs the time and it lives on the server. An
1705            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1706            // `XINFO` reporting it all have to agree about what moment this is.
1707            "stream" => {
1708                let db = session.db;
1709                let now = server.now_ms();
1710                streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1711            }
1712            // The one keyspace command that needs more than the databases,
1713            // because the socket it talks down is held on the server between
1714            // commands and not opened again for each one.
1715            "keyspace" if spec.name == "migrate" => {
1716                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1717            }
1718            // Every database and not the one the session is on, because `COPY` takes
1719            // a `DB n` and writes into a database nobody selected.
1720            "keyspace" => {
1721                keyspace::execute(&server.dbs, session.db, spec, args, out).map(|()| Flow::Continue)
1722            }
1723            // No database at all, because an index is not a key. The registry
1724            // is the whole of what these sixteen commands touch, and then
1725            // `FT.CREATE` hands back the name it made so the keys that
1726            // already match its prefix can be read into it. The lock goes
1727            // before the scan runs, since the scan takes it again for every
1728            // key it reads.
1729            "search" => {
1730                let db = session.db;
1731                let made = search::execute(&mut server.search.lock(), db, spec, args, out);
1732                made.map(|made| {
1733                    if let Some(name) = made {
1734                        indexing::scan(server, db, name);
1735                    }
1736                    Flow::Continue
1737                })
1738            }
1739            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1740            _ => server::execute(server, session, spec, args, out),
1741        }
1742    };
1743    let flow = match done {
1744        Ok(flow) => flow,
1745        Err(e) => {
1746            out.truncate(mark);
1747            write_error(out, &e);
1748            Flow::Continue
1749        }
1750    };
1751
1752    // Counted here and not before the call, which is where Redis counts it, so
1753    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1754    // same way theirs does.
1755    //
1756    // Failure is read off the reply rather than off the `Result`, because the
1757    // two are not the same set. A command that ran out of arguments comes back
1758    // as an `Err` and a command that was sent the wrong password writes its own
1759    // error line and comes back `Ok`, and both of those are a call that failed.
1760    // The first byte at the mark is what a client would branch on, and it is `-`
1761    // for an error on either protocol and `!` for RESP3's long form.
1762    let row = server.mine().cmdstats.at(spec);
1763    row.calls.bump();
1764    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1765        row.failed.bump();
1766    }
1767    flow
1768}
1769
1770/// The error line for an error value.
1771///
1772/// The prefix is what a client branches on, and there are three of them:
1773/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1774/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1775/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1776/// than routed through here. `OOM` is not a [`Code`] of its own because
1777/// [`Code::Full`] already covers the string that is too long for
1778/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1779fn write_error(out: &mut Out, e: &Error) {
1780    let prefix: &[u8] = match e.code() {
1781        Code::WrongType => b"WRONGTYPE ",
1782        // Only the HyperLogLog commands answer this one, and the prefix is the
1783        // sentence a client branches on to tell a sketch it cannot read from a
1784        // sketch it sent wrong.
1785        Code::Corrupt => b"INVALIDOBJ ",
1786        _ => b"ERR ",
1787    };
1788    out.error_line(prefix, e.message().as_bytes());
1789}
1790
1791#[cfg(test)]
1792mod tests {
1793    use super::*;
1794    use crate::proto::{Limits, Proto};
1795    use crate::request::Argv;
1796
1797    /// Build the wire bytes for a command.
1798    ///
1799    /// Tests go through the codec rather than around it, so an argument in a
1800    /// test is the same borrowed slice a connection produces.
1801    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1802        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1803        for p in parts {
1804            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1805            wire.extend_from_slice(p);
1806            wire.extend_from_slice(b"\r\n");
1807        }
1808        wire
1809    }
1810
1811    /// A server, a connection and a buffer, driven the way the reactor will.
1812    struct Fixture {
1813        server: Server,
1814        session: Session,
1815        argv: Argv,
1816        out: Out,
1817    }
1818
1819    impl Fixture {
1820        fn new() -> Fixture {
1821            Fixture::on(Server::new())
1822        }
1823
1824        /// The same, on a server whose databases are cut into `width` stripes.
1825        fn striped(width: usize) -> Fixture {
1826            Fixture::on(Server::with_width(width))
1827        }
1828
1829        fn on(server: Server) -> Fixture {
1830            Fixture {
1831                server,
1832                session: Session::new(7),
1833                argv: Argv::new(),
1834                out: Out::new(Proto::Resp2),
1835            }
1836        }
1837
1838        /// Run one command and answer with the bytes it wrote.
1839        fn run(&mut self, parts: &[&[u8]]) -> String {
1840            self.flow(parts).1
1841        }
1842
1843        /// Run one command and answer with the bytes exactly as written.
1844        ///
1845        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
1846        /// every reply that is text and destroys a `DUMP` payload, since a
1847        /// payload is arbitrary bytes and a checksum on the end of them.
1848        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1849            let wire = encode(parts);
1850            self.argv.decode(&wire, &Limits::default()).unwrap();
1851            self.out.clear();
1852            execute(
1853                &mut self.server,
1854                &mut self.session,
1855                Args::new(&self.argv, &wire),
1856                &mut self.out,
1857            );
1858            self.out.as_slice().to_vec()
1859        }
1860
1861        /// Move every clock in the server on by `ms`.
1862        fn advance(&mut self, ms: u64) {
1863            self.server.advance_clock_ms(ms);
1864        }
1865
1866        /// The same, with what the connection should do next.
1867        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1868            let wire = encode(parts);
1869            self.argv.decode(&wire, &Limits::default()).unwrap();
1870            self.out.clear();
1871            let flow = execute(
1872                &mut self.server,
1873                &mut self.session,
1874                Args::new(&self.argv, &wire),
1875                &mut self.out,
1876            );
1877            (
1878                flow,
1879                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1880            )
1881        }
1882    }
1883
1884    /// What a client does all day: write the same keys again and again. Every
1885    /// one of those writes leaves the previous record behind, so a server that
1886    /// never compacts holds every version of every key it has ever been sent.
1887    #[test]
1888    fn rewriting_the_same_keys_does_not_grow_the_server() {
1889        let mut f = Fixture::new();
1890        let val = vec![b'v'; 1024];
1891        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1892
1893        for k in &keys {
1894            f.run(&[b"SET", k, &val]);
1895        }
1896        f.server.compact_step();
1897        let after_first = f.server.memory_bytes();
1898
1899        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
1900        // of it. Thirty two megabytes written to hold sixty four kilobytes,
1901        // which is the shape of a real workload and is enough churn to fill
1902        // sixteen segments if nothing ever comes back.
1903        for _ in 0..500 {
1904            for k in &keys {
1905                f.run(&[b"SET", k, &val]);
1906            }
1907            f.server.compact_step();
1908        }
1909
1910        assert!(
1911            f.server.memory_bytes() <= after_first * 2,
1912            "held {} after five hundred passes against {after_first} after one",
1913            f.server.memory_bytes()
1914        );
1915        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1916        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1917    }
1918
1919    /// The same churn on a database nobody starts on, either side of a quiet
1920    /// spell long enough for the maintenance turn to stop asking about it.
1921    ///
1922    /// The turn after each batch skips a database that has already said it has
1923    /// nothing to collect and has not been touched since, which is what keeps a
1924    /// server whose clients are all on database zero from loading and storing
1925    /// in the other fifteen every batch to be told no. Two things could go
1926    /// wrong with that. A database might never be marked at all, so this uses
1927    /// database nine, which nothing marks by accident. And a database whose
1928    /// mark was cleared might never get it back, so this drains the collector
1929    /// until it says there is nothing left, checks the mark really is gone, and
1930    /// then writes another thirty two megabytes through the same sixty four
1931    /// keys. If either went wrong the server would hold all of it.
1932    #[test]
1933    fn a_database_nobody_started_on_is_still_collected() {
1934        let mut f = Fixture::new();
1935        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1936        let val = vec![b'v'; 1024];
1937        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1938
1939        for k in &keys {
1940            f.run(&[b"SET", k, &val]);
1941        }
1942        while f.server.compact_step().is_some() {}
1943        assert_eq!(
1944            f.server.dirty & (1 << 9),
1945            0,
1946            "database nine was drained and should not be asked again until it is written to"
1947        );
1948        let after_first = f.server.memory_bytes();
1949
1950        for _ in 0..500 {
1951            for k in &keys {
1952                f.run(&[b"SET", k, &val]);
1953            }
1954            f.server.compact_step();
1955        }
1956
1957        assert!(
1958            f.server.memory_bytes() <= after_first * 2,
1959            "held {} after five hundred passes against {after_first} after one",
1960            f.server.memory_bytes()
1961        );
1962        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1963        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1964        // And nothing landed anywhere else on the way.
1965        f.run(&[b"SELECT", b"0"]);
1966        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1967    }
1968
1969    #[test]
1970    fn a_command_goes_from_bytes_to_bytes() {
1971        let mut f = Fixture::new();
1972        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1973        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1974        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1975        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1976        // The name is matched whatever case it came in, and so are the options.
1977        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1978        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1979    }
1980
1981    #[test]
1982    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1983        let mut f = Fixture::new();
1984        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1985        // A key named twice exists twice and can only be deleted once, and both
1986        // of those are Redis's answers rather than tidier ones.
1987        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1988        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1989        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1990        // UNLINK is the same body and reports the same way.
1991        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1992        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1993    }
1994
1995    #[test]
1996    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1997        let mut f = Fixture::new();
1998        f.run(&[b"SET", b"k", b"v"]);
1999        // A simple string on both protocols, which is unusual: most replies
2000        // that carry a word are bulk strings.
2001        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
2002        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
2003    }
2004
2005    #[test]
2006    fn touch_counts_the_way_exists_counts() {
2007        let mut f = Fixture::new();
2008        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2009        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
2010        assert_eq!(
2011            f.run(&[b"TOUCH", b"a", b"a"]),
2012            ":2\r\n",
2013            "twice counts twice"
2014        );
2015        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
2016        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
2017    }
2018
2019    #[test]
2020    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
2021        let mut f = Fixture::new();
2022        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2023        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
2024
2025        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
2026        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2027        assert_eq!(
2028            f.run(&[b"TTL", b"b"]),
2029            ":100\r\n",
2030            "the source's and not b's"
2031        );
2032        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2033    }
2034
2035    #[test]
2036    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
2037        let mut f = Fixture::new();
2038        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
2039        // The source is checked before the destination, so this is the error
2040        // and not the zero RENAMENX would otherwise answer for a taken name.
2041        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
2042    }
2043
2044    #[test]
2045    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
2046        let mut f = Fixture::new();
2047        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
2048
2049        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
2050        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2051        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
2052        // one call the two disagree about and neither does any work for.
2053        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
2054        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
2055        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
2056        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
2057    }
2058
2059    #[test]
2060    fn renaming_a_set_does_not_touch_a_member() {
2061        let mut f = Fixture::new();
2062        for i in 0..300 {
2063            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
2064        }
2065        let before = f.server.memory_bytes();
2066
2067        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
2068        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
2069        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
2070        assert!(
2071            f.server.memory_bytes().abs_diff(before) < 256,
2072            "the members were copied: {} against {before}",
2073            f.server.memory_bytes()
2074        );
2075    }
2076
2077    #[test]
2078    fn a_copy_is_a_second_value_and_not_a_second_name() {
2079        let mut f = Fixture::new();
2080        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
2081
2082        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
2083        f.run(&[b"SADD", b"t", b"m3"]);
2084        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
2085        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
2086    }
2087
2088    /// Every type a key can hold, copied, because two of them used to panic.
2089    ///
2090    /// `COPY` reads the value out of the source through one match on the type
2091    /// tag, and that match had a catch all at the bottom from back when a set
2092    /// and a hash were the only bodies. The list and the sorted set landed after
2093    /// it and nobody came back, so `COPY mylist other` took the shard down. It
2094    /// is an ordinary command against a type the server supports everywhere
2095    /// else, so this walks all five rather than the two that were broken: the
2096    /// point is that the next type cannot land the same way.
2097    #[test]
2098    fn every_type_can_be_copied() {
2099        let mut f = Fixture::new();
2100        f.run(&[b"SET", b"str", b"v1"]);
2101        f.run(&[b"SADD", b"set", b"m1"]);
2102        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2103        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
2104        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
2105
2106        for name in [
2107            &b"str"[..],
2108            &b"set"[..],
2109            &b"hash"[..],
2110            &b"list"[..],
2111            &b"zset"[..],
2112        ] {
2113            let dst = [name, b":copy"].concat();
2114            assert_eq!(
2115                f.run(&[b"COPY", name, &dst]),
2116                ":1\r\n",
2117                "copying {}",
2118                String::from_utf8_lossy(name)
2119            );
2120            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
2121        }
2122
2123        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
2124            let mut want = String::from("*2\r\n");
2125            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
2126            want
2127        });
2128        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
2129
2130        // And the copy is its own value, not a second name for the source.
2131        f.run(&[b"RPUSH", b"list:copy", b"c"]);
2132        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
2133        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
2134    }
2135
2136    #[test]
2137    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
2138        let mut f = Fixture::new();
2139        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2140        f.run(&[b"SET", b"b", b"v2"]);
2141
2142        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
2143        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2144        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
2145        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2146        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
2147        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
2148    }
2149
2150    #[test]
2151    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
2152        let mut f = Fixture::new();
2153        f.run(&[b"SET", b"a", b"v1"]);
2154
2155        // Same key, different database, so this is not the same object and is
2156        // an ordinary copy. Same key in the same database is the error below.
2157        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
2158        f.run(&[b"SELECT", b"1"]);
2159        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
2160        assert_eq!(
2161            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
2162            ":0\r\n",
2163            "taken"
2164        );
2165        assert_eq!(
2166            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
2167            ":1\r\n"
2168        );
2169    }
2170
2171    #[test]
2172    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
2173        let mut f = Fixture::new();
2174        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2175        assert_eq!(
2176            f.run(&[b"SORT", b"l"]),
2177            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2178        );
2179        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
2180        assert_eq!(
2181            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
2182            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2183        );
2184        assert_eq!(
2185            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
2186            "*1\r\n$1\r\n2\r\n"
2187        );
2188    }
2189
2190    #[test]
2191    fn sort_reads_a_key_per_element_for_by_and_for_get() {
2192        let mut f = Fixture::new();
2193        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
2194        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
2195        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
2196        // misses, which is a nil in the middle of the array and not a short one.
2197        assert_eq!(
2198            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
2199            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
2200        );
2201    }
2202
2203    #[test]
2204    fn sort_store_writes_a_list_and_answers_its_length() {
2205        let mut f = Fixture::new();
2206        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2207        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
2208        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
2209        assert_eq!(
2210            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
2211            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2212        );
2213        // An empty result takes the destination with it rather than leaving a
2214        // list that holds nothing.
2215        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
2216        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
2217    }
2218
2219    #[test]
2220    fn sort_ro_does_not_know_the_word_store() {
2221        let mut f = Fixture::new();
2222        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
2223        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
2224        assert_eq!(
2225            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
2226            "-ERR syntax error\r\n"
2227        );
2228        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2229    }
2230
2231    #[test]
2232    fn sort_refuses_what_it_cannot_sort() {
2233        let mut f = Fixture::new();
2234        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
2235        f.run(&[b"SET", b"s", b"x"]);
2236        assert_eq!(
2237            f.run(&[b"SORT", b"s"]),
2238            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
2239        );
2240        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
2241        assert_eq!(
2242            f.run(&[b"SORT", b"words"]),
2243            "-ERR One or more scores can't be converted into double\r\n"
2244        );
2245        assert_eq!(
2246            f.run(&[b"SORT", b"words", b"ALPHA"]),
2247            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
2248        );
2249        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
2250    }
2251
2252    #[test]
2253    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
2254        let mut f = Fixture::new();
2255        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
2256        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
2257        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2258        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2259        assert_eq!(
2260            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
2261            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2262        );
2263        // And back, which proves the body survived the trip rather than being
2264        // rebuilt from a copy that happened to look the same.
2265        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
2266        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2267    }
2268
2269    #[test]
2270    fn move_answers_zero_when_either_end_says_no() {
2271        let mut f = Fixture::new();
2272        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
2273        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
2274        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2275        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
2276        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2277        // The destination is taken, so nothing moves and the source is still
2278        // there with what it had.
2279        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
2280        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
2281        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2282        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
2283    }
2284
2285    #[test]
2286    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
2287        let mut f = Fixture::new();
2288        assert_eq!(
2289            f.run(&[b"MOVE", b"a", b"0"]),
2290            "-ERR source and destination objects are the same\r\n"
2291        );
2292        assert_eq!(
2293            f.run(&[b"MOVE", b"a", b"99"]),
2294            "-ERR DB index is out of range\r\n"
2295        );
2296        assert_eq!(
2297            f.run(&[b"MOVE", b"a", b"-1"]),
2298            "-ERR DB index is out of range\r\n"
2299        );
2300        assert_eq!(
2301            f.run(&[b"MOVE", b"a", b"x"]),
2302            "-ERR value is not an integer or out of range\r\n"
2303        );
2304    }
2305
2306    #[test]
2307    fn swapdb_swaps_what_two_connections_would_see() {
2308        let mut f = Fixture::new();
2309        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
2310        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2311        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
2312        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2313
2314        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2315        // Still on database zero, and database zero is a different database.
2316        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
2317        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2318        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2319        // A database swapped with itself is fine and changes nothing.
2320        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
2321        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2322    }
2323
2324    #[test]
2325    fn swapdb_says_which_index_it_could_not_read() {
2326        let mut f = Fixture::new();
2327        assert_eq!(
2328            f.run(&[b"SWAPDB", b"x", b"1"]),
2329            "-ERR invalid first DB index\r\n"
2330        );
2331        assert_eq!(
2332            f.run(&[b"SWAPDB", b"0", b"y"]),
2333            "-ERR invalid second DB index\r\n"
2334        );
2335        // A number too big to be an index on a server that keeps one in an int
2336        // is the same complaint, and a plausible one that is not ours is the
2337        // range complaint instead. The split is Redis's.
2338        assert_eq!(
2339            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2340            "-ERR invalid first DB index\r\n"
2341        );
2342        assert_eq!(
2343            f.run(&[b"SWAPDB", b"0", b"99"]),
2344            "-ERR DB index is out of range\r\n"
2345        );
2346        assert_eq!(
2347            f.run(&[b"SWAPDB", b"-1", b"0"]),
2348            "-ERR DB index is out of range\r\n"
2349        );
2350    }
2351
2352    #[test]
2353    fn wait_answers_zero_replicas_without_waiting() {
2354        let mut f = Fixture::new();
2355        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2356        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2357        // A replica that is never going to arrive, and a timeout that would be
2358        // a real wait on a server that had one.
2359        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2360        // Negative replicas is not an error, because zero is already more than
2361        // it asked for.
2362        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2363        assert_eq!(
2364            f.run(&[b"WAIT", b"x", b"0"]),
2365            "-ERR value is not an integer or out of range\r\n"
2366        );
2367        assert_eq!(
2368            f.run(&[b"WAIT", b"0", b"-1"]),
2369            "-ERR timeout is negative\r\n"
2370        );
2371        assert_eq!(
2372            f.run(&[b"WAIT", b"0", b"1.5"]),
2373            "-ERR timeout is not an integer or out of range\r\n"
2374        );
2375    }
2376
2377    #[test]
2378    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2379        let mut f = Fixture::new();
2380        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2381        assert_eq!(
2382            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2383            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2384        );
2385        assert_eq!(
2386            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2387            "-ERR value is out of range, value must between 0 and 1\r\n"
2388        );
2389        assert_eq!(
2390            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2391            "-ERR value is out of range, must be positive\r\n"
2392        );
2393        // The arguments are all read before the server looks at itself, so a
2394        // bad timeout beats the append only complaint even with numlocal set.
2395        assert_eq!(
2396            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2397            "-ERR timeout is negative\r\n"
2398        );
2399    }
2400
2401    /// The bytes inside a bulk reply, with the header and the trailing break
2402    /// taken off. Every `DUMP` test needs this and none of them care how the
2403    /// length was written.
2404    fn payload(reply: &[u8]) -> Vec<u8> {
2405        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2406        reply[head + 2..reply.len() - 2].to_vec()
2407    }
2408
2409    #[test]
2410    fn a_value_survives_a_dump_and_a_restore() {
2411        let mut f = Fixture::new();
2412        f.run(&[b"SET", b"s", b"hello"]);
2413        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2414        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2415        f.run(&[b"SADD", b"u", b"x", b"y"]);
2416        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2417        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2418
2419        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2420            let mut copy = key.to_vec();
2421            copy.push(b'2');
2422            let bytes = payload(&f.raw(&[b"DUMP", key]));
2423            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
2424            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
2425        }
2426
2427        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2428        assert_eq!(
2429            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2430            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2431        );
2432        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2433        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2434        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2435        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2436        // The encoding survives too, since the payload names the plainest legal
2437        // type and the loader puts the value back on the rung it belongs on.
2438        assert_eq!(
2439            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2440            f.run(&[b"OBJECT", b"ENCODING", b"t"])
2441        );
2442    }
2443
2444    #[test]
2445    fn a_dumped_hash_keeps_its_field_deadlines() {
2446        let mut f = Fixture::new();
2447        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2448        assert_eq!(
2449            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2450            "*1\r\n:1\r\n"
2451        );
2452        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2453        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2454        assert_eq!(
2455            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2456            "*2\r\n:-1\r\n:100\r\n"
2457        );
2458    }
2459
2460    #[test]
2461    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2462        let mut f = Fixture::new();
2463        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2464        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2465        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2466        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2467        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2468        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2469        // An absolute deadline that has already gone is not an error. The key is
2470        // not created and the reply is the same OK a live one gets.
2471        assert_eq!(
2472            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2473            "+OK\r\n"
2474        );
2475        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2476    }
2477
2478    #[test]
2479    fn dump_answers_nothing_for_a_key_that_is_not_there() {
2480        let mut f = Fixture::new();
2481        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2482        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2483        f.advance(50);
2484        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2485    }
2486
2487    #[test]
2488    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2489        let mut f = Fixture::new();
2490        f.run(&[b"SET", b"a", b"first"]);
2491        f.run(&[b"SET", b"b", b"second"]);
2492        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2493        assert_eq!(
2494            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2495            "-BUSYKEY Target key name already exists.\r\n"
2496        );
2497        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2498        assert_eq!(
2499            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2500            "+OK\r\n"
2501        );
2502        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2503    }
2504
2505    /// The busy key comes before the payload, which is not the order the
2506    /// arguments read in. Whether a key is taken should not depend on whether
2507    /// the bytes behind it happened to be good.
2508    #[test]
2509    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2510        let mut f = Fixture::new();
2511        f.run(&[b"SET", b"a", b"v"]);
2512        assert_eq!(
2513            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2514            "-BUSYKEY Target key name already exists.\r\n"
2515        );
2516        // And the options come before even that, so a bad FREQ beats the busy
2517        // key the same way a bad DB beats a missing source in COPY.
2518        assert_eq!(
2519            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2520            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2521        );
2522    }
2523
2524    #[test]
2525    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2526        let mut f = Fixture::new();
2527        f.run(&[b"SET", b"a", b"hello"]);
2528        let good = payload(&f.raw(&[b"DUMP", b"a"]));
2529
2530        let mut flipped = good.clone();
2531        flipped[2] ^= 0x40;
2532        assert_eq!(
2533            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2534            "-ERR DUMP payload version or checksum are wrong\r\n"
2535        );
2536        assert_eq!(
2537            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2538            "-ERR DUMP payload version or checksum are wrong\r\n"
2539        );
2540        // A footer that is right over a body that is not. The type byte says
2541        // string and there is nothing behind it, so the checksum agrees and the
2542        // value does not exist.
2543        let mut truncated = good[..1].to_vec();
2544        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2545        let crc = yo_common::crc::crc64(0, &truncated);
2546        truncated.extend_from_slice(&crc.to_le_bytes());
2547        assert_eq!(
2548            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2549            "-ERR Bad data format\r\n"
2550        );
2551        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2552    }
2553
2554    #[test]
2555    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2556        let mut f = Fixture::new();
2557        f.run(&[b"SET", b"a", b"v"]);
2558        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2559        assert_eq!(
2560            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2561            "-ERR Invalid TTL value, must be >= 0\r\n"
2562        );
2563        assert_eq!(
2564            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2565            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2566        );
2567        assert_eq!(
2568            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2569            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2570        );
2571        // Both are accepted and both are then dropped, which is D-26.
2572        assert_eq!(
2573            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2574            "+OK\r\n"
2575        );
2576        assert_eq!(
2577            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2578            "+OK\r\n"
2579        );
2580    }
2581
2582    /// Neither word is refused for being the wrong one. Each is only accepted
2583    /// while the other is unset, so the second of the two falls through to the
2584    /// plain syntax error rather than getting a message of its own.
2585    #[test]
2586    fn restore_takes_idletime_or_freq_and_not_both() {
2587        let mut f = Fixture::new();
2588        f.run(&[b"SET", b"a", b"v"]);
2589        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2590        assert_eq!(
2591            f.run(&[
2592                b"RESTORE",
2593                b"b",
2594                b"0",
2595                &bytes,
2596                b"IDLETIME",
2597                b"1",
2598                b"FREQ",
2599                b"2"
2600            ]),
2601            "-ERR syntax error\r\n"
2602        );
2603        assert_eq!(
2604            f.run(&[
2605                b"RESTORE",
2606                b"b",
2607                b"0",
2608                &bytes,
2609                b"FREQ",
2610                b"2",
2611                b"IDLETIME",
2612                b"1"
2613            ]),
2614            "-ERR syntax error\r\n"
2615        );
2616        assert_eq!(
2617            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2618            "-ERR syntax error\r\n"
2619        );
2620        assert_eq!(
2621            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2622            "-ERR syntax error\r\n"
2623        );
2624    }
2625
2626    #[test]
2627    fn copy_checks_its_options_before_it_looks_for_anything() {
2628        let mut f = Fixture::new();
2629        // No key exists at all, and every one of these is still the option
2630        // complaint rather than a zero, which is the order a real server uses.
2631        assert_eq!(
2632            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2633            "-ERR DB index is out of range\r\n"
2634        );
2635        assert_eq!(
2636            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2637            "-ERR DB index is out of range\r\n"
2638        );
2639        assert_eq!(
2640            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2641            "-ERR value is not an integer or out of range\r\n"
2642        );
2643        assert_eq!(
2644            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2645            "-ERR syntax error\r\n"
2646        );
2647        assert_eq!(
2648            f.run(&[b"COPY", b"a", b"a"]),
2649            "-ERR source and destination objects are the same\r\n"
2650        );
2651        // Repeated, reordered and lowercased, and the last DB wins.
2652        assert_eq!(
2653            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2654            ":0\r\n"
2655        );
2656    }
2657
2658    #[test]
2659    fn time_is_two_bulk_strings_and_moves() {
2660        let mut f = Fixture::new();
2661        let first = f.run(&[b"TIME"]);
2662        assert!(first.starts_with("*2\r\n$"), "got {first}");
2663        let parts: Vec<&str> = first.split("\r\n").collect();
2664        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2665        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2666        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2667        assert!((0..1_000_000).contains(&micros), "got {micros}");
2668        // The coarse clock the keyspace uses is a cached millisecond that a
2669        // background tick refreshes, so a TIME built on it would answer the
2670        // same microsecond twice in a row here.
2671        assert_ne!(first, f.run(&[b"TIME"]));
2672    }
2673
2674    #[test]
2675    fn a_keyspace_scan_walks_every_key_once() {
2676        let mut f = Fixture::new();
2677        for i in 0..500 {
2678            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2679        }
2680
2681        let mut seen: Vec<String> = Vec::new();
2682        let mut cursor = "0".to_owned();
2683        let mut calls = 0;
2684        loop {
2685            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2686            seen.extend(keys);
2687            cursor = next;
2688            calls += 1;
2689            assert!(calls < 10_000, "the cursor is not advancing");
2690            if cursor == "0" {
2691                break;
2692            }
2693        }
2694
2695        seen.sort();
2696        seen.dedup();
2697        assert_eq!(seen.len(), 500, "every key once and only once");
2698        // And more than one call to get them, or the COUNT is being ignored and
2699        // the loop above proved nothing about resuming.
2700        assert!(calls > 1, "500 keys came back in one batch");
2701    }
2702
2703    #[test]
2704    fn a_scan_narrows_by_pattern_and_by_type() {
2705        let mut f = Fixture::new();
2706        f.run(&[b"SET", b"str", b"v"]);
2707        f.run(&[b"SADD", b"members", b"a"]);
2708        f.run(&[b"HSET", b"fields", b"f", b"v"]);
2709
2710        let all = |f: &mut Fixture, args: &[&[u8]]| {
2711            let mut out: Vec<String> = Vec::new();
2712            let mut cursor = "0".to_owned();
2713            loop {
2714                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2715                line.extend_from_slice(args);
2716                let (next, keys) = scan_reply(&f.run(&line));
2717                out.extend(keys);
2718                cursor = next;
2719                if cursor == "0" {
2720                    break;
2721                }
2722            }
2723            out.sort();
2724            out
2725        };
2726
2727        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2728        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2729        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2730        // Case insensitive, the same as Redis's own comparison.
2731        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2732        // A type nothing can hold is not an error, it just matches nothing.
2733        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2734        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2735        // Both filters at once, and they are an and rather than an or.
2736        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2737    }
2738
2739    #[test]
2740    fn a_scan_says_what_is_wrong_with_it() {
2741        let mut f = Fixture::new();
2742        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2743        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2744        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2745        assert_eq!(
2746            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2747            "-ERR syntax error\r\n"
2748        );
2749        assert_eq!(
2750            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2751            "-ERR value is not an integer or out of range\r\n"
2752        );
2753        assert_eq!(
2754            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2755            "-ERR syntax error\r\n"
2756        );
2757        // A cursor the client made up is a cursor. It resumes somewhere
2758        // arbitrary and answers whatever is there, which is what Redis does and
2759        // is the only behaviour that does not need the server to remember every
2760        // cursor it has handed out.
2761        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2762    }
2763
2764    #[test]
2765    fn keys_and_randomkey_look_at_the_whole_database() {
2766        let mut f = Fixture::new();
2767        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2768        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2769
2770        for name in ["one", "two", "three"] {
2771            f.run(&[b"SET", name.as_bytes(), b"v"]);
2772        }
2773        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2774        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2775        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2776
2777        for _ in 0..50 {
2778            let got = f.run(&[b"RANDOMKEY"]);
2779            assert!(
2780                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2781                "got {got}"
2782            );
2783        }
2784    }
2785
2786    #[test]
2787    fn a_walk_does_not_answer_keys_that_have_expired() {
2788        let mut f = Fixture::new();
2789        f.run(&[b"SET", b"alive", b"v"]);
2790        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2791        f.server.advance_clock_ms(2);
2792        assert_eq!(
2793            f.run(&[b"DBSIZE"]),
2794            ":2\r\n",
2795            "nothing has collected it yet"
2796        );
2797
2798        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2799        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2800        assert_eq!(keys, ["alive"]);
2801        for _ in 0..20 {
2802            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2803        }
2804        // The walk collected it on the way past, which is what makes DBSIZE
2805        // here answer what Redis answers once its own cycle has been round.
2806        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2807    }
2808
2809    #[test]
2810    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2811        let mut f = Fixture::new();
2812        f.run(&[b"SET", b"k", b"v"]);
2813        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2814        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2815
2816        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2817        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2818        let ms = int(&f.run(&[b"PTTL", b"k"]));
2819        assert!((99_000..=100_000).contains(&ms), "got {ms}");
2820
2821        // The absolute pair, derived from the same one number the store kept.
2822        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2823        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2824        assert_eq!(at, (at_ms + 500) / 1000);
2825        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2826
2827        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2828        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2829        assert_eq!(
2830            f.run(&[b"PERSIST", b"k"]),
2831            ":0\r\n",
2832            "nothing to take off the second time"
2833        );
2834        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2835        assert_eq!(
2836            f.run(&[b"GET", b"k"]),
2837            "$1\r\nv\r\n",
2838            "and the value went through all of that untouched"
2839        );
2840    }
2841
2842    #[test]
2843    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2844        let mut f = Fixture::new();
2845        f.run(&[b"SET", b"str", b"v"]);
2846        f.run(&[b"SADD", b"set", b"a", b"b"]);
2847        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2848
2849        for key in [b"str".as_slice(), b"set", b"hash"] {
2850            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2851            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2852        }
2853        // The body is not touched by any of that, which is the whole reason the
2854        // deadline lives in the record and the body lives somewhere else.
2855        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2856        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2857        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2858    }
2859
2860    #[test]
2861    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2862        let mut f = Fixture::new();
2863        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2864            f.run(&[b"SET", key, b"v"]);
2865        }
2866        // Four ways of naming a moment that has passed, and all four are a
2867        // delete answering 1 rather than an error. Zero is a moment, minus one
2868        // is a moment, and the hash field commands refuse the negative one.
2869        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2870        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2871        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2872        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2873        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2874        assert_eq!(
2875            f.run(&[b"EXPIRE", b"a", b"100"]),
2876            ":0\r\n",
2877            "and the key really went, so there is nothing to put a deadline on"
2878        );
2879    }
2880
2881    #[test]
2882    fn the_four_conditions_decide_whether_the_deadline_moves() {
2883        let mut f = Fixture::new();
2884        f.run(&[b"SET", b"k", b"v"]);
2885
2886        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2887        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2888        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2889        assert_eq!(
2890            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2891            ":1\r\n",
2892            "no deadline reads as infinitely far away, so LT passes where GT fails"
2893        );
2894
2895        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2896        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2897        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2898        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2899        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2900        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2901
2902        // The condition is answered before the past check, so this is a 0 and
2903        // the key survives. The other order would delete it.
2904        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2905        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2906        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2907        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2908    }
2909
2910    #[test]
2911    fn the_conditions_are_a_set_and_not_a_keyword() {
2912        let mut f = Fixture::new();
2913        f.run(&[b"SET", b"k", b"v"]);
2914
2915        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2916        assert_eq!(
2917            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2918            ":0\r\n",
2919            "the same keyword twice means it once, and NX now has a deadline to fail on"
2920        );
2921
2922        // XX with LT is the one pair that is not either of them on its own: LT
2923        // alone would accept a key with no deadline and this does not.
2924        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2925        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2926        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2927        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2928        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2929        f.run(&[b"PERSIST", b"k"]);
2930        assert_eq!(
2931            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2932            ":0\r\n",
2933            "where LT on its own would have taken it"
2934        );
2935        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2936    }
2937
2938    #[test]
2939    fn a_key_is_gone_once_its_moment_passes() {
2940        let mut f = Fixture::new();
2941        f.run(&[b"SET", b"k", b"v"]);
2942        f.run(&[b"EXPIRE", b"k", b"100"]);
2943
2944        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2945        f.server.set_clock_ms(at as u64 + 1);
2946        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2947        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2948        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2949        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2950    }
2951
2952    #[test]
2953    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2954        let mut f = Fixture::new();
2955        f.run(&[b"SET", b"k", b"v"]);
2956        for (bad, want) in [
2957            (
2958                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2959                "-ERR value is not an integer or out of range\r\n",
2960            ),
2961            (
2962                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2963                "-ERR Unsupported option MAYBE\r\n",
2964            ),
2965            (
2966                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2967                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2968            ),
2969            (
2970                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2971                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2972            ),
2973            (
2974                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2975                "-ERR GT and LT options at the same time are not compatible\r\n",
2976            ),
2977            // Seconds that overflow when multiplied into milliseconds. Every
2978            // message names the command it came from.
2979            (
2980                &[b"EXPIRE", b"k", b"9223372036854775807"],
2981                "-ERR invalid expire time in 'expire' command\r\n",
2982            ),
2983            (
2984                &[b"EXPIREAT", b"k", b"9223372036854775807"],
2985                "-ERR invalid expire time in 'expireat' command\r\n",
2986            ),
2987            (
2988                &[b"PEXPIRE", b"k", b"9223372036854775807"],
2989                "-ERR invalid expire time in 'pexpire' command\r\n",
2990            ),
2991        ] {
2992            assert_eq!(f.run(bad), want, "for {bad:?}");
2993        }
2994        assert_eq!(
2995            f.run(&[b"TTL", b"k"]),
2996            ":-1\r\n",
2997            "and none of those put a deadline on anything"
2998        );
2999
3000        // The one of the four that has no arithmetic to overflow. Redis takes
3001        // it and holds the number as given, and a record here holds forty six
3002        // bits, so it lands in the year 4199 instead. D-17.
3003        assert_eq!(
3004            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
3005            ":1\r\n"
3006        );
3007        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
3008    }
3009
3010    #[test]
3011    fn flushing_empties_this_database_or_every_one_of_them() {
3012        let mut f = Fixture::new();
3013        f.run(&[b"SELECT", b"0"]);
3014        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3015        f.run(&[b"SELECT", b"1"]);
3016        f.run(&[b"SET", b"c", b"3"]);
3017        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3018        // ASYNC and SYNC are both taken and neither changes anything, since the
3019        // keyspace is empty before the OK goes out either way.
3020        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
3021        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3022        // Only database one was emptied.
3023        f.run(&[b"SELECT", b"0"]);
3024        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
3025        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
3026        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3027        f.run(&[b"SELECT", b"1"]);
3028        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3029        // Anything else after the name is a syntax error, and so is a third
3030        // argument even when the second one is a word we take.
3031        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
3032        assert_eq!(
3033            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
3034            "-ERR syntax error\r\n"
3035        );
3036    }
3037
3038    #[test]
3039    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
3040        let mut f = Fixture::new();
3041        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
3042        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
3043        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
3044        // Nothing is cached, so nothing is there, one answer per hash asked
3045        // about.
3046        assert_eq!(
3047            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
3048            "*2\r\n:0\r\n:0\r\n"
3049        );
3050        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
3051        assert_eq!(
3052            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
3053            "*0\r\n"
3054        );
3055        assert_eq!(
3056            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
3057            "-ERR Library not found\r\n"
3058        );
3059
3060        // Redis's two messages here are its own, one per container, and one of
3061        // them reads like a typo.
3062        assert_eq!(
3063            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
3064            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
3065        );
3066        assert_eq!(
3067            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
3068            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
3069        );
3070        // A second argument after the mode is the generic one instead, because
3071        // the count is checked before the word is looked at.
3072        assert_eq!(
3073            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
3074            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
3075        );
3076        assert_eq!(
3077            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
3078            "-ERR Unknown argument bogus\r\n"
3079        );
3080        assert_eq!(
3081            f.run(&[b"SCRIPT", b"EXISTS"]),
3082            "-ERR wrong number of arguments for 'script|exists' command\r\n"
3083        );
3084
3085        // The ones that need an interpreter are not here, and say so rather
3086        // than answering OK to a load that loaded nothing.
3087        assert_eq!(
3088            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
3089            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
3090        );
3091        assert_eq!(
3092            f.run(&[b"FUNCTION", b"STATS"]),
3093            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
3094        );
3095    }
3096
3097    #[test]
3098    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
3099        let mut f = Fixture::new();
3100        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
3101        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
3102        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
3103        // Read back as a string it is still an integer, written out as digits
3104        // only because somebody asked for them.
3105        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
3106        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
3107        // A counter that is not a number is the error the store raises and this
3108        // layer only spells, which is the whole point of the split.
3109        f.run(&[b"SET", b"k", b"hello"]);
3110        assert_eq!(
3111            f.run(&[b"INCR", b"k"]),
3112            "-ERR value is not an integer or out of range\r\n"
3113        );
3114        assert_eq!(
3115            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
3116            "-ERR increment would produce NaN or Infinity\r\n"
3117        );
3118    }
3119
3120    /// Every one of these was read off a running 8.8. They are the answers a
3121    /// client library's own test suite checks, and the shapes are not
3122    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
3123    /// integer, `INCREX` is a pair.
3124    #[test]
3125    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
3126        let mut f = Fixture::new();
3127        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
3128        // The same digest a real 8.8 answers for the same five bytes, which is
3129        // what makes `IFDEQ` usable against a mixed deployment.
3130        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
3131        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
3132        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
3133        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
3134        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
3135        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
3136        assert_eq!(
3137            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
3138            "*2\r\n:1\r\n:0\r\n",
3139            "a refused increment reports the value it left alone and applied nothing"
3140        );
3141        assert_eq!(
3142            f.run(&[
3143                b"INCREX",
3144                b"n",
3145                b"BYINT",
3146                b"5",
3147                b"UBOUND",
3148                b"3",
3149                b"SATURATE"
3150            ]),
3151            "*2\r\n:3\r\n:2\r\n"
3152        );
3153        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
3154        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
3155    }
3156
3157    #[test]
3158    fn the_same_answers_come_out_in_resp3_spelling() {
3159        let mut f = Fixture::new();
3160        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
3161        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
3162        // A float counter is a double on RESP3 and the digits in a bulk string
3163        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
3164        assert_eq!(
3165            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
3166            "*2\r\n,1.5\r\n,1.5\r\n"
3167        );
3168        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
3169        // `RESET` puts the protocol back, which is the part that is easy to
3170        // miss and leaves a pooled connection speaking the wrong one.
3171        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
3172        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
3173    }
3174
3175    #[test]
3176    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
3177        let mut f = Fixture::new();
3178        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
3179        assert_eq!(flow, Flow::Continue);
3180        assert_eq!(
3181            reply,
3182            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
3183        );
3184        // A name with a line ending in it cannot write its own frame into the
3185        // stream, which is the reason the error writer maps them to spaces.
3186        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
3187        assert_eq!(reply.matches("\r\n").count(), 1);
3188    }
3189
3190    #[test]
3191    fn arity_is_checked_before_the_command_is() {
3192        let mut f = Fixture::new();
3193        assert_eq!(
3194            f.run(&[b"GET"]),
3195            "-ERR wrong number of arguments for 'get' command\r\n"
3196        );
3197        assert_eq!(
3198            f.run(&[b"MSET", b"k"]),
3199            "-ERR wrong number of arguments for 'mset' command\r\n"
3200        );
3201        // The table says `PING` takes one or more and a real server then
3202        // refuses three, which is the sort of thing that only shows up against
3203        // the real thing.
3204        assert_eq!(
3205            f.run(&[b"PING", b"a", b"b"]),
3206            "-ERR wrong number of arguments for 'ping' command\r\n"
3207        );
3208        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
3209        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
3210        // `DELEX` takes two or four and nothing between.
3211        assert_eq!(
3212            f.run(&[b"DELEX", b"k", b"IFEQ"]),
3213            "-ERR wrong number of arguments for 'delex' command\r\n"
3214        );
3215    }
3216
3217    /// The option rules, all of them measured against 8.8 rather than read off
3218    /// the documentation. The surprising one is that `SET` accepts the same
3219    /// keyword twice and `INCREX` does not.
3220    #[test]
3221    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
3222        let mut f = Fixture::new();
3223        let syntax = "-ERR syntax error\r\n";
3224        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
3225        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
3226        assert_eq!(
3227            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
3228            syntax
3229        );
3230        assert_eq!(
3231            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
3232            syntax
3233        );
3234        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
3235        // Twice is fine, and the last one wins.
3236        assert_eq!(
3237            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
3238            "+OK\r\n"
3239        );
3240        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
3241        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
3242        // `INCREX` refuses what `SET` allows.
3243        assert_eq!(
3244            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
3245            syntax
3246        );
3247        assert_eq!(
3248            f.run(&[b"INCREX", b"n", b"ENX"]),
3249            "-ERR ENX flag requires an expiration\r\n"
3250        );
3251        assert_eq!(
3252            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
3253            "-ERR UBOUND is not an integer or out of range\r\n"
3254        );
3255        assert_eq!(
3256            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
3257            "-ERR LBOUND can't be greater than UBOUND\r\n"
3258        );
3259        assert_eq!(
3260            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
3261            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
3262        );
3263    }
3264
3265    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
3266    /// key that is not there, which answers null without ever looking at the
3267    /// expiration it was given.
3268    #[test]
3269    fn the_expiry_rules_are_redis_own() {
3270        let mut f = Fixture::new();
3271        let bad = "-ERR invalid expire time in 'set' command\r\n";
3272        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
3273        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
3274        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
3275        assert_eq!(
3276            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
3277            bad
3278        );
3279        assert_eq!(
3280            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
3281            "-ERR value is not an integer or out of range\r\n"
3282        );
3283        assert_eq!(
3284            f.run(&[b"SETEX", b"k", b"0", b"v"]),
3285            "-ERR invalid expire time in 'setex' command\r\n"
3286        );
3287        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
3288        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
3289        assert_eq!(
3290            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
3291            "-ERR syntax error\r\n",
3292            "the option list is still checked before the key is looked up"
3293        );
3294        // A deadline in the past is accepted and the key goes with it.
3295        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3296        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
3297        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3298    }
3299
3300    #[test]
3301    fn mset_takes_its_pairs_from_the_read_buffer() {
3302        let mut f = Fixture::new();
3303        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
3304        assert_eq!(
3305            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
3306            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
3307        );
3308        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
3309        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
3310        assert_eq!(
3311            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
3312            "-ERR wrong number of key-value pairs\r\n"
3313        );
3314        assert_eq!(
3315            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
3316            "-ERR invalid numkeys value\r\n"
3317        );
3318        assert_eq!(
3319            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
3320            "-ERR invalid numkeys value\r\n"
3321        );
3322    }
3323
3324    #[test]
3325    fn lcs_answers_the_length_the_string_and_the_runs() {
3326        let mut f = Fixture::new();
3327        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
3328        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3329        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3330        assert_eq!(
3331            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3332            "*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"
3333        );
3334        // Without `IDX` the two options that only mean something with it are
3335        // accepted and ignored, which is what a real server does.
3336        assert_eq!(
3337            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3338            "$6\r\nmytext\r\n"
3339        );
3340    }
3341
3342    #[test]
3343    fn select_moves_the_connection_and_the_databases_stay_apart() {
3344        let mut f = Fixture::new();
3345        f.run(&[b"SET", b"k", b"zero"]);
3346        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3347        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3348        f.run(&[b"SET", b"k", b"four"]);
3349        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3350        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3351        assert_eq!(
3352            f.run(&[b"SELECT", b"99"]),
3353            "-ERR DB index is out of range\r\n"
3354        );
3355        assert_eq!(
3356            f.run(&[b"SELECT", b"-1"]),
3357            "-ERR DB index is out of range\r\n"
3358        );
3359        assert_eq!(
3360            f.run(&[b"SELECT", b"abc"]),
3361            "-ERR value is not an integer or out of range\r\n"
3362        );
3363        // `RESET` brings it back to zero.
3364        f.run(&[b"SELECT", b"4"]);
3365        f.run(&[b"RESET"]);
3366        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3367    }
3368
3369    #[test]
3370    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3371        let mut f = Fixture::new();
3372        let reply = f.run(&[b"HELLO"]);
3373        assert!(reply.starts_with("*14\r\n"), "{reply}");
3374        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3375        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3376        assert!(
3377            reply.contains(":7\r\n"),
3378            "the connection id is in there: {reply}"
3379        );
3380        assert_eq!(
3381            f.run(&[b"HELLO", b"4"]),
3382            "-NOPROTO unsupported protocol version\r\n"
3383        );
3384        assert_eq!(
3385            f.run(&[b"HELLO", b"abc"]),
3386            "-ERR Protocol version is not an integer or out of range\r\n"
3387        );
3388        assert_eq!(
3389            f.run(&[b"HELLO", b"3", b"SETNAME"]),
3390            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3391        );
3392        assert!(
3393            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3394                .starts_with("%7\r\n")
3395        );
3396        assert_eq!(f.session.name(), b"bob");
3397        f.run(&[b"RESET"]);
3398        assert_eq!(f.session.name(), b"");
3399    }
3400
3401    #[test]
3402    fn command_describes_this_server_in_the_shape_a_driver_reads() {
3403        let mut f = Fixture::new();
3404        let count = format!(":{}\r\n", COMMANDS.len());
3405        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3406        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3407        assert_eq!(
3408            info,
3409            "*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\
3410             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3411        );
3412        // A null in the list, and the plain one: `$-1` and not `*-1`.
3413        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3414        assert_eq!(
3415            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3416            "*1\r\n$8\r\ngetrange\r\n"
3417        );
3418        assert_eq!(
3419            f.run(&[b"COMMAND", b"NOPE"]),
3420            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3421        );
3422    }
3423
3424    /// A cluster aware client asks this question and then routes on the
3425    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
3426    /// that matters.
3427    #[test]
3428    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3429        let mut f = Fixture::new();
3430        assert_eq!(
3431            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3432            "*1\r\n$1\r\nk\r\n"
3433        );
3434        assert_eq!(
3435            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3436            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3437        );
3438        assert_eq!(
3439            f.run(&[
3440                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3441            ]),
3442            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3443        );
3444        assert_eq!(
3445            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3446            "-ERR The command has no key arguments\r\n"
3447        );
3448        assert_eq!(
3449            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3450            "-ERR Invalid number of arguments specified for command\r\n"
3451        );
3452    }
3453
3454    #[test]
3455    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3456        let mut f = Fixture::new();
3457        assert_eq!(
3458            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3459            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3460        );
3461        // A pattern matches more than one, and a setting two patterns both ask
3462        // for is still sent once.
3463        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3464        assert!(both.starts_with("*6\r\n"), "{both}");
3465        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3466        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3467        assert_eq!(
3468            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3469            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3470        );
3471        assert_eq!(
3472            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3473            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3474        );
3475        assert_eq!(
3476            f.run(&[b"CONFIG", b"GET"]),
3477            "-ERR wrong number of arguments for 'config|get' command\r\n"
3478        );
3479        // Too few arguments and an odd number of them are different
3480        // complaints, which is the sort of thing only the real server tells
3481        // you.
3482        assert_eq!(
3483            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3484            "-ERR wrong number of arguments for 'config|set' command\r\n"
3485        );
3486        assert_eq!(
3487            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3488            "-ERR syntax error\r\n"
3489        );
3490        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3491        assert_eq!(
3492            f.run(&[b"CONFIG", b"REWRITE"]),
3493            "-ERR The server is running without a config file\r\n"
3494        );
3495    }
3496
3497    #[test]
3498    fn the_eviction_policy_reads_back_what_was_written_to_it() {
3499        let mut f = Fixture::new();
3500        assert_eq!(
3501            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3502            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3503        );
3504        assert_eq!(
3505            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3506            "+OK\r\n",
3507            "the name is matched without regard to case, like every other one"
3508        );
3509        assert_eq!(
3510            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3511            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3512        );
3513        // And INFO agrees with CONFIG, which it did not when it was a literal.
3514        assert!(
3515            f.run(&[b"INFO", b"memory"])
3516                .contains("maxmemory_policy:allkeys-lfu"),
3517            "INFO and CONFIG disagree about the policy"
3518        );
3519        // The refusal names every legal value in the order the real server's
3520        // enum table lists them, because a client comparing the message compares
3521        // the whole string.
3522        assert_eq!(
3523            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3524            "-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"
3525        );
3526        // A bad pair leaves the good one in the same command alone, and the
3527        // policy is checked by the same pass that checks the numbers.
3528        assert_eq!(
3529            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3530            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3531        );
3532        f.run(&[
3533            b"CONFIG",
3534            b"SET",
3535            b"hash-max-listpack-entries",
3536            b"7",
3537            b"maxmemory-policy",
3538            b"nonsense",
3539        ]);
3540        assert_eq!(
3541            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3542            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3543        );
3544    }
3545
3546    #[test]
3547    fn the_three_eviction_numbers_read_back_too() {
3548        let mut f = Fixture::new();
3549        for (name, default, set) in [
3550            ("maxmemory-samples", "5", "12"),
3551            ("lfu-log-factor", "10", "3"),
3552            ("lfu-decay-time", "1", "60"),
3553        ] {
3554            let get = || {
3555                format!(
3556                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3557                    name.len(),
3558                    default.len()
3559                )
3560            };
3561            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3562            assert_eq!(
3563                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3564                "+OK\r\n"
3565            );
3566            assert_eq!(
3567                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3568                format!(
3569                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3570                    name.len(),
3571                    set.len()
3572                )
3573            );
3574            // A number that is not a number is refused with the same sentence
3575            // every other number gets, which names the setting the client typed.
3576            assert_eq!(
3577                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3578                format!(
3579                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3580                )
3581            );
3582        }
3583    }
3584
3585    #[test]
3586    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3587        let mut f = Fixture::new();
3588        assert_eq!(
3589            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3590            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3591            "no limit is the default"
3592        );
3593        // The pairing is Redis's and it is a trap: the bare letter is a power of
3594        // ten and the one with the b is a power of two.
3595        for (typed, bytes) in [
3596            (&b"1024"[..], "1024"),
3597            (b"1k", "1000"),
3598            (b"1kb", "1024"),
3599            (b"1M", "1000000"),
3600            (b"1Mb", "1048576"),
3601            (b"1gb", "1073741824"),
3602            (b"100mb", "104857600"),
3603        ] {
3604            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3605            assert_eq!(
3606                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3607                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3608                "set {}",
3609                String::from_utf8_lossy(typed)
3610            );
3611        }
3612        assert!(
3613            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3614            "the report agrees with the setting"
3615        );
3616
3617        // A unit nobody has heard of, and a negative number, which is not a very
3618        // large one however it is spelled.
3619        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3620            assert_eq!(
3621                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3622                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3623                "refused {}",
3624                String::from_utf8_lossy(bad)
3625            );
3626        }
3627        assert!(
3628            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3629            "and the refusal left the old one alone"
3630        );
3631    }
3632
3633    #[test]
3634    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3635        let mut f = Fixture::new();
3636        f.run(&[b"SET", b"here", b"already"]);
3637        // A byte, which is under what an empty server holds, so nothing this
3638        // command could do would get it under. The default policy is
3639        // `noeviction`, so nothing is what it does.
3640        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3641        assert_eq!(
3642            f.run(&[b"SET", b"k", b"v"]),
3643            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3644        );
3645        assert_eq!(
3646            f.run(&[b"LPUSH", b"l", b"v"]),
3647            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3648        );
3649        // Reading is allowed, and so is the one thing that would help.
3650        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3651        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3652        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3653
3654        // Taking the limit away lets the write through again.
3655        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3656        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3657    }
3658
3659    #[test]
3660    fn an_allkeys_policy_makes_room_instead_of_refusing() {
3661        let mut f = Fixture::new();
3662        let val = vec![b'v'; 256];
3663        for i in 0..24000u32 {
3664            let k = format!("key:{i:08}");
3665            f.run(&[b"SET", k.as_bytes(), &val]);
3666        }
3667        let full = f.server.memory_bytes();
3668        assert!(
3669            full > 3 * 1024 * 1024,
3670            "the arena is several segments: {full}"
3671        );
3672
3673        // Two megabytes under what it is holding, which is one segment's worth,
3674        // so getting there means giving a whole segment back and not just
3675        // dropping a few records.
3676        let limit = full - 2 * 1024 * 1024;
3677        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3678        f.run(&[
3679            b"CONFIG",
3680            b"SET",
3681            b"maxmemory",
3682            limit.to_string().as_bytes(),
3683        ]);
3684
3685        // Writes keep working the whole way down. The budget means one command
3686        // does not do it all, so this runs until the server has settled and
3687        // checks that nothing was refused on the way.
3688        for i in 0..2000u32 {
3689            let k = format!("new:{i:08}");
3690            assert_eq!(
3691                f.run(&[b"SET", k.as_bytes(), &val]),
3692                "+OK\r\n",
3693                "write {i} was refused"
3694            );
3695            f.server.refresh_memory();
3696            if f.server.memory_bytes() <= limit {
3697                break;
3698            }
3699        }
3700        assert!(
3701            f.server.memory_bytes() <= limit,
3702            "it never got under: {} against {limit}",
3703            f.server.memory_bytes()
3704        );
3705        let info = f.run(&[b"INFO", b"stats"]);
3706        assert!(!info.contains("evicted_keys:0"), "{info}");
3707        assert!(
3708            f.run(&[b"DBSIZE"]) != ":0\r\n",
3709            "and it did not empty the database to get there"
3710        );
3711    }
3712
3713    #[test]
3714    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3715        // The limit is judged against a number kept as the collections move,
3716        // rather than found by asking all of them, and the two have to be the
3717        // same number or the limit is enforced against a fiction. This does the
3718        // things that move it, which is growing a collection, shrinking one,
3719        // changing its representation, deleting it and reusing its slot, across
3720        // all five types, and checks the two against each other as it goes.
3721        let mut f = Fixture::new();
3722        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3723        let big = vec![b'v'; 200];
3724
3725        for i in 0..400u32 {
3726            let n = i.to_string();
3727            let n = n.as_bytes();
3728            f.run(&[b"SADD", b"s", n]);
3729            f.run(&[b"SADD", b"s2", &big]);
3730            f.run(&[b"HSET", b"h", n, &big]);
3731            f.run(&[b"RPUSH", b"l", &big]);
3732            f.run(&[b"ZADD", b"z", n, n]);
3733            f.run(&[b"ARSET", b"a", n, &big]);
3734            if i % 7 == 0 {
3735                f.run(&[b"SREM", b"s", n]);
3736                f.run(&[b"HDEL", b"h", n]);
3737                f.run(&[b"LPOP", b"l"]);
3738                f.run(&[b"ZREM", b"z", n]);
3739                f.run(&[b"ARDEL", b"a", n]);
3740            }
3741            if i % 53 == 0 {
3742                // Every type deleted and made again, so a slot goes on the free
3743                // list and comes back holding something else.
3744                f.run(&[b"DEL", b"s2"]);
3745            }
3746            assert_eq!(
3747                f.server.settled_memory(),
3748                f.server.memory_bytes(),
3749                "after round {i}"
3750            );
3751        }
3752
3753        // The run has to have built something, or the two numbers agreeing is
3754        // two zeroes agreeing.
3755        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3756        assert!(
3757            f.server.memory_bytes() > 512 * 1024,
3758            "{}",
3759            f.server.memory_bytes()
3760        );
3761
3762        // And it survives the collections going away entirely.
3763        f.run(&[b"FLUSHALL"]);
3764        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3765    }
3766
3767    #[test]
3768    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3769        // A server with no limit does not keep the running total, so setting a
3770        // limit on a database that is already full has to start it from a walk.
3771        // If it did not, the first reading would be zero and the server would
3772        // think it had all the room in the world.
3773        let mut f = Fixture::new();
3774        for i in 0..200u32 {
3775            let n = i.to_string();
3776            f.run(&[b"SADD", b"s", n.as_bytes()]);
3777            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3778        }
3779        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3780        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3781
3782        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3783        for i in 200..400u32 {
3784            let n = i.to_string();
3785            f.run(&[b"SADD", b"s", n.as_bytes()]);
3786        }
3787        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3788        assert_eq!(
3789            f.server.settled_memory(),
3790            f.server.memory_bytes(),
3791            "the writes it was not watching are in the number it started from"
3792        );
3793    }
3794
3795    #[test]
3796    fn evicted_keys_and_expired_keys_are_different_numbers() {
3797        let mut f = Fixture::new();
3798        // Nothing has been evicted and nothing can be under the default policy,
3799        // so this stays at zero while the other one moves.
3800        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3801        f.server.advance_clock_ms(20);
3802        f.run(&[b"GET", b"gone"]);
3803        let info = f.run(&[b"INFO", b"stats"]);
3804        assert!(info.contains("expired_keys:1"), "{info}");
3805        assert!(info.contains("evicted_keys:0"), "{info}");
3806    }
3807
3808    #[test]
3809    fn the_object_subcommands_follow_the_policy() {
3810        let mut f = Fixture::new();
3811        f.run(&[b"SET", b"s", b"v"]);
3812        // Under the default the clock is kept and the counter is not, and under
3813        // an LFU policy it is the other way round. Each subcommand refuses on
3814        // the side where its reading of the three bytes means nothing.
3815        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3816        assert!(
3817            f.run(&[b"OBJECT", b"FREQ", b"s"])
3818                .starts_with("-ERR An LFU maxmemory policy is not selected"),
3819        );
3820
3821        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3822        assert!(
3823            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3824                .starts_with("-ERR An LFU maxmemory policy is selected"),
3825        );
3826        // The key was written under a clock policy, so what comes back is that
3827        // clock read as a counter. It is a number and not an error, which is the
3828        // point: switching at runtime does not invalidate anything, it only makes
3829        // the old field mean something else until the key is used again.
3830        assert!(
3831            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3832            "FREQ should answer under an LFU policy"
3833        );
3834    }
3835
3836    #[test]
3837    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3838        let mut f = Fixture::new();
3839        f.run(&[b"SET", b"s", b"hello"]);
3840        f.run(&[b"SET", b"n", b"123"]);
3841        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3842        f.run(&[b"SADD", b"ss", b"a", b"b"]);
3843        f.run(&[b"HSET", b"h", b"f", b"v"]);
3844        for (key, want) in [
3845            (b"s".as_slice(), "embstr"),
3846            (b"n", "int"),
3847            (b"si", "intset"),
3848            (b"ss", "listpack"),
3849            (b"h", "listpack"),
3850        ] {
3851            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3852            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3853        }
3854
3855        // A field deadline widens the blob rather than promoting it, and this
3856        // is the only place a client can see that happen.
3857        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3858        assert_eq!(
3859            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3860            "$10\r\nlistpackex\r\n"
3861        );
3862
3863        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3864        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3865        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3866    }
3867
3868    #[test]
3869    fn object_answers_nil_for_a_key_that_is_not_there() {
3870        let mut f = Fixture::new();
3871        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3872            assert_eq!(
3873                f.run(&[b"OBJECT", sub, b"nokey"]),
3874                "$-1\r\n",
3875                "a nil and not an error, which is what 8.10.1 does"
3876            );
3877        }
3878        // And the key is looked up before FREQ has its complaint, so the
3879        // complaint only reaches a key that exists.
3880        f.run(&[b"SET", b"s", b"v"]);
3881        assert!(
3882            f.run(&[b"OBJECT", b"FREQ", b"s"])
3883                .starts_with("-ERR An LFU maxmemory policy is not"),
3884        );
3885        assert_eq!(
3886            f.run(&[b"OBJECT", b"NOPE", b"s"]),
3887            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3888        );
3889        assert_eq!(
3890            f.run(&[b"OBJECT", b"ENCODING"]),
3891            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3892        );
3893        assert_eq!(
3894            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3895            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3896        );
3897        assert_eq!(
3898            f.run(&[b"OBJECT"]),
3899            "-ERR wrong number of arguments for 'object' command\r\n"
3900        );
3901    }
3902
3903    #[test]
3904    fn config_moves_the_ladder_and_object_encoding_agrees() {
3905        let mut f = Fixture::new();
3906        assert_eq!(
3907            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3908            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3909            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3910        );
3911        // The old spelling is the same number under a different name, and a
3912        // glob that catches both sends both.
3913        assert_eq!(
3914            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3915            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3916        );
3917        assert!(
3918            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3919                .starts_with("*8\r\n")
3920        );
3921        assert!(
3922            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3923                .starts_with("*6\r\n")
3924        );
3925
3926        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3927        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3928
3929        assert_eq!(
3930            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3931            "+OK\r\n",
3932            "written under the old name and read back under the new one"
3933        );
3934        assert_eq!(
3935            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3936            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3937        );
3938        assert_eq!(
3939            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3940            "$8\r\nlistpack\r\n",
3941            "the hash that already exists is left exactly where it was"
3942        );
3943        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3944        assert_eq!(
3945            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3946            "$9\r\nhashtable\r\n",
3947            "and the next one built goes straight to a table"
3948        );
3949
3950        // The set has three of these and all three move.
3951        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3952        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3953        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3954        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3955        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3956        assert_eq!(
3957            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3958            "$9\r\nhashtable\r\n"
3959        );
3960    }
3961
3962    #[test]
3963    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3964        let mut f = Fixture::new();
3965        assert_eq!(
3966            f.run(&[
3967                b"CONFIG",
3968                b"SET",
3969                b"hash-max-listpack-entries",
3970                b"7",
3971                b"set-max-listpack-entries",
3972                b"abc"
3973            ]),
3974            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3975        );
3976        assert_eq!(
3977            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3978            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3979            "the pair in front of the bad one did not go in"
3980        );
3981        // The name in the complaint is the one that was typed, so the old
3982        // spelling comes back as the old spelling.
3983        assert_eq!(
3984            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3985            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3986        );
3987        assert_eq!(
3988            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3989            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3990        );
3991        // A number past what an i64 holds is the parse complaint and not the
3992        // range one, which is upstream reading it before it checks it.
3993        assert_eq!(
3994            f.run(&[
3995                b"CONFIG",
3996                b"SET",
3997                b"set-max-intset-entries",
3998                b"99999999999999999999"
3999            ]),
4000            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
4001        );
4002        assert_eq!(
4003            f.run(&[
4004                b"CONFIG",
4005                b"SET",
4006                b"set-max-intset-entries",
4007                b"9223372036854775807"
4008            ]),
4009            "+OK\r\n"
4010        );
4011    }
4012
4013    #[test]
4014    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
4015        let mut f = Fixture::new();
4016        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
4017        f.run(&[b"SELECT", b"3"]);
4018        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4019        assert_eq!(
4020            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4021            "$9\r\nhashtable\r\n",
4022            "these are one server wide number in Redis, whatever a Keyspace carries"
4023        );
4024    }
4025
4026    #[test]
4027    fn info_reports_the_numbers_it_can_stand_behind() {
4028        let mut f = Fixture::new();
4029        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
4030        let all = f.run(&[b"INFO"]);
4031        assert!(all.contains("redis_version:8.8.0"), "{all}");
4032        assert!(
4033            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
4034            "{all}"
4035        );
4036        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
4037        assert!(all.contains("role:master"), "{all}");
4038        // One section is one section.
4039        let clients = f.run(&[b"INFO", b"clients"]);
4040        assert!(clients.contains("connected_clients:0"), "{clients}");
4041        assert!(!clients.contains("redis_version"), "{clients}");
4042        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
4043    }
4044
4045    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
4046    ///
4047    /// This is Redis's `unit/info-command` written against the fixture. Every
4048    /// assertion in it is one of theirs, in their order, and the two fields it
4049    /// turns on are the two that suite was failing on: `master_repl_offset`,
4050    /// which is in the default set, and `rejected_calls`, which is not.
4051    #[test]
4052    fn commandstats_is_asked_for_and_replication_is_not() {
4053        let mut f = Fixture::new();
4054        for arg in ["", "all", "default", "everything"] {
4055            let info = if arg.is_empty() {
4056                f.run(&[b"INFO"])
4057            } else {
4058                f.run(&[b"INFO", arg.as_bytes()])
4059            };
4060            assert!(info.contains("redis_version"), "{arg}: {info}");
4061            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
4062            assert!(info.contains("used_memory"), "{arg}: {info}");
4063            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
4064            let asked = arg == "all" || arg == "everything";
4065            assert_eq!(
4066                info.contains("rejected_calls"),
4067                asked,
4068                "{arg} should{} carry the command counters: {info}",
4069                if asked { "" } else { " not" }
4070            );
4071        }
4072
4073        let cpu = f.run(&[b"INFO", b"cpu"]);
4074        assert!(cpu.contains("used_cpu_user"), "{cpu}");
4075        assert!(!cpu.contains("used_memory"), "{cpu}");
4076
4077        // Their case, to make the point that a section name is not case
4078        // sensitive any more than a command name is.
4079        let stats = f.run(&[b"INFO", b"commandSTATS"]);
4080        assert!(!stats.contains("used_memory"), "{stats}");
4081        assert!(stats.contains("rejected_calls"), "{stats}");
4082
4083        // Two sections named, and neither of them pulls in a third.
4084        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
4085        assert!(pair.contains("used_cpu_user"), "{pair}");
4086        assert!(!pair.contains("master_repl_offset"), "{pair}");
4087
4088        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
4089        assert!(with_all.contains("used_memory"), "{with_all}");
4090        assert!(with_all.contains("master_repl_offset"), "{with_all}");
4091        assert!(with_all.contains("rejected_calls"), "{with_all}");
4092        // A section named twice is still written once.
4093        assert_eq!(
4094            with_all.matches("used_cpu_user_children").count(),
4095            1,
4096            "{with_all}"
4097        );
4098
4099        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
4100        assert!(with_default.contains("used_memory"), "{with_default}");
4101        assert!(
4102            with_default.contains("master_repl_offset"),
4103            "{with_default}"
4104        );
4105        assert!(!with_default.contains("rejected_calls"), "{with_default}");
4106        assert_eq!(
4107            with_default.matches("used_cpu_user_children").count(),
4108            1,
4109            "{with_default}"
4110        );
4111    }
4112
4113    /// The memory section says what this process may use, not what the machine
4114    /// has.
4115    ///
4116    /// The distinction is the whole point of it. A server inside a container
4117    /// that reports the host's memory is a server whose operator sizes it for
4118    /// memory it will be killed for touching, so all three numbers are there:
4119    /// what the machine has, what the cgroup allows, and the quarter of the
4120    /// tighter one that pools are sized from.
4121    #[test]
4122    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
4123        let mut f = Fixture::new();
4124        let info = f.run(&[b"INFO", b"memory"]);
4125        for field in [
4126            "total_system_memory:",
4127            "mem_cgroup_limit:",
4128            "mem_limit:",
4129            "mem_budget:",
4130        ] {
4131            assert!(info.contains(field), "no {field} in {info}");
4132        }
4133
4134        let field = |name: &str| -> u64 {
4135            info.lines()
4136                .find_map(|l| l.strip_prefix(name))
4137                .unwrap_or_else(|| panic!("no {name} in {info}"))
4138                .trim()
4139                .parse()
4140                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
4141        };
4142        let limit = field("mem_limit:");
4143        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
4144        // Zero means there is no limit to report, which is a real answer on a
4145        // machine with no cgroups and no way to ask how big it is.
4146        if limit != 0 {
4147            let host = field("total_system_memory:");
4148            let cgroup = field("mem_cgroup_limit:");
4149            assert!(
4150                limit == host || limit == cgroup,
4151                "the limit came from neither number: {info}"
4152            );
4153        }
4154    }
4155
4156    /// The three counters, each on the path that raises it.
4157    ///
4158    /// `calls` on a command that worked, `failed_calls` on one that ran and
4159    /// answered with an error, and `rejected_calls` on one that never ran at
4160    /// all. The last two are the pair that is easy to collapse into one number
4161    /// and that Redis keeps apart, because a client sending the wrong number of
4162    /// arguments and a client asking for a list element that is not there are
4163    /// not the same problem.
4164    #[test]
4165    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
4166        let mut f = Fixture::new();
4167        f.run(&[b"SET", b"k", b"v"]);
4168        f.run(&[b"SET", b"k", b"w"]);
4169        // Ran, and answered with an error, because `k` is not a list.
4170        f.run(&[b"LPUSH", b"k", b"x"]);
4171        // Never ran: `LPUSH` takes at least three arguments.
4172        f.run(&[b"LPUSH", b"k"]);
4173
4174        let stats = f.run(&[b"INFO", b"commandstats"]);
4175        assert!(
4176            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
4177            "{stats}"
4178        );
4179        assert!(
4180            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
4181            "{stats}"
4182        );
4183        assert!(
4184            !stats.contains("cmdstat_zadd"),
4185            "a command nobody has sent has no row: {stats}"
4186        );
4187    }
4188
4189    /// A cache that writes with a deadline and never reads back used to hold
4190    /// every key it had ever written, because lazy expiry needs somebody to walk
4191    /// past a key before it can reclaim it and nobody ever did.
4192    #[test]
4193    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
4194        let mut f = Fixture::new();
4195        for i in 0..3_000u32 {
4196            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4197        }
4198        for i in 0..1_000u32 {
4199            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4200        }
4201        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
4202        f.advance(100);
4203        assert_eq!(
4204            f.run(&[b"DBSIZE"]),
4205            ":4000\r\n",
4206            "DBSIZE counts records and nothing has read past the dead ones yet"
4207        );
4208
4209        // What the shard loop does, one slice at a time.
4210        let mut spent = 0;
4211        for _ in 0..2_000 {
4212            spent += f.server.expire_step(4096);
4213            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
4214                break;
4215            }
4216        }
4217        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
4218        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
4219        for i in 0..1_000u32 {
4220            assert_eq!(
4221                f.run(&[b"GET", format!("k{i}").as_bytes()]),
4222                "$1\r\nv\r\n",
4223                "it took a key that had no deadline"
4224            );
4225        }
4226    }
4227
4228    #[test]
4229    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
4230        let mut f = Fixture::new();
4231        for i in 0..2_000u32 {
4232            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4233        }
4234        assert_eq!(f.server.expire_step(4096), 0);
4235        // And one database having them does not make the other fifteen pay.
4236        f.run(&[b"SELECT", b"3"]);
4237        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
4238        f.advance(100);
4239        for _ in 0..64 {
4240            f.server.expire_step(4096);
4241        }
4242        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4243        f.run(&[b"SELECT", b"0"]);
4244        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
4245        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
4246    }
4247
4248    /// The gate, which is what stops a maintenance slice that runs every hundred
4249    /// nanoseconds from drawing a sample every hundred nanoseconds.
4250    #[test]
4251    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
4252        let mut f = Fixture::new();
4253        for i in 0..500u32 {
4254            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4255        }
4256        f.advance(100);
4257        let at = f.server.striped(0).now_ms();
4258        f.server.set_clock_ms(at);
4259        // A small budget, so that one slice cannot finish the job and a second
4260        // one having nothing to do would mean the gate and not an empty
4261        // database.
4262        assert!(f.server.expire_slice(8) > 0, "the first one works");
4263        for _ in 0..1_000 {
4264            assert_eq!(
4265                f.server.expire_slice(8),
4266                0,
4267                "the millisecond has not moved and neither should this"
4268            );
4269        }
4270        assert!(
4271            f.server.striped(0).expires() > 400,
4272            "there is plenty left to take"
4273        );
4274        f.server.set_clock_ms(at + 1);
4275        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
4276    }
4277
4278    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
4279    /// how much of a cache is volatile was reading a constant.
4280    #[test]
4281    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
4282        let mut f = Fixture::new();
4283        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4284        assert!(
4285            f.run(&[b"INFO", b"keyspace"])
4286                .contains("db0:keys=3,expires=0"),
4287            "none of them has one yet"
4288        );
4289        f.run(&[b"EXPIRE", b"a", b"1000"]);
4290        f.run(&[b"EXPIRE", b"b", b"1000"]);
4291        let two = f.run(&[b"INFO", b"keyspace"]);
4292        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
4293        f.run(&[b"PERSIST", b"a"]);
4294        f.run(&[b"DEL", b"b"]);
4295        let none = f.run(&[b"INFO", b"keyspace"]);
4296        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
4297
4298        // Each database answers for itself, the way Redis reports it.
4299        f.run(&[b"SELECT", b"1"]);
4300        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
4301        let both = f.run(&[b"INFO", b"keyspace"]);
4302        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
4303        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
4304    }
4305
4306    #[cfg(unix)]
4307    #[test]
4308    fn info_cpu_reports_processor_time_that_was_really_measured() {
4309        let mut f = Fixture::new();
4310        let cpu = f.run(&[b"INFO", b"cpu"]);
4311        assert!(cpu.contains("# CPU"), "{cpu}");
4312        // Redis's unit/info-command asks for this one by name in three tests.
4313        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
4314        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
4315        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
4316        assert!(!cpu.contains("redis_version"), "{cpu}");
4317
4318        // It is a measurement and not a constant, so it goes up when work
4319        // happens. A tight loop rather than a sleep, because sleeping is the
4320        // one thing that does not move this number.
4321        let before = used_cpu_user(&cpu);
4322        let mut n = 0u64;
4323        let mut rounds = 0;
4324        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
4325            for i in 0..1_000_000u64 {
4326                n = n.wrapping_add(i.wrapping_mul(i));
4327            }
4328            rounds += 1;
4329            // A bound rather than a spin, so a platform where this number does
4330            // not move fails here instead of hanging. Even a clock with whole
4331            // millisecond granularity gets there in the first round or two.
4332            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4333        }
4334    }
4335
4336    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
4337    #[cfg(unix)]
4338    fn used_cpu_user(info: &str) -> f64 {
4339        info.lines()
4340            .find_map(|l| l.strip_prefix("used_cpu_user:"))
4341            .expect("no used_cpu_user in the reply")
4342            .trim()
4343            .parse()
4344            .expect("used_cpu_user is not a number")
4345    }
4346
4347    /// The safety net under the rule that a body checks its arguments before
4348    /// it writes anything. `MGET` writes its array header first and then reads
4349    /// each key, so if a later argument could fail the header would already be
4350    /// out. Nothing in the string group does that today and this is what would
4351    /// catch the first one that did.
4352    #[test]
4353    fn a_command_that_fails_leaves_nothing_half_written() {
4354        let mut f = Fixture::new();
4355        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4356        assert_eq!(reply, "-ERR offset is out of range\r\n");
4357        assert!(!reply.contains(':'), "no integer went out in front of it");
4358    }
4359
4360    #[test]
4361    fn quit_answers_first_and_closes_after() {
4362        let mut f = Fixture::new();
4363        let (flow, reply) = f.flow(&[b"QUIT"]);
4364        assert_eq!(reply, "+OK\r\n");
4365        assert_eq!(flow, Flow::Close);
4366    }
4367
4368    /// A server that has not been asked to stop is not stopping, and one that
4369    /// has says so without writing anything back.
4370    ///
4371    /// The empty reply is the point. Redis answers nothing at all here and the
4372    /// client sees the socket close, and an `OK` would be a promise from a
4373    /// process that is about to not exist.
4374    #[test]
4375    fn shutdown_writes_nothing_and_sets_the_flag() {
4376        let mut f = Fixture::new();
4377        assert!(!f.server.stopping(), "nobody has asked yet");
4378
4379        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4380        assert_eq!(reply, "");
4381        assert_eq!(flow, Flow::Close);
4382        assert!(f.server.stopping());
4383    }
4384
4385    /// Every flag combination 8.10.1 takes, and every one it refuses.
4386    ///
4387    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
4388    /// contradict each other, `ABORT` says to do nothing so it cannot be
4389    /// combined with a word about how to do it, and repeating any one of them
4390    /// is fine. All of it was read off a running 8.10.1 rather than worked out
4391    /// from the documentation, which does not say.
4392    #[test]
4393    fn shutdown_takes_the_flags_redis_takes() {
4394        for flags in [
4395            &[b"NOSAVE".as_slice()][..],
4396            &[b"SAVE"],
4397            &[b"NOW"],
4398            &[b"FORCE"],
4399            &[b"nosave"],
4400            &[b"NOW", b"NOW"],
4401            &[b"SAVE", b"SAVE"],
4402            &[b"NOSAVE", b"NOW", b"FORCE"],
4403        ] {
4404            let mut f = Fixture::new();
4405            let mut parts = vec![b"SHUTDOWN".as_slice()];
4406            parts.extend_from_slice(flags);
4407            let (flow, reply) = f.flow(&parts);
4408            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4409            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4410            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4411        }
4412
4413        for flags in [
4414            &[b"BOGUS".as_slice()][..],
4415            &[b"SAVE", b"NOSAVE"],
4416            &[b"NOSAVE", b"SAVE"],
4417            &[b"ABORT", b"NOW"],
4418            &[b"NOSAVE", b"ABORT"],
4419            &[b"NOW", b"FORCE", b"ABORT"],
4420        ] {
4421            let mut f = Fixture::new();
4422            let mut parts = vec![b"SHUTDOWN".as_slice()];
4423            parts.extend_from_slice(flags);
4424            assert_eq!(
4425                f.run(&parts),
4426                "-ERR syntax error\r\n",
4427                "SHUTDOWN {flags:?} was accepted"
4428            );
4429            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4430        }
4431    }
4432
4433    /// `ABORT` has nothing to call off, ever.
4434    ///
4435    /// A shutdown here is decided and done inside one turn of the loop, so
4436    /// there is no window in which one is in progress. That makes Redis's
4437    /// message for a cancel with nothing to cancel the right answer every time
4438    /// rather than only when nothing happens to be pending. Two `ABORT`s is
4439    /// still one `ABORT`, which is what 8.10.1 does.
4440    #[test]
4441    fn shutdown_abort_never_has_anything_to_abort() {
4442        let mut f = Fixture::new();
4443        for parts in [
4444            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4445            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4446        ] {
4447            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4448            assert!(!f.server.stopping(), "an abort stopped the server");
4449        }
4450    }
4451
4452    /// A fixture whose server writes into a directory of its own.
4453    ///
4454    /// Every test here really writes files, because the whole point of the
4455    /// command is the files and a backup that is only a state machine would
4456    /// pass a test suite and fail the first person who tried to restore one.
4457    /// The directory carries the test's name so that the suite can run its
4458    /// tests in parallel the way it always does.
4459    struct Backups {
4460        f: Fixture,
4461        dir: PathBuf,
4462    }
4463
4464    impl Backups {
4465        fn new(name: &str) -> Backups {
4466            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4467            let _ = std::fs::remove_dir_all(&dir);
4468            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4469            let mut f = Fixture::new();
4470            f.server.set_dir(dir.clone());
4471            Backups { f, dir }
4472        }
4473
4474        fn run(&mut self, parts: &[&[u8]]) -> String {
4475            self.f.run(parts)
4476        }
4477
4478        /// The names in `backupdir`, sorted, so a test can say what is on disk.
4479        fn files(&self) -> Vec<String> {
4480            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4481                Ok(entries) => entries
4482                    .filter_map(|e| e.ok())
4483                    .map(|e| e.file_name().to_string_lossy().into_owned())
4484                    .collect(),
4485                Err(_) => Vec::new(),
4486            };
4487            names.sort();
4488            names
4489        }
4490
4491        fn read(&self, name: &str) -> Vec<u8> {
4492            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4493        }
4494    }
4495
4496    impl Drop for Backups {
4497        fn drop(&mut self) {
4498            let _ = std::fs::remove_dir_all(&self.dir);
4499        }
4500    }
4501
4502    /// The four states and the moves between them, in the order a client walks
4503    /// them, with the files checked at every step.
4504    #[test]
4505    fn backup_walks_the_states_the_reference_walks() {
4506        let mut b = Backups::new("states");
4507        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4508
4509        assert!(status(&mut b).contains("idle"));
4510        assert!(b.files().is_empty(), "an idle server has written a backup");
4511
4512        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4513        assert!(status(&mut b).contains("incrementing"));
4514        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4515
4516        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4517        assert!(status(&mut b).contains("sealed"));
4518        assert_eq!(
4519            b.files(),
4520            [
4521                "appendonly.aof.1.base.rdb",
4522                "appendonly.aof.1.incr.aof",
4523                "appendonly.aof.manifest",
4524            ]
4525        );
4526
4527        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4528        assert!(status(&mut b).contains("idle"));
4529        assert!(b.files().is_empty(), "cleanup left something behind");
4530    }
4531
4532    /// Every move that is refused, in the reference's words.
4533    #[test]
4534    fn backup_refuses_the_moves_the_reference_refuses() {
4535        let mut b = Backups::new("refusals");
4536
4537        assert_eq!(
4538            b.run(&[b"BACKUP", b"SEAL"]),
4539            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4540        );
4541        assert_eq!(
4542            b.run(&[b"BACKUP", b"ABORT"]),
4543            "-ERR No backup in progress\r\n"
4544        );
4545        // Cleanup from idle is not an error, it is a way of saying there was
4546        // nothing to clean up.
4547        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4548
4549        b.run(&[b"BACKUP", b"START"]);
4550        assert_eq!(
4551            b.run(&[b"BACKUP", b"START"]),
4552            "-ERR A backup is already in progress, ABORT it first\r\n"
4553        );
4554        assert_eq!(
4555            b.run(&[b"BACKUP", b"CLEANUP"]),
4556            "-ERR Backup is in progress\r\n"
4557        );
4558
4559        b.run(&[b"BACKUP", b"SEAL"]);
4560        assert_eq!(
4561            b.run(&[b"BACKUP", b"START"]),
4562            "-ERR A sealed backup exists, CLEANUP it first\r\n"
4563        );
4564        assert_eq!(
4565            b.run(&[b"BACKUP", b"SEAL"]),
4566            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4567        );
4568        assert_eq!(
4569            b.run(&[b"BACKUP", b"ABORT"]),
4570            "-ERR No backup in progress\r\n"
4571        );
4572    }
4573
4574    /// An abort takes the base file away and leaves a state saying who did it.
4575    ///
4576    /// The next backup takes the next sequence number rather than reusing the
4577    /// one whose files were just thrown away, so a directory somebody copied a
4578    /// half finished backup out of cannot end up with two different files under
4579    /// one name.
4580    #[test]
4581    fn backup_abort_removes_the_file_and_says_who_did_it() {
4582        let mut b = Backups::new("abort");
4583        b.run(&[b"BACKUP", b"START"]);
4584        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4585
4586        let status = b.run(&[b"BACKUP", b"STATUS"]);
4587        assert!(status.contains("failed"), "{status}");
4588        assert!(status.contains("aborted by user"), "{status}");
4589        assert!(b.files().is_empty(), "abort left the base file behind");
4590        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4591
4592        // A start from failed works, and is the second backup.
4593        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4594        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4595        let status = b.run(&[b"BACKUP", b"STATUS"]);
4596        assert!(status.contains("incrementing"), "{status}");
4597        assert!(!status.contains("aborted"), "the old error was kept");
4598    }
4599
4600    /// `LIST` names nothing, then one file, then three, and they are absolute.
4601    #[test]
4602    fn backup_list_names_the_files_that_are_pinned_so_far() {
4603        let mut b = Backups::new("list");
4604        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4605
4606        b.run(&[b"BACKUP", b"START"]);
4607        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4608        let base = base.to_string_lossy().into_owned();
4609        assert_eq!(
4610            b.run(&[b"BACKUP", b"LIST"]),
4611            format!("*1\r\n${}\r\n{base}\r\n", base.len())
4612        );
4613
4614        b.run(&[b"BACKUP", b"SEAL"]);
4615        let listed = b.run(&[b"BACKUP", b"LIST"]);
4616        assert!(listed.starts_with("*3\r\n"), "{listed}");
4617        // The order is the manifest's order, base then incremental then the
4618        // manifest itself, which is the order a restore needs them in.
4619        let names: Vec<&str> = listed
4620            .lines()
4621            .filter(|l| l.starts_with('/') || l.contains(":\\"))
4622            .collect();
4623        assert_eq!(names.len(), 3, "{listed}");
4624        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4625        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4626        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4627    }
4628
4629    /// The base file is the dataset as it was at `START` and not at `SEAL`.
4630    ///
4631    /// That is D-46 and it is the one thing about this a client can notice, so
4632    /// it is pinned here rather than left to be discovered by whoever restores
4633    /// one. The incremental file is empty for the same reason: there is no
4634    /// append only log underneath this server to copy the writes in between out
4635    /// of.
4636    #[test]
4637    fn a_backup_holds_the_dataset_as_it_was_at_start() {
4638        let mut b = Backups::new("contents");
4639        b.run(&[b"SET", b"bk", b"v1"]);
4640        b.run(&[b"BACKUP", b"START"]);
4641        b.run(&[b"SET", b"bk", b"v2"]);
4642        b.run(&[b"BACKUP", b"SEAL"]);
4643
4644        let base = b.read("appendonly.aof.1.base.rdb");
4645        assert!(base.starts_with(b"REDIS"), "not an RDB file");
4646        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4647        assert!(
4648            !base.windows(2).any(|w| w == b"v2"),
4649            "the base file moved on after START"
4650        );
4651        // The aux field a loader acts on, and the one that says this file is
4652        // the base of an append only file rather than a standalone dump. Its
4653        // value is the one byte string 1, which the encoder writes as an
4654        // integer the way a real server writes it.
4655        let at = base
4656            .windows(8)
4657            .position(|w| w == b"aof-base")
4658            .expect("no aof-base aux field");
4659        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4660
4661        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4662        assert_eq!(
4663            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4664            "file appendonly.aof.1.base.rdb seq 1 type b\n\
4665             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4666        );
4667    }
4668
4669    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
4670    /// RESP2, which is what every other map shaped reply in this server does.
4671    #[test]
4672    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4673        let mut b = Backups::new("status");
4674        b.f.server.set_clock_ms(1_700_000_000_000);
4675
4676        assert_eq!(
4677            b.run(&[b"BACKUP", b"STATUS"]),
4678            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4679             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4680        );
4681
4682        b.f.out = Out::new(Proto::Resp3);
4683        b.run(&[b"BACKUP", b"START"]);
4684        assert_eq!(
4685            b.run(&[b"BACKUP", b"STATUS"]),
4686            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4687             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4688        );
4689
4690        b.run(&[b"BACKUP", b"SEAL"]);
4691        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4692        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4693    }
4694
4695    /// A sealed backup that nobody cleans up goes away on its own once
4696    /// `backup-sealed-ttl` seconds have passed since the seal.
4697    #[test]
4698    fn a_sealed_backup_is_swept_away_after_the_timeout() {
4699        let mut b = Backups::new("ttl");
4700        b.f.server.set_clock_ms(1_000_000);
4701        assert_eq!(
4702            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4703            "+OK\r\n"
4704        );
4705        b.run(&[b"BACKUP", b"START"]);
4706        b.run(&[b"BACKUP", b"SEAL"]);
4707
4708        // A minute short of the deadline, nothing happens.
4709        b.f.server.set_clock_ms(1_000_000 + 59_000);
4710        b.f.server.backup_expire();
4711        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4712        assert_eq!(b.files().len(), 3);
4713
4714        b.f.server.set_clock_ms(1_000_000 + 60_000);
4715        b.f.server.backup_expire();
4716        let status = b.run(&[b"BACKUP", b"STATUS"]);
4717        assert!(status.contains("idle"), "{status}");
4718        assert!(b.files().is_empty(), "the timeout left the files behind");
4719
4720        // Zero is the default and means a sealed backup is kept for ever.
4721        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4722        b.run(&[b"BACKUP", b"START"]);
4723        b.run(&[b"BACKUP", b"SEAL"]);
4724        b.f.server.set_clock_ms(9_000_000_000);
4725        b.f.server.backup_expire();
4726        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4727    }
4728
4729    /// The three settings around the command, read and written the way 8.10.1
4730    /// reads and writes them.
4731    #[test]
4732    fn the_backup_settings_behave_the_way_the_reference_does() {
4733        let mut b = Backups::new("config");
4734        let dir = b.dir.to_string_lossy().into_owned();
4735
4736        assert_eq!(
4737            b.run(&[b"CONFIG", b"GET", b"dir"]),
4738            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4739        );
4740        assert_eq!(
4741            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4742            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4743        );
4744        assert_eq!(
4745            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4746            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4747        );
4748
4749        // `dir` is a protected config, so it is refused even for the value it
4750        // already holds, and `backupdirname` is immutable.
4751        assert_eq!(
4752            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4753            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4754        );
4755        assert_eq!(
4756            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4757            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4758        );
4759        assert!(
4760            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4761                .contains("argument couldn't be parsed into an integer")
4762        );
4763        assert!(
4764            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4765                .contains("argument must be between 0 and 9223372036854775807 inclusive")
4766        );
4767    }
4768
4769    /// The help text, which has `HELP` in it twice because the reference's does.
4770    #[test]
4771    fn backup_help_is_the_text_the_reference_sends() {
4772        let mut f = Fixture::new();
4773        let help = f.run(&[b"BACKUP", b"HELP"]);
4774        assert!(help.starts_with("*17\r\n"), "{help}");
4775        assert!(
4776            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4777        );
4778        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
4779        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
4780        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
4781    }
4782
4783    /// What a mistyped `BACKUP` gets told.
4784    ///
4785    /// The arity error names `backup` where the reference names `backup|start`,
4786    /// which is D-46: the table reports one arity for the container the way the
4787    /// reference does, and the per subcommand table that would carry the better
4788    /// name is not built yet. Every subcommand is exactly two words, so nothing
4789    /// legal is refused by it.
4790    #[test]
4791    fn backup_refuses_what_it_cannot_read() {
4792        let mut f = Fixture::new();
4793        assert_eq!(
4794            f.run(&[b"BACKUP"]),
4795            "-ERR wrong number of arguments for 'backup' command\r\n"
4796        );
4797        assert_eq!(
4798            f.run(&[b"BACKUP", b"START", b"x"]),
4799            "-ERR wrong number of arguments for 'backup' command\r\n"
4800        );
4801        assert_eq!(
4802            f.run(&[b"BACKUP", b"NOPE"]),
4803            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4804        );
4805    }
4806
4807    #[test]
4808    fn the_command_counter_counts_every_command_including_the_bad_ones() {
4809        let mut f = Fixture::new();
4810        f.run(&[b"PING"]);
4811        f.run(&[b"NOPE"]);
4812        f.run(&[b"GET"]);
4813        assert_eq!(f.server.totals().commands, 3);
4814    }
4815
4816    #[test]
4817    fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
4818        let mut server = Server::new();
4819        server.set_threads(2);
4820        // A fresh server has every database marked, so start from nothing to
4821        // see the one mark arrive.
4822        server.dirty = 0;
4823        server.locals[1].mark(1 << 9);
4824        server.collect_marks();
4825        assert_ne!(server.dirty & (1 << 9), 0);
4826        // And taken once rather than left to be taken again next turn.
4827        assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
4828    }
4829
4830    #[test]
4831    fn what_two_threads_counted_is_added_up_when_info_asks() {
4832        let mut server = Server::new();
4833        server.set_threads(2);
4834        // Written into the two sets by hand, because what is under test is the
4835        // adding up and not the claiming, and one test thread can only ever
4836        // claim one set.
4837        let ping = lookup(b"PING").expect("PING is a command");
4838        for (at, calls) in [(0, 2), (1, 3)] {
4839            let counters = &server.locals[at];
4840            for _ in 0..calls {
4841                counters.stats.commands.bump();
4842                counters.cmdstats.at(ping).calls.bump();
4843            }
4844            counters.stats.opened();
4845        }
4846        assert_eq!(server.totals().commands, 5);
4847        assert_eq!(server.totals().clients, 2);
4848        assert_eq!(server.totals().connections, 2);
4849        let rows: Vec<_> = server.command_stats().collect();
4850        assert_eq!(rows.len(), 1);
4851        assert_eq!(rows[0].0, "ping");
4852        assert_eq!(rows[0].1.calls, 5);
4853        // A reset takes the totals and leaves the open connections, which are
4854        // still open.
4855        server.reset_stats();
4856        assert_eq!(server.totals().commands, 0);
4857        assert_eq!(server.totals().connections, 0);
4858        assert_eq!(server.totals().clients, 2);
4859    }
4860
4861    #[test]
4862    fn the_parked_count_says_what_the_waiter_list_says() {
4863        let mut f = Fixture::new();
4864        assert_eq!(f.server.parked(), 0);
4865        for client in 1..=3u64 {
4866            f.session = Session::new(client);
4867            assert_eq!(f.flow(&[b"BLPOP", b"q", b"0"]).0, Flow::Block);
4868        }
4869        assert_eq!(f.server.parked(), 3);
4870        assert_eq!(f.server.waiters().len(), 3);
4871
4872        // The three ways the list gets shorter, each of which has to move the
4873        // number with it, because a number left behind is either a walk of the
4874        // list that never happens or one that runs off the end of it.
4875        f.server.drop_waiter(1);
4876        assert_eq!(f.server.parked(), f.server.waiters().len());
4877        f.server.forget_waiters(1);
4878        assert_eq!(f.server.parked(), f.server.waiters().len());
4879        f.run(&[b"RPUSH", b"q", b"v"]);
4880        let mut out = Out::new(Proto::Resp2);
4881        assert!(f.server.serve_waiter(0, 0, &mut out));
4882        f.server.drop_waiter(0);
4883        assert_eq!(f.server.parked(), 0);
4884        assert!(f.server.waiters().is_empty());
4885    }
4886
4887    #[test]
4888    fn a_set_goes_from_bytes_to_bytes() {
4889        let mut f = Fixture::new();
4890        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4891        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4892        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4893        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4894        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4895        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4896        assert_eq!(
4897            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4898            "*3\r\n:1\r\n:0\r\n:1\r\n"
4899        );
4900        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4901        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4902    }
4903
4904    #[test]
4905    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4906        let mut f = Fixture::new();
4907        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4908        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4909        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4910        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4911        assert_eq!(
4912            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4913            "*2\r\n:0\r\n:0\r\n"
4914        );
4915        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4916    }
4917
4918    #[test]
4919    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4920        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
4921        // and one that gets a `*` hands it a list, without either of them being
4922        // told which command was sent.
4923        let mut f = Fixture::new();
4924        f.run(&[b"SADD", b"s", b"one"]);
4925        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4926
4927        f.run(&[b"HELLO", b"3"]);
4928        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4929    }
4930
4931    #[test]
4932    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4933        // An intset holds the number, so these digits exist for the first time
4934        // in the reply buffer.
4935        let mut f = Fixture::new();
4936        f.run(&[b"SADD", b"s", b"42"]);
4937        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4938        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4939        assert_eq!(
4940            f.run(&[b"SISMEMBER", b"s", b"042"]),
4941            ":0\r\n",
4942            "the member is the bytes and not the number they parse to"
4943        );
4944    }
4945
4946    #[test]
4947    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4948        let mut f = Fixture::new();
4949        f.run(&[b"SET", b"str", b"v"]);
4950        f.run(&[b"SADD", b"set", b"a"]);
4951
4952        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4953        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4954        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4955        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4956        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4957        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4958        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4959        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4960        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4961
4962        // MGET is the one that does not, because Redis gives nil for the odd
4963        // key out rather than failing the good keys next to it.
4964        assert_eq!(
4965            f.run(&[b"MGET", b"str", b"set", b"nope"]),
4966            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4967        );
4968        // And plain SET overwrites any type, which takes the body with it.
4969        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4970        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4971    }
4972
4973    #[test]
4974    fn a_wrongtype_leaves_nothing_half_written() {
4975        // SMISMEMBER writes an array header and then one reply per member, so
4976        // it is the first command in the server that could get a header out in
4977        // front of an error if it checked its key in the wrong order.
4978        let mut f = Fixture::new();
4979        f.run(&[b"SET", b"k", b"v"]);
4980        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4981        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4982        assert!(!reply.contains('*'), "an array header went out in front");
4983    }
4984
4985    #[test]
4986    fn emptying_a_set_takes_the_key_with_it() {
4987        let mut f = Fixture::new();
4988        f.run(&[b"SADD", b"s", b"a", b"b"]);
4989        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4990        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4991        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4992        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4993        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4994    }
4995
4996    /// Pull the cursor and the members out of one `SSCAN` reply.
4997    ///
4998    /// Crude on purpose. A test that walked a set through a real client would
4999    /// be testing the client, and what these tests are about is the shape of
5000    /// the bytes and the fact that a walk sees every member once.
5001    fn split_scan(reply: &str) -> (String, Vec<String>) {
5002        let mut lines = reply.split("\r\n");
5003        assert_eq!(lines.next(), Some("*2"), "got {reply}");
5004        lines.next().expect("the cursor header");
5005        let cursor = lines.next().expect("the cursor").to_owned();
5006        let header = lines.next().expect("the member header");
5007        let n: usize = header[1..].parse().expect("a member count");
5008        let mut members = Vec::with_capacity(n);
5009        for _ in 0..n {
5010            lines.next().expect("a member header");
5011            members.push(lines.next().expect("a member").to_owned());
5012        }
5013        (cursor, members)
5014    }
5015
5016    #[test]
5017    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
5018        let mut f = Fixture::new();
5019        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
5020
5021        let one = f.run(&[b"SPOP", b"s"]);
5022        assert!(
5023            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
5024            "got {one}"
5025        );
5026        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
5027
5028        // A count takes that many, and the last one takes the key with it.
5029        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
5030        assert!(rest.starts_with("*3\r\n"), "got {rest}");
5031        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5032        // And a pop at a key that is not there is a nil, not an empty bulk.
5033        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
5034        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
5035    }
5036
5037    #[test]
5038    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
5039        // The one place in the server where the reply type carries something
5040        // the command name does not. SPOP's members are distinct so a RESP3
5041        // client can build a set out of them. SRANDMEMBER with a negative count
5042        // can hand back the same member three times, and a set would lose two.
5043        let mut f = Fixture::new();
5044        f.run(&[b"HELLO", b"3"]);
5045        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
5046
5047        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
5048        // And a positive count is an array too, since Redis makes it one.
5049        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
5050
5051        // A negative count against a set of one is where the difference bites:
5052        // the same member three times, which is a three element reply and would
5053        // have been a one element reply if it had gone out as a set.
5054        f.run(&[b"SADD", b"one", b"z"]);
5055        assert_eq!(
5056            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
5057            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
5058        );
5059    }
5060
5061    #[test]
5062    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
5063        let mut f = Fixture::new();
5064        f.run(&[b"SADD", b"s", b"only"]);
5065        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5066        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5067        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
5068
5069        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
5070        // The count form answers an empty array rather than a nil, which is the
5071        // pair of answers Redis gives and is not the pair it looks like.
5072        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
5073        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
5074        // Asking for more than is there answers all of it once and not padding.
5075        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
5076    }
5077
5078    #[test]
5079    fn a_pop_count_that_is_not_a_positive_number_says_so() {
5080        let mut f = Fixture::new();
5081        f.run(&[b"SADD", b"s", b"a"]);
5082        let bad = "-ERR value is out of range, must be positive\r\n";
5083        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
5084        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
5085        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
5086        // Zero is allowed and is a real answer rather than an error.
5087        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
5088        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
5089    }
5090
5091    #[test]
5092    fn a_scan_walks_a_set_of_any_size_exactly_once() {
5093        let mut f = Fixture::new();
5094        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
5095        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
5096            .into_iter()
5097            .chain(members.iter().map(Vec::as_slice))
5098            .collect();
5099        f.run(&args);
5100
5101        let mut seen = Vec::new();
5102        let mut cursor = "0".to_owned();
5103        loop {
5104            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
5105            let (next, got) = split_scan(&reply);
5106            seen.extend(got);
5107            cursor = next;
5108            if cursor == "0" {
5109                break;
5110            }
5111        }
5112        seen.sort();
5113        seen.dedup();
5114        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
5115
5116        // A set small enough to be a listpack answers in one call whatever
5117        // cursor it was handed, which is what Redis does for that encoding.
5118        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
5119        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
5120        assert_eq!(cursor, "0");
5121        assert_eq!(got.len(), 3);
5122        // And a key that is not there is a finished scan of nothing.
5123        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
5124    }
5125
5126    #[test]
5127    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
5128        let mut f = Fixture::new();
5129        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
5130
5131        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
5132        let mut got = got;
5133        got.sort();
5134        assert_eq!(got, ["aa", "ab"]);
5135
5136        // An integer member has no digits stored anywhere, so MATCH is the one
5137        // place a scan pays to write some.
5138        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
5139        let mut got = got;
5140        got.sort();
5141        assert_eq!(got, ["12", "13"]);
5142
5143        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
5144        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
5145        assert_eq!(
5146            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
5147            "-ERR syntax error\r\n"
5148        );
5149        // A count under one is a syntax error and not a range error, which is
5150        // the odder of Redis's two answers and the reason it is copied exactly.
5151        assert_eq!(
5152            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
5153            "-ERR syntax error\r\n"
5154        );
5155    }
5156
5157    #[test]
5158    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
5159        let mut f = Fixture::new();
5160        f.run(&[b"SADD", b"src", b"a", b"b"]);
5161        f.run(&[b"SADD", b"dst", b"c"]);
5162
5163        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
5164        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
5165        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
5166        // A member that is not in the source is a zero and moves nothing.
5167        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
5168        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
5169
5170        // A destination that does not exist gets made, and a source that runs
5171        // out goes away.
5172        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
5173        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
5174        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
5175    }
5176
5177    #[test]
5178    fn moving_checks_the_types_in_the_order_redis_checks_them() {
5179        // Not the order it looks like it should be. A source that is not there
5180        // answers zero without ever looking at the destination, so this is a
5181        // zero and not a WRONGTYPE even though the destination is a string.
5182        let mut f = Fixture::new();
5183        f.run(&[b"SET", b"str", b"v"]);
5184        f.run(&[b"SADD", b"set", b"a"]);
5185
5186        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5187        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
5188        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
5189        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
5190        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
5191        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
5192        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
5193        assert_eq!(
5194            f.run(&[b"SISMEMBER", b"set", b"a"]),
5195            ":1\r\n",
5196            "and none of that moved anything"
5197        );
5198    }
5199
5200    #[test]
5201    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5202        // SSCAN writes an outer array header before it walks, so it is the
5203        // command most likely to get bytes out in front of an error.
5204        let mut f = Fixture::new();
5205        f.run(&[b"SADD", b"s", b"a"]);
5206        for bad in [
5207            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
5208            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
5209            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
5210        ] {
5211            let reply = f.run(bad);
5212            assert!(reply.starts_with("-ERR"), "got {reply}");
5213            assert!(!reply.contains('*'), "an array header went out in front");
5214        }
5215    }
5216
5217    #[test]
5218    fn a_hash_writes_reads_and_deletes_its_fields() {
5219        let mut f = Fixture::new();
5220        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
5221        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
5222        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5223        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
5224        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
5225        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
5226        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
5227        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
5228        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
5229        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
5230
5231        // The value the client sent is `9`, so HGET h b must not find the `2`
5232        // that is a value. A search with a step of one would have.
5233        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
5234
5235        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
5236        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
5237        assert_eq!(
5238            f.run(&[b"EXISTS", b"h"]),
5239            ":0\r\n",
5240            "and losing the last field lost the key"
5241        );
5242    }
5243
5244    #[test]
5245    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
5246        let mut f = Fixture::new();
5247        f.run(&[b"HSET", b"h", b"a", b"1"]);
5248        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5249        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
5250        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
5251        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
5252        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
5253
5254        f.run(&[b"HELLO", b"3"]);
5255        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
5256        assert_eq!(
5257            f.run(&[b"HGETALL", b"nokey"]),
5258            "%0\r\n",
5259            "a missing key is the empty hash and never a nil"
5260        );
5261        assert_eq!(
5262            f.run(&[b"HKEYS", b"h"]),
5263            "*1\r\n$1\r\na\r\n",
5264            "and the two that answer one side stay arrays"
5265        );
5266    }
5267
5268    #[test]
5269    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
5270        let mut f = Fixture::new();
5271        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
5272        assert_eq!(
5273            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
5274            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
5275            "the reply is positional, so b is a nil and not a gap"
5276        );
5277        assert_eq!(
5278            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
5279            "*2\r\n$-1\r\n$-1\r\n",
5280            "and a missing key is all nils rather than an empty array"
5281        );
5282
5283        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
5284        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
5285        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5286    }
5287
5288    #[test]
5289    fn a_hash_counts_up_and_says_so_when_it_cannot() {
5290        let mut f = Fixture::new();
5291        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
5292        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
5293        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
5294        assert_eq!(
5295            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
5296            "$4\r\n10.5\r\n",
5297            "a bulk string and not a double, on both protocols"
5298        );
5299
5300        f.run(&[b"HSET", b"h", b"s", b"words"]);
5301        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
5302        assert!(
5303            bad.starts_with("-ERR hash value is not an integer"),
5304            "{bad}"
5305        );
5306        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
5307        assert!(
5308            bad.starts_with("-ERR value is not an integer"),
5309            "a bad argument is not yet a hash value, {bad}"
5310        );
5311        assert_eq!(
5312            f.run(&[b"HGET", b"h", b"s"]),
5313            "$5\r\nwords\r\n",
5314            "and neither of them wrote anything"
5315        );
5316    }
5317
5318    #[test]
5319    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
5320        let mut f = Fixture::new();
5321        for i in 0..500 {
5322            let field = format!("field-{i}");
5323            let value = format!("value-{i}");
5324            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
5325        }
5326
5327        let mut seen: Vec<String> = Vec::new();
5328        let mut cursor = "0".to_owned();
5329        loop {
5330            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
5331            let (next, items) = scan_reply(&reply);
5332            assert_eq!(items.len() % 2, 0, "a pair went out half written");
5333            for pair in items.chunks(2) {
5334                assert_eq!(
5335                    pair[0].strip_prefix("field-"),
5336                    pair[1].strip_prefix("value-"),
5337                    "a field came back with someone else's value"
5338                );
5339                seen.push(pair[0].clone());
5340            }
5341            cursor = next;
5342            if cursor == "0" {
5343                break;
5344            }
5345        }
5346        seen.sort();
5347        seen.dedup();
5348        assert_eq!(seen.len(), 500, "every field once and only once");
5349
5350        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
5351        assert!(
5352            items.iter().all(|s| s.starts_with("field-")),
5353            "NOVALUES still sent the values"
5354        );
5355
5356        let (_, one) = scan_reply(&f.run(&[
5357            b"HSCAN",
5358            b"h",
5359            b"0",
5360            b"MATCH",
5361            b"field-499",
5362            b"COUNT",
5363            b"1000",
5364        ]));
5365        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
5366    }
5367
5368    #[test]
5369    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
5370        let mut f = Fixture::new();
5371        f.run(&[b"HSET", b"h", b"a", b"1"]);
5372        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
5373        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
5374        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
5375        assert_eq!(
5376            f.run(&[b"HRANDFIELD", b"h", b"3"]),
5377            "*1\r\n$1\r\na\r\n",
5378            "a positive count is capped at the size of the hash"
5379        );
5380        assert_eq!(
5381            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
5382            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
5383            "and a negative one repeats itself"
5384        );
5385        assert_eq!(
5386            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5387            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5388            "flat on RESP2"
5389        );
5390
5391        f.run(&[b"HELLO", b"3"]);
5392        assert_eq!(
5393            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5394            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5395            "and nested on RESP3, but still an array and never a map"
5396        );
5397    }
5398
5399    #[test]
5400    fn every_hash_command_says_wrongtype_and_writes_nothing() {
5401        let mut f = Fixture::new();
5402        f.run(&[b"SET", b"str", b"v"]);
5403        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5404
5405        for cmd in [
5406            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5407            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5408            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5409            &[b"HGET".as_slice(), b"str", b"f"][..],
5410            &[b"HMGET".as_slice(), b"str", b"f"][..],
5411            &[b"HDEL".as_slice(), b"str", b"f"][..],
5412            &[b"HLEN".as_slice(), b"str"][..],
5413            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5414            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5415            &[b"HGETALL".as_slice(), b"str"][..],
5416            &[b"HKEYS".as_slice(), b"str"][..],
5417            &[b"HVALS".as_slice(), b"str"][..],
5418            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5419            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5420            &[b"HRANDFIELD".as_slice(), b"str"][..],
5421            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5422            &[b"HSCAN".as_slice(), b"str", b"0"][..],
5423        ] {
5424            let reply = f.run(cmd);
5425            assert_eq!(reply, wrong, "{:?}", cmd[0]);
5426        }
5427        assert_eq!(
5428            f.run(&[b"GET", b"str"]),
5429            "$1\r\nv\r\n",
5430            "and none of them touched the value"
5431        );
5432    }
5433
5434    #[test]
5435    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5436        let mut f = Fixture::new();
5437        f.run(&[b"HSET", b"h", b"f", b"v"]);
5438        for bad in [
5439            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5440            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5441            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5442            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5443        ] {
5444            let reply = f.run(bad);
5445            assert!(reply.starts_with("-ERR"), "got {reply}");
5446            assert!(!reply.contains('*'), "an array header went out in front");
5447        }
5448    }
5449
5450    #[test]
5451    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5452        let mut f = Fixture::new();
5453        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5454        assert_eq!(
5455            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5456            "*1\r\n:1\r\n"
5457        );
5458        assert_eq!(
5459            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5460            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5461            "one answer per field, and the two sentinels are TTL's own"
5462        );
5463
5464        // The same deadline in the other three units, all of them derived from
5465        // the one number the store kept.
5466        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5467        assert!((99_000..=100_000).contains(&ms), "got {ms}");
5468        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5469        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5470        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5471        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5472
5473        assert_eq!(
5474            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5475            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5476            "one for the deadline taken off, and it does not say what it was"
5477        );
5478        assert_eq!(
5479            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5480            "*1\r\n:-1\r\n"
5481        );
5482        assert_eq!(
5483            f.run(&[b"HGET", b"h", b"a"]),
5484            "$1\r\n1\r\n",
5485            "and the field is still there with the value it had"
5486        );
5487    }
5488
5489    #[test]
5490    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5491        let mut f = Fixture::new();
5492        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5493        assert_eq!(
5494            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5495            "*1\r\n:2\r\n",
5496            "two, and not one, because nothing was stored"
5497        );
5498        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5499        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5500
5501        assert_eq!(
5502            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5503            "*1\r\n:2\r\n"
5504        );
5505        assert_eq!(
5506            f.run(&[b"EXISTS", b"h"]),
5507            ":0\r\n",
5508            "and the last field going took the key with it"
5509        );
5510
5511        // Zero is a delete and not an error, where minus one is an error. That
5512        // is Redis's split and it is easy to get backwards.
5513        f.run(&[b"HSET", b"h", b"a", b"1"]);
5514        assert_eq!(
5515            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5516            "*1\r\n:2\r\n"
5517        );
5518    }
5519
5520    #[test]
5521    fn a_field_is_gone_once_its_moment_passes() {
5522        let mut f = Fixture::new();
5523        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5524        assert_eq!(
5525            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5526            "*1\r\n:1\r\n"
5527        );
5528        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5529
5530        // Time moves once per turn of the event loop and nowhere else, so a
5531        // test moves it by hand rather than by sleeping. There is nothing to
5532        // sleep for: the deadline is a number and so is the clock.
5533        f.server.advance_clock_ms(60);
5534        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5535        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5536        assert_eq!(
5537            f.run(&[b"HGETALL", b"h"]),
5538            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5539            "and the walks do not hand back a field that has expired"
5540        );
5541    }
5542
5543    #[test]
5544    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5545        let mut f = Fixture::new();
5546        for cmd in [
5547            &[
5548                b"HEXPIRE".as_slice(),
5549                b"nokey",
5550                b"100",
5551                b"FIELDS",
5552                b"2",
5553                b"a",
5554                b"b",
5555            ][..],
5556            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5557            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5558            &[
5559                b"HEXPIRETIME".as_slice(),
5560                b"nokey",
5561                b"FIELDS",
5562                b"2",
5563                b"a",
5564                b"b",
5565            ][..],
5566            &[
5567                b"HPERSIST".as_slice(),
5568                b"nokey",
5569                b"FIELDS",
5570                b"2",
5571                b"a",
5572                b"b",
5573            ][..],
5574        ] {
5575            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5576        }
5577    }
5578
5579    #[test]
5580    fn writing_a_field_clears_the_deadline_that_was_on_it() {
5581        let mut f = Fixture::new();
5582        f.run(&[b"HSET", b"h", b"a", b"1"]);
5583        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5584        f.run(&[b"HSET", b"h", b"a", b"2"]);
5585        assert_eq!(
5586            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5587            "*1\r\n:-1\r\n",
5588            "Redis has done this since 7.4, and it is why HGETEX exists"
5589        );
5590    }
5591
5592    #[test]
5593    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5594        let mut f = Fixture::new();
5595        f.run(&[b"HSET", b"h", b"a", b"1"]);
5596        assert_eq!(
5597            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5598            "*1\r\n:0\r\n",
5599            "XX on a field with no deadline changes nothing"
5600        );
5601        assert_eq!(
5602            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5603            "*1\r\n:1\r\n"
5604        );
5605        assert_eq!(
5606            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5607            "*1\r\n:0\r\n",
5608            "and NX will not move one that is already there"
5609        );
5610        assert_eq!(
5611            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5612            "*1\r\n:0\r\n"
5613        );
5614        assert_eq!(
5615            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5616            "*1\r\n:1\r\n"
5617        );
5618        assert_eq!(
5619            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5620            "*1\r\n:1\r\n"
5621        );
5622        assert_eq!(
5623            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5624            "*1\r\n:50\r\n"
5625        );
5626    }
5627
5628    #[test]
5629    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5630        let mut f = Fixture::new();
5631        f.run(&[b"HSET", b"h", b"a", b"1"]);
5632        for (bad, want) in [
5633            (
5634                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5635                "-ERR invalid expire time, must be >= 0",
5636            ),
5637            (
5638                &[
5639                    b"HEXPIRE".as_slice(),
5640                    b"h",
5641                    b"9999999999999999",
5642                    b"FIELDS",
5643                    b"1",
5644                    b"a",
5645                ][..],
5646                "-ERR invalid expire time in 'hexpire' command",
5647            ),
5648            (
5649                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5650                "-ERR wrong number of arguments for 'hexpire' command",
5651            ),
5652            (
5653                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5654                "-ERR Parameter `numFields` should be greater than 0",
5655            ),
5656            (
5657                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5658                "-ERR wrong number of arguments",
5659            ),
5660            (
5661                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5662                "-ERR wrong number of arguments",
5663            ),
5664        ] {
5665            let reply = f.run(bad);
5666            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5667            assert!(!reply.contains('*'), "an array header went out in front");
5668        }
5669        assert_eq!(
5670            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5671            "*1\r\n:-1\r\n",
5672            "and not one of them put a deadline on anything"
5673        );
5674    }
5675
5676    #[test]
5677    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5678        let mut f = Fixture::new();
5679        f.run(&[b"SET", b"str", b"v"]);
5680        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5681
5682        for cmd in [
5683            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5684            &[
5685                b"HPEXPIRE".as_slice(),
5686                b"str",
5687                b"100",
5688                b"FIELDS",
5689                b"1",
5690                b"f",
5691            ][..],
5692            &[
5693                b"HEXPIREAT".as_slice(),
5694                b"str",
5695                b"9999999999",
5696                b"FIELDS",
5697                b"1",
5698                b"f",
5699            ][..],
5700            &[
5701                b"HPEXPIREAT".as_slice(),
5702                b"str",
5703                b"9999999999999",
5704                b"FIELDS",
5705                b"1",
5706                b"f",
5707            ][..],
5708            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5709            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5710            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5711            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5712            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5713        ] {
5714            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5715        }
5716        assert_eq!(
5717            f.run(&[b"GET", b"str"]),
5718            "$1\r\nv\r\n",
5719            "and none of them touched the value"
5720        );
5721    }
5722
5723    #[test]
5724    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5725        let mut f = Fixture::new();
5726        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5727        assert_eq!(
5728            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5729            "*2\r\n$1\r\n1\r\n$-1\r\n",
5730            "positional, so the field that was not there is a nil in its place"
5731        );
5732        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5733        assert_eq!(
5734            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5735            "*1\r\n$-1\r\n"
5736        );
5737        assert_eq!(
5738            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5739            "*1\r\n$1\r\n2\r\n"
5740        );
5741        assert_eq!(
5742            f.run(&[b"EXISTS", b"h"]),
5743            ":0\r\n",
5744            "and the last field took the key"
5745        );
5746    }
5747
5748    #[test]
5749    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5750        let mut f = Fixture::new();
5751        f.run(&[b"HSET", b"h", b"a", b"1"]);
5752        assert_eq!(
5753            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5754            "*1\r\n$1\r\n1\r\n"
5755        );
5756        assert_eq!(
5757            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5758            "*1\r\n:-1\r\n",
5759            "no option means leave it alone, which is the one place this is not GETEX"
5760        );
5761
5762        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5763        assert_eq!(
5764            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5765            "*1\r\n:100\r\n"
5766        );
5767        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5768        assert_eq!(
5769            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5770            "*1\r\n:100\r\n",
5771            "and a plain read really does leave it alone"
5772        );
5773        assert_eq!(
5774            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5775            "*1\r\n$1\r\n1\r\n"
5776        );
5777        assert_eq!(
5778            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5779            "*1\r\n:-1\r\n"
5780        );
5781
5782        assert_eq!(
5783            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5784            "*1\r\n$1\r\n1\r\n",
5785            "the value goes out before the deadline that has already gone is applied"
5786        );
5787        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5788        assert_eq!(
5789            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5790            "*1\r\n$-1\r\n"
5791        );
5792    }
5793
5794    #[test]
5795    fn hsetex_writes_all_of_it_or_none_of_it() {
5796        let mut f = Fixture::new();
5797        assert_eq!(
5798            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5799            ":1\r\n"
5800        );
5801        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5802        assert_eq!(
5803            f.run(&[
5804                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5805            ]),
5806            ":0\r\n",
5807            "FNX wants every field named to be missing"
5808        );
5809        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5810        assert_eq!(
5811            f.run(&[b"HEXISTS", b"h", b"new"]),
5812            ":0\r\n",
5813            "and none of the list was written"
5814        );
5815        assert_eq!(
5816            f.run(&[
5817                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5818            ]),
5819            ":0\r\n",
5820            "and FXX wants every one of them to be there"
5821        );
5822        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5823        assert_eq!(
5824            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5825            ":1\r\n"
5826        );
5827        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5828
5829        assert_eq!(
5830            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5831            ":0\r\n"
5832        );
5833        assert_eq!(
5834            f.run(&[b"EXISTS", b"gone"]),
5835            ":0\r\n",
5836            "a key with no fields cannot meet FXX and is not created trying"
5837        );
5838    }
5839
5840    #[test]
5841    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5842        let mut f = Fixture::new();
5843        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5844        assert_eq!(
5845            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5846            "*1\r\n:100\r\n"
5847        );
5848
5849        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5850        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5851        assert_eq!(
5852            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5853            "*1\r\n:100\r\n",
5854            "KEEPTTL put back what the write cleared"
5855        );
5856
5857        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5858        assert_eq!(
5859            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5860            "*1\r\n:-1\r\n",
5861            "and without it a write clears the deadline the way HSET does"
5862        );
5863
5864        // Any order, because Redis reads these in a loop and not in a fixed
5865        // sequence.
5866        assert_eq!(
5867            f.run(&[
5868                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5869            ]),
5870            ":1\r\n"
5871        );
5872        assert_eq!(
5873            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5874            "*1\r\n:100\r\n"
5875        );
5876
5877        assert_eq!(
5878            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5879            ":1\r\n",
5880            "written, and not the separate code the HEXPIRE family has for this"
5881        );
5882        assert_eq!(
5883            f.run(&[b"EXISTS", b"h"]),
5884            ":0\r\n",
5885            "and storing it and then removing it emptied the hash"
5886        );
5887    }
5888
5889    #[test]
5890    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5891        let mut f = Fixture::new();
5892        f.run(&[b"HSET", b"h", b"a", b"1"]);
5893        for (bad, want) in [
5894            // HGETDEL has three sentences of its own for these three mistakes.
5895            (
5896                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5897                "-ERR Number of fields must be a positive integer",
5898            ),
5899            (
5900                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5901                "-ERR The `numfields` parameter must match the number of arguments",
5902            ),
5903            (
5904                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5905                "-ERR Mandatory argument FIELDS is missing or not at the right position",
5906            ),
5907            // And HGETEX and HSETEX have three different ones between them.
5908            (
5909                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5910                "-ERR invalid number of fields",
5911            ),
5912            (
5913                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5914                "-ERR wrong number of arguments",
5915            ),
5916            (
5917                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5918                "-ERR unknown argument: FIELD",
5919            ),
5920            (
5921                &[
5922                    b"HGETEX".as_slice(),
5923                    b"h",
5924                    b"KEEPTTL",
5925                    b"FIELDS",
5926                    b"1",
5927                    b"a",
5928                ][..],
5929                "-ERR unknown argument: KEEPTTL",
5930            ),
5931            (
5932                &[
5933                    b"HGETEX".as_slice(),
5934                    b"h",
5935                    b"EX",
5936                    b"100",
5937                    b"PERSIST",
5938                    b"FIELDS",
5939                    b"1",
5940                    b"a",
5941                ][..],
5942                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5943            ),
5944            (
5945                &[
5946                    b"HSETEX".as_slice(),
5947                    b"h",
5948                    b"EX",
5949                    b"1",
5950                    b"KEEPTTL",
5951                    b"FIELDS",
5952                    b"1",
5953                    b"a",
5954                    b"1",
5955                ][..],
5956                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5957            ),
5958            (
5959                &[
5960                    b"HSETEX".as_slice(),
5961                    b"h",
5962                    b"FNX",
5963                    b"FXX",
5964                    b"FIELDS",
5965                    b"1",
5966                    b"a",
5967                    b"1",
5968                ][..],
5969                "-ERR Only one of FXX or FNX arguments can be specified",
5970            ),
5971            (
5972                &[
5973                    b"HSETEX".as_slice(),
5974                    b"h",
5975                    b"FIELDS",
5976                    b"2",
5977                    b"a",
5978                    b"1",
5979                    b"b",
5980                ][..],
5981                "-ERR wrong number of arguments",
5982            ),
5983            (
5984                &[
5985                    b"HGETEX".as_slice(),
5986                    b"h",
5987                    b"EX",
5988                    b"-1",
5989                    b"FIELDS",
5990                    b"1",
5991                    b"a",
5992                ][..],
5993                "-ERR invalid expire time, must be >= 0",
5994            ),
5995            (
5996                &[
5997                    b"HGETEX".as_slice(),
5998                    b"h",
5999                    b"PXAT",
6000                    b"99999999999999",
6001                    b"FIELDS",
6002                    b"1",
6003                    b"a",
6004                ][..],
6005                "-ERR invalid expire time in 'hgetex' command",
6006            ),
6007            (
6008                &[
6009                    b"HSETEX".as_slice(),
6010                    b"h",
6011                    b"EX",
6012                    b"abc",
6013                    b"FIELDS",
6014                    b"1",
6015                    b"a",
6016                    b"1",
6017                ][..],
6018                "-ERR value is not an integer or out of range",
6019            ),
6020        ] {
6021            let reply = f.run(bad);
6022            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
6023            assert!(!reply.contains('*'), "an array header went out in front");
6024        }
6025        assert_eq!(
6026            f.run(&[b"HGET", b"h", b"a"]),
6027            "$1\r\n1\r\n",
6028            "and not one of them wrote anything"
6029        );
6030        assert_eq!(
6031            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6032            "*1\r\n:-1\r\n"
6033        );
6034    }
6035
6036    #[test]
6037    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
6038        let mut f = Fixture::new();
6039        f.run(&[b"SET", b"str", b"v"]);
6040        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6041        for cmd in [
6042            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6043            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6044            &[
6045                b"HGETEX".as_slice(),
6046                b"str",
6047                b"EX",
6048                b"100",
6049                b"FIELDS",
6050                b"1",
6051                b"f",
6052            ][..],
6053            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
6054        ] {
6055            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
6056        }
6057        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
6058    }
6059
6060    /// The two orders `HIMPORT` juggles, which are not the same order.
6061    ///
6062    /// Values arrive in the order the fields were declared in and the hash is
6063    /// built in sorted order, so the first value is not generally the first
6064    /// field. And the sort is by length before bytes, which nothing else here
6065    /// sorts names with: `b` comes before `aa` where a plain byte comparison
6066    /// would put `aa` first. Both read off 8.10.1.
6067    #[test]
6068    fn himport_writes_declared_values_into_sorted_fields() {
6069        let mut f = Fixture::new();
6070        assert_eq!(
6071            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
6072            "+OK\r\n"
6073        );
6074        assert_eq!(
6075            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
6076            "+OK\r\n"
6077        );
6078        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
6079        assert_eq!(
6080            f.run(&[b"HGETALL", b"k"]),
6081            bulks(&["a", "3", "b", "1", "aa", "2"])
6082        );
6083    }
6084
6085    /// It replaces the key rather than writing over it, so a field the fieldset
6086    /// does not name is gone afterwards and so is the deadline.
6087    #[test]
6088    fn himport_set_replaces_the_whole_key() {
6089        let mut f = Fixture::new();
6090        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
6091        f.run(&[b"EXPIRE", b"k", b"100"]);
6092        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6093        assert_eq!(
6094            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6095            "+OK\r\n"
6096        );
6097        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6098        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
6099    }
6100
6101    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
6102    /// throws them away, and a key built from one outlives it.
6103    #[test]
6104    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
6105        let mut f = Fixture::new();
6106        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
6107        f.run(&[b"SELECT", b"1"]);
6108        assert_eq!(
6109            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6110            "+OK\r\n"
6111        );
6112        f.run(&[b"SELECT", b"0"]);
6113        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
6114        assert_eq!(
6115            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
6116            "-ERR no such fieldset\r\n"
6117        );
6118    }
6119
6120    /// Which complaint wins when a line is wrong in more than one place.
6121    ///
6122    /// The type of the key beats both of the others, so a `HIMPORT SET` against
6123    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
6124    /// the ordering a real server has and not the one the argument order
6125    /// suggests.
6126    #[test]
6127    fn himport_complains_in_the_order_a_real_server_does() {
6128        let mut f = Fixture::new();
6129        f.run(&[b"SET", b"str", b"v"]);
6130        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6131        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6132        assert_eq!(
6133            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
6134            wrong,
6135            "the type beats a missing fieldset"
6136        );
6137        assert_eq!(
6138            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
6139            wrong,
6140            "and it beats a value count that does not fit"
6141        );
6142        assert_eq!(
6143            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
6144            "-ERR no such fieldset\r\n"
6145        );
6146        // One sentence for too few and for too many alike.
6147        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
6148            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
6149            line.extend_from_slice(values);
6150            assert_eq!(
6151                f.run(&line),
6152                "-ERR value count does not match fieldset field count\r\n",
6153                "{} values into two fields",
6154                values.len()
6155            );
6156        }
6157        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6158    }
6159
6160    /// The arity of each subcommand, and the unknown one.
6161    #[test]
6162    fn himport_checks_each_subcommand_count_under_its_own_name() {
6163        let mut f = Fixture::new();
6164        assert_eq!(
6165            f.run(&[b"HIMPORT"]),
6166            "-ERR wrong number of arguments for 'himport' command\r\n"
6167        );
6168        for (rest, name) in [
6169            (&["PREPARE"][..], "prepare"),
6170            (&["PREPARE", "fs"][..], "prepare"),
6171            (&["SET"][..], "set"),
6172            (&["SET", "k"][..], "set"),
6173            (&["SET", "k", "fs"][..], "set"),
6174            (&["DISCARD"][..], "discard"),
6175            (&["DISCARD", "a", "b"][..], "discard"),
6176            (&["DISCARDALL", "x"][..], "discardall"),
6177        ] {
6178            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
6179            line.extend(rest.iter().map(|a| a.as_bytes()));
6180            assert_eq!(
6181                f.run(&line),
6182                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
6183                "HIMPORT {}",
6184                rest.join(" ")
6185            );
6186        }
6187        assert_eq!(
6188            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
6189            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
6190        );
6191    }
6192
6193    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
6194    /// is the answer of the two that could not be guessed from outside.
6195    #[test]
6196    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
6197        let mut f = Fixture::new();
6198        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6199        assert_eq!(
6200            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
6201            "-ERR duplicate field name in fieldset\r\n"
6202        );
6203        assert_eq!(
6204            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6205            "+OK\r\n"
6206        );
6207        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6208    }
6209
6210    /// Preparing the same name twice replaces it, and the two discards count
6211    /// what they took rather than answering OK.
6212    #[test]
6213    fn himport_prepare_replaces_and_the_discards_count() {
6214        let mut f = Fixture::new();
6215        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6216        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
6217        assert_eq!(
6218            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6219            "+OK\r\n"
6220        );
6221        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
6222
6223        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
6224        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
6225        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
6226        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
6227        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
6228        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
6229    }
6230
6231    /// The one integer of a single element array reply.
6232    /// The number out of a plain integer reply.
6233    ///
6234    /// [`int_reply`] is the same thing wrapped in a one element array, which is
6235    /// the shape every hash field command answers in.
6236    fn int(reply: &str) -> i64 {
6237        let body = reply
6238            .strip_prefix(':')
6239            .and_then(|s| s.strip_suffix("\r\n"))
6240            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
6241        body.parse().expect("an integer")
6242    }
6243
6244    fn int_reply(reply: &str) -> i64 {
6245        let body = reply
6246            .strip_prefix("*1\r\n:")
6247            .and_then(|s| s.strip_suffix("\r\n"))
6248            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
6249        body.parse().expect("an integer")
6250    }
6251
6252    /// The cursor and the flat items of a scan reply.
6253    fn scan_reply(reply: &str) -> (String, Vec<String>) {
6254        let mut lines = reply.split("\r\n");
6255        assert_eq!(lines.next(), Some("*2"), "got {reply}");
6256        lines.next().expect("the cursor header");
6257        let cursor = lines.next().expect("a cursor").to_owned();
6258        let header = lines.next().expect("an item count");
6259        let n: usize = header[1..].parse().expect("a count");
6260        let mut items = Vec::with_capacity(n);
6261        for _ in 0..n {
6262            lines.next().expect("an item header");
6263            items.push(lines.next().expect("an item").to_owned());
6264        }
6265        (cursor, items)
6266    }
6267
6268    /// The members of a set reply, sorted, since none of these promise an
6269    /// order and a test that asserted one would be asserting an accident.
6270    fn sorted(reply: &str) -> Vec<String> {
6271        let mut lines = reply.split("\r\n");
6272        let header = lines.next().expect("a header");
6273        assert!(
6274            header.starts_with('*') || header.starts_with('~'),
6275            "got {reply}"
6276        );
6277        let n: usize = header[1..].parse().expect("a member count");
6278        let mut got = Vec::with_capacity(n);
6279        for _ in 0..n {
6280            lines.next().expect("a member header");
6281            got.push(lines.next().expect("a member").to_owned());
6282        }
6283        got.sort();
6284        got
6285    }
6286
6287    #[test]
6288    fn the_algebra_answers_what_the_sets_share_and_do_not() {
6289        let mut f = Fixture::new();
6290        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6291        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6292        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
6293
6294        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
6295        assert_eq!(
6296            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
6297            ["1", "2", "3", "4", "5"]
6298        );
6299        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
6300        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
6301
6302        // A key that is not there is an empty set, which empties an
6303        // intersection and does nothing at all to a union.
6304        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
6305        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
6306        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
6307        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
6308    }
6309
6310    #[test]
6311    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
6312        let mut f = Fixture::new();
6313        f.run(&[b"SADD", b"a", b"x"]);
6314        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
6315        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
6316        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
6317
6318        f.run(&[b"HELLO", b"3"]);
6319        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
6320        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
6321        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
6322        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
6323    }
6324
6325    #[test]
6326    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
6327        let mut f = Fixture::new();
6328        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6329        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6330
6331        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
6332        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
6333        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
6334        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
6335        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
6336        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
6337
6338        // An empty answer deletes the destination rather than leaving an empty
6339        // set behind, and the destination may be one of the sources.
6340        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
6341        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6342        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
6343        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
6344
6345        // And a destination holding something else is overwritten, the same way
6346        // SET overwrites, rather than refused.
6347        f.run(&[b"SET", b"str", b"v"]);
6348        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
6349        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
6350    }
6351
6352    #[test]
6353    fn sintercard_counts_without_building_and_stops_at_a_limit() {
6354        let mut f = Fixture::new();
6355        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6356        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
6357
6358        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
6359        assert_eq!(
6360            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6361            ":2\r\n"
6362        );
6363        assert_eq!(
6364            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6365            ":3\r\n",
6366            "a limit of zero is no limit"
6367        );
6368        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
6369        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
6370
6371        // The counted keys are what make its three error messages its own.
6372        assert_eq!(
6373            f.run(&[b"SINTERCARD", b"0", b"a"]),
6374            "-ERR numkeys should be greater than 0\r\n"
6375        );
6376        assert_eq!(
6377            f.run(&[b"SINTERCARD", b"abc", b"a"]),
6378            "-ERR numkeys should be greater than 0\r\n"
6379        );
6380        assert_eq!(
6381            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
6382            "-ERR Number of keys can't be greater than number of args\r\n"
6383        );
6384        assert_eq!(
6385            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
6386            "-ERR LIMIT can't be negative\r\n"
6387        );
6388        assert_eq!(
6389            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
6390            "-ERR syntax error\r\n"
6391        );
6392        // A key really can be called LIMIT, which is why the count exists.
6393        f.run(&[b"SADD", b"LIMIT", b"2"]);
6394        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
6395    }
6396
6397    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
6398    /// over a difference. Every number here was read off 8.10.1 first.
6399    #[test]
6400    fn sunioncard_and_sdiffcard_count_without_building() {
6401        let mut f = Fixture::new();
6402        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6403        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6404
6405        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6406        assert_eq!(
6407            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6408            ":2\r\n"
6409        );
6410        assert_eq!(
6411            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6412            ":6\r\n",
6413            "a limit of zero is no limit"
6414        );
6415        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6416        assert_eq!(
6417            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6418            ":4\r\n",
6419            "a missing key adds nothing to a union"
6420        );
6421
6422        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6423        assert_eq!(
6424            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6425            ":1\r\n"
6426        );
6427        assert_eq!(
6428            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6429            ":2\r\n",
6430            "a difference is not symmetric"
6431        );
6432        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6433        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6434        assert_eq!(
6435            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6436            ":0\r\n",
6437            "nothing taken away from nothing"
6438        );
6439
6440        // The same three messages SINTERCARD has, because the line is the same
6441        // line and is parsed once for all three.
6442        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6443            assert_eq!(
6444                f.run(&[name, b"0", b"a"]),
6445                "-ERR numkeys should be greater than 0\r\n"
6446            );
6447            assert_eq!(
6448                f.run(&[name, b"abc", b"a"]),
6449                "-ERR numkeys should be greater than 0\r\n"
6450            );
6451            assert_eq!(
6452                f.run(&[name, b"-1", b"a"]),
6453                "-ERR numkeys should be greater than 0\r\n"
6454            );
6455            assert_eq!(
6456                f.run(&[name, b"3", b"a", b"b"]),
6457                "-ERR Number of keys can't be greater than number of args\r\n"
6458            );
6459            assert_eq!(
6460                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6461                "-ERR LIMIT can't be negative\r\n"
6462            );
6463            assert_eq!(
6464                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6465                "-ERR LIMIT can't be negative\r\n",
6466                "a LIMIT that is not a number gets the negative message too"
6467            );
6468            assert_eq!(
6469                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6470                "-ERR syntax error\r\n"
6471            );
6472            assert_eq!(
6473                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6474                "-ERR syntax error\r\n"
6475            );
6476            assert_eq!(
6477                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6478                "-ERR syntax error\r\n"
6479            );
6480        }
6481
6482        // And a key called LIMIT is a key, here as much as on SINTERCARD.
6483        f.run(&[b"SADD", b"LIMIT", b"2"]);
6484        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6485        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6486    }
6487
6488    #[test]
6489    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6490        let mut f = Fixture::new();
6491        f.run(&[b"SADD", b"a", b"1"]);
6492        f.run(&[b"SADD", b"d", b"old"]);
6493        f.run(&[b"SET", b"str", b"v"]);
6494
6495        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6496        for bad in [
6497            &[b"SINTER".as_slice(), b"a", b"str"][..],
6498            &[b"SUNION".as_slice(), b"str"][..],
6499            &[b"SDIFF".as_slice(), b"a", b"str"][..],
6500            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6501            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6502            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6503            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6504        ] {
6505            let reply = f.run(bad);
6506            assert_eq!(reply, wrong, "for {:?}", bad[0]);
6507        }
6508        assert_eq!(
6509            f.run(&[b"SMEMBERS", b"d"]),
6510            "*1\r\n$3\r\nold\r\n",
6511            "and the destination was left alone every time"
6512        );
6513    }
6514
6515    /// The leak a set can spring that nothing on the wire would ever show: the
6516    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
6517    #[test]
6518    fn churning_sets_does_not_grow_the_server() {
6519        let mut f = Fixture::new();
6520        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6521        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6522            .chain(std::iter::once(&b"s"[..]))
6523            .chain(members.iter().map(Vec::as_slice))
6524            .collect();
6525
6526        f.run(&args);
6527        f.run(&[b"DEL", b"s"]);
6528        f.server.compact_step();
6529        let after_first = f.server.memory_bytes();
6530
6531        for _ in 0..200 {
6532            f.run(&args);
6533            f.run(&[b"DEL", b"s"]);
6534            f.server.compact_step();
6535        }
6536        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6537        assert!(
6538            f.server.memory_bytes() <= after_first * 2,
6539            "held {} after two hundred passes against {after_first} after one",
6540            f.server.memory_bytes()
6541        );
6542    }
6543
6544    // --------------------------------------------------------------- bitmaps
6545
6546    /// The two single bit commands, and the encoding rule underneath them.
6547    ///
6548    /// A write always leaves the value `raw` and a read never re-encodes, which
6549    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
6550    /// with its first digit changed after a `SETBIT`.
6551    #[test]
6552    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6553        let mut f = Fixture::new();
6554        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6555        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6556        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6557        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6558        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6559        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6560
6561        // Writing a nought past the end still creates the key and still pads.
6562        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6563        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6564        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6565
6566        f.run(&[b"SET", b"num", b"12345"]);
6567        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6568        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6569        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6570        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6571        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6572    }
6573
6574    /// Counting, in bytes and in bits.
6575    ///
6576    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
6577    /// says 22 for it. The server is the thing being copied here.
6578    #[test]
6579    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6580        let mut f = Fixture::new();
6581        f.run(&[b"SET", b"mykey", b"foobar"]);
6582        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6583        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6584        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6585        assert_eq!(
6586            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6587            ":6\r\n"
6588        );
6589        assert_eq!(
6590            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6591            ":25\r\n"
6592        );
6593        assert_eq!(
6594            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6595            ":17\r\n"
6596        );
6597        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6598
6599        // A start past the end is left where it is and the end is pulled back,
6600        // so the range comes out backwards and counts nothing.
6601        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6602
6603        // A lone start is a syntax error here, where BITPOS allows it.
6604        assert_eq!(
6605            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6606            "-ERR syntax error\r\n"
6607        );
6608        assert_eq!(
6609            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6610            "-ERR syntax error\r\n"
6611        );
6612    }
6613
6614    /// Searching, and the one place a miss is not minus one.
6615    ///
6616    /// A search for a nought that runs to the end of the string answers the
6617    /// length in bits, because the string is treated as if it had noughts after
6618    /// it forever. Give it an explicit end and it answers minus one instead.
6619    #[test]
6620    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6621        let mut f = Fixture::new();
6622        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6623        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6624        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6625        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6626        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6627        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6628
6629        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6630        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6631        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6632        assert_eq!(
6633            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6634            ":8\r\n"
6635        );
6636
6637        // A missing key is all noughts, so a one is never found and a nought is
6638        // at position zero.
6639        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6640        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6641    }
6642
6643    /// The eight operations, with the answers a real server gives for them.
6644    #[test]
6645    fn the_eight_combinations_write_what_a_real_server_writes() {
6646        let mut f = Fixture::new();
6647        f.run(&[b"SET", b"a", b"abc"]);
6648        f.run(&[b"SET", b"b", b"abd"]);
6649        let cases: &[(&[u8], &str)] = &[
6650            (b"AND", "ab`"),
6651            (b"OR", "abg"),
6652            (b"XOR", "\u{0}\u{0}\u{7}"),
6653            (b"DIFF", "\u{0}\u{0}\u{3}"),
6654            (b"DIFF1", "\u{0}\u{0}\u{4}"),
6655            (b"ANDOR", "ab`"),
6656            (b"ONE", "\u{0}\u{0}\u{7}"),
6657        ];
6658        for (op, want) in cases {
6659            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6660            assert_eq!(
6661                f.run(&[b"GET", b"d"]),
6662                format!("$3\r\n{want}\r\n"),
6663                "{op:?}"
6664            );
6665        }
6666        // The one whose answer is not text, so it is compared as bytes.
6667        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6668        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6669
6670        // A missing source is a string of noughts as long as it needs to be, so
6671        // an AND against one writes three zero bytes rather than nothing.
6672        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6673        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6674
6675        // Every source missing is an empty result, and an empty result takes
6676        // the destination with it.
6677        f.run(&[b"SET", b"dest", b"x"]);
6678        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6679        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6680    }
6681
6682    /// What `BITOP` says when it is asked for something it cannot do.
6683    #[test]
6684    fn bitop_names_the_operation_in_its_own_complaints() {
6685        let mut f = Fixture::new();
6686        f.run(&[b"SET", b"a", b"abc"]);
6687        assert_eq!(
6688            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6689            "-ERR syntax error\r\n"
6690        );
6691        assert_eq!(
6692            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6693            "-ERR BITOP NOT must be called with a single source key.\r\n"
6694        );
6695        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6696            assert_eq!(
6697                f.run(&[b"BITOP", op, b"d", b"a"]),
6698                format!(
6699                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
6700                    String::from_utf8_lossy(op)
6701                )
6702            );
6703        }
6704        f.run(&[b"LPUSH", b"l", b"x"]);
6705        assert_eq!(
6706            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6707            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6708        );
6709    }
6710
6711    /// Packed fields, the three overflow policies and the `#` offset.
6712    #[test]
6713    fn bitfield_reads_and_writes_packed_fields() {
6714        let mut f = Fixture::new();
6715        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6716        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6717
6718        assert_eq!(
6719            f.run(&[
6720                b"BITFIELD",
6721                b"bf",
6722                b"INCRBY",
6723                b"u2",
6724                b"100",
6725                b"1",
6726                b"GET",
6727                b"u4",
6728                b"0"
6729            ]),
6730            "*2\r\n:1\r\n:0\r\n"
6731        );
6732        // The field at bit 100 is two bits wide, so it ends in the thirteenth
6733        // byte and the value grew to thirteen bytes to hold it.
6734        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6735
6736        // A `#` offset counts in fields rather than in bits.
6737        assert_eq!(
6738            f.run(&[
6739                b"BITFIELD",
6740                b"bf",
6741                b"SET",
6742                b"u8",
6743                b"#0",
6744                b"255",
6745                b"GET",
6746                b"u8",
6747                b"#0"
6748            ]),
6749            "*2\r\n:0\r\n:255\r\n"
6750        );
6751
6752        assert_eq!(
6753            f.run(&[
6754                b"BITFIELD",
6755                b"bf",
6756                b"OVERFLOW",
6757                b"SAT",
6758                b"INCRBY",
6759                b"i8",
6760                b"0",
6761                b"120",
6762                b"INCRBY",
6763                b"i8",
6764                b"0",
6765                b"120"
6766            ]),
6767            "*2\r\n:119\r\n:127\r\n"
6768        );
6769        assert_eq!(
6770            f.run(&[
6771                b"BITFIELD",
6772                b"bf2",
6773                b"OVERFLOW",
6774                b"FAIL",
6775                b"INCRBY",
6776                b"u2",
6777                b"0",
6778                b"5"
6779            ]),
6780            "*1\r\n$-1\r\n"
6781        );
6782        assert_eq!(
6783            f.run(&[
6784                b"BITFIELD",
6785                b"bf3",
6786                b"OVERFLOW",
6787                b"WRAP",
6788                b"INCRBY",
6789                b"u2",
6790                b"0",
6791                b"5"
6792            ]),
6793            "*1\r\n:1\r\n"
6794        );
6795        assert_eq!(
6796            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6797            "*1\r\n:4611686018427387904\r\n"
6798        );
6799    }
6800
6801    /// A bad subcommand anywhere in the line stops all of it.
6802    ///
6803    /// Redis checks the whole argument list before it runs any of it, so the
6804    /// `SET` in front of the bad type here never happens and the key it would
6805    /// have created is not there afterwards.
6806    #[test]
6807    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6808        let mut f = Fixture::new();
6809        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6810        assert_eq!(
6811            f.run(&[
6812                b"BITFIELD",
6813                b"bad",
6814                b"SET",
6815                b"u8",
6816                b"0",
6817                b"1",
6818                b"GET",
6819                b"u99",
6820                b"0"
6821            ]),
6822            bad_type
6823        );
6824        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6825        assert_eq!(
6826            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6827            bad_type
6828        );
6829        assert_eq!(
6830            f.run(&[b"BITFIELD", b"bad", b"GET"]),
6831            "-ERR syntax error\r\n"
6832        );
6833        assert_eq!(
6834            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6835            "-ERR syntax error\r\n"
6836        );
6837        assert_eq!(
6838            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6839            "-ERR syntax error\r\n"
6840        );
6841        assert_eq!(
6842            f.run(&[
6843                b"BITFIELD",
6844                b"bad",
6845                b"OVERFLOW",
6846                b"NOPE",
6847                b"GET",
6848                b"u8",
6849                b"0"
6850            ]),
6851            "-ERR Invalid OVERFLOW type specified\r\n"
6852        );
6853        assert_eq!(
6854            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6855            "-ERR value is not an integer or out of range\r\n"
6856        );
6857        for at in [&b"#-1"[..], b"abc"] {
6858            assert_eq!(
6859                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6860                "-ERR bit offset is not an integer or out of range\r\n"
6861            );
6862        }
6863    }
6864
6865    /// The read only twin reads, refuses to write, and creates nothing.
6866    #[test]
6867    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6868        let mut f = Fixture::new();
6869        f.run(&[b"SET", b"n", b"123"]);
6870        assert_eq!(
6871            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6872            "*1\r\n:49\r\n"
6873        );
6874        // A read does not unpack an int the way a write does.
6875        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6876
6877        // An OVERFLOW word is allowed even though nothing here can overflow.
6878        assert_eq!(
6879            f.run(&[
6880                b"BITFIELD_RO",
6881                b"n",
6882                b"OVERFLOW",
6883                b"SAT",
6884                b"GET",
6885                b"u8",
6886                b"0"
6887            ]),
6888            "*1\r\n:49\r\n"
6889        );
6890        for sub in [&b"SET"[..], b"INCRBY"] {
6891            assert_eq!(
6892                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6893                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6894            );
6895        }
6896
6897        assert_eq!(
6898            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6899            "*1\r\n:0\r\n"
6900        );
6901        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6902    }
6903
6904    /// The offsets a bitmap command will not take.
6905    #[test]
6906    fn an_offset_off_the_end_of_the_world_is_refused() {
6907        let mut f = Fixture::new();
6908        let bad = "-ERR bit offset is not an integer or out of range\r\n";
6909        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6910            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6911            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6912        }
6913        for arg in [&b"2"[..], b"-1"] {
6914            assert_eq!(
6915                f.run(&[b"BITPOS", b"k", arg]),
6916                "-ERR The bit argument must be 1 or 0.\r\n"
6917            );
6918        }
6919        assert_eq!(
6920            f.run(&[b"BITPOS", b"k", b"abc"]),
6921            "-ERR value is not an integer or out of range\r\n"
6922        );
6923        assert_eq!(
6924            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6925            "-ERR value is not an integer or out of range\r\n"
6926        );
6927        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6928        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6929        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6930    }
6931
6932    /// Every one of the seven refuses a key that is not a string.
6933    #[test]
6934    fn every_bitmap_command_says_wrongtype() {
6935        let mut f = Fixture::new();
6936        f.run(&[b"LPUSH", b"l", b"x"]);
6937        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6938        let cases: &[&[&[u8]]] = &[
6939            &[b"SETBIT", b"l", b"0", b"1"],
6940            &[b"GETBIT", b"l", b"0"],
6941            &[b"BITCOUNT", b"l"],
6942            &[b"BITPOS", b"l", b"1"],
6943            &[b"BITOP", b"AND", b"d", b"l"],
6944            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6945            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6946        ];
6947        for case in cases {
6948            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6949        }
6950    }
6951
6952    // --------------------------------------------------------- hyperloglogs
6953
6954    #[test]
6955    fn a_sketch_is_added_to_and_counted() {
6956        let mut f = Fixture::new();
6957        // Creating the key counts as a change, even with nothing to add.
6958        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6959        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6960        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6961        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6962        // And it is a string, which is not an implementation detail: a client
6963        // can `GET` a sketch out of one server and `SET` it into another.
6964        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6965        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6966
6967        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6968        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6969        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6970    }
6971
6972    #[test]
6973    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6974        let mut f = Fixture::new();
6975        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6976        // Not text, so it is compared as bytes.
6977        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";
6978        let mut reply = b"$27\r\n".to_vec();
6979        reply.extend_from_slice(want);
6980        reply.extend_from_slice(b"\r\n");
6981        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6982    }
6983
6984    #[test]
6985    fn counting_several_keys_counts_their_union() {
6986        let mut f = Fixture::new();
6987        f.run(&[b"PFADD", b"a", b"x", b"y"]);
6988        f.run(&[b"PFADD", b"b", b"y", b"z"]);
6989        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6990        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6991        // A key that is not there is an empty sketch, not an error and not
6992        // something that gets created by being counted.
6993        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6994        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6995        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6996    }
6997
6998    #[test]
6999    fn a_merge_keeps_what_the_destination_had() {
7000        let mut f = Fixture::new();
7001        f.run(&[b"PFADD", b"a", b"x", b"y"]);
7002        f.run(&[b"PFADD", b"b", b"z"]);
7003        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
7004        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
7005        // The destination is one of the sources, so a second merge adds to it.
7006        f.run(&[b"PFADD", b"c", b"w"]);
7007        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
7008        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
7009        // And with no sources it is a no-op that still answers OK and still
7010        // creates a destination that was not there.
7011        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
7012        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
7013    }
7014
7015    #[test]
7016    fn the_debug_forms_answer_four_different_shapes() {
7017        let mut f = Fixture::new();
7018        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7019        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
7020        assert_eq!(
7021            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7022            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
7023        );
7024        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
7025        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
7026        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
7027        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
7028        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7029        // A dense sketch has no opcodes left to print.
7030        assert_eq!(
7031            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7032            "-ERR HLL encoding is not sparse\r\n"
7033        );
7034
7035        // All 16384 registers, of which three are not nought.
7036        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
7037        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
7038        assert_eq!(reply.matches(":0\r\n").count(), 16381);
7039        assert_eq!(reply.matches(":1\r\n").count(), 2);
7040        assert_eq!(reply.matches(":2\r\n").count(), 1);
7041
7042        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
7043    }
7044
7045    #[test]
7046    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
7047        let mut f = Fixture::new();
7048        f.run(&[b"SET", b"plain", b"not a sketch"]);
7049        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
7050        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
7051        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
7052        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
7053        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
7054
7055        // A key that is not a string at all gets the ordinary sentence, and a
7056        // destination that would have been written is not created.
7057        f.run(&[b"RPUSH", b"l", b"x"]);
7058        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7059        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
7060        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
7061        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
7062        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
7063        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
7064    }
7065
7066    #[test]
7067    fn pfdebug_has_its_own_complaints() {
7068        let mut f = Fixture::new();
7069        f.run(&[b"PFADD", b"h", b"a"]);
7070        // The word is quoted exactly as the client spelled it, and this is not
7071        // the "Try X HELP." sentence every other container command uses.
7072        assert_eq!(
7073            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
7074            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
7075        );
7076        // Where all three of the real commands take a missing key as empty.
7077        let gone = "-ERR The specified key does not exist\r\n";
7078        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
7079        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
7080        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
7081        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
7082        assert_eq!(
7083            f.run(&[b"PFDEBUG"]),
7084            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
7085        );
7086        assert_eq!(
7087            f.run(&[b"PFSELFTEST", b"x"]),
7088            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
7089        );
7090    }
7091
7092    #[test]
7093    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
7094        let mut f = Fixture::new();
7095        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7096        // The sketch with its last byte cut off, which is still a header and a
7097        // magic and is a run length encoding that stops short of register 16384.
7098        let reply = f.raw(&[b"GET", b"h"]);
7099        let short = reply[5..reply.len() - 3].to_vec();
7100        f.run(&[b"SET", b"h", &short]);
7101        assert_eq!(
7102            f.run(&[b"PFCOUNT", b"h"]),
7103            "-INVALIDOBJ Corrupted HLL object detected\r\n"
7104        );
7105    }
7106
7107    #[test]
7108    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
7109        let mut f = Fixture::new();
7110        // One that stays sparse and one that has gone dense, since the payload
7111        // carries the bytes and the two encodings are different lengths.
7112        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
7113        for i in 0..10_000u32 {
7114            let ele = format!("e{i}");
7115            f.run(&[b"PFADD", b"big", ele.as_bytes()]);
7116        }
7117        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
7118        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
7119
7120        for key in [&b"small"[..], b"big"] {
7121            let mut copy = key.to_vec();
7122            copy.push(b'2');
7123            let bytes = payload(&f.raw(&[b"DUMP", key]));
7124            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
7125            // The bytes, the encoding and the estimate all come back, which is
7126            // the whole of what byte compatibility across a round trip means.
7127            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
7128            assert_eq!(
7129                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
7130                f.run(&[b"PFDEBUG", b"ENCODING", key])
7131            );
7132            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
7133        }
7134        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
7135        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
7136    }
7137
7138    /// One RESP2 bulk string. The JSON replies are almost all one of these and
7139    /// the text inside them has quotes in it, so writing the frame out by hand
7140    /// buries the part of the assertion that matters.
7141    fn bulk(s: &str) -> String {
7142        format!("${}\r\n{s}\r\n", s.len())
7143    }
7144
7145    /// A RESP2 array of bulk strings, which is what most of the list replies
7146    /// are and what writing them out by hand in every assertion looks like.
7147    fn bulks(parts: &[&str]) -> String {
7148        let mut s = format!("*{}\r\n", parts.len());
7149        for p in parts {
7150            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
7151        }
7152        s
7153    }
7154
7155    #[test]
7156    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
7157        let mut f = Fixture::new();
7158        // Each element in turn goes at the head, so the last one sent is at the
7159        // front when it is over. That reads like a bug in the client and it is
7160        // what every Redis has always done.
7161        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
7162        assert_eq!(
7163            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7164            bulks(&["c", "b", "a"])
7165        );
7166        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
7167        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
7168        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
7169        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
7170        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
7171        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
7172    }
7173
7174    #[test]
7175    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
7176        let mut f = Fixture::new();
7177        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
7178        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
7179        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7180        f.run(&[b"RPUSH", b"k", b"a"]);
7181        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
7182        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
7183        assert_eq!(
7184            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7185            bulks(&["z", "a", "y"])
7186        );
7187    }
7188
7189    /// The four ways a pop can come back with nothing, which are three
7190    /// different replies and a RESP2 client can tell all of them apart.
7191    #[test]
7192    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
7193        let mut f = Fixture::new();
7194        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
7195        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
7196        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
7197        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
7198        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7199        // A count of zero against a list that is there is an empty array and
7200        // not a null array, which is the fourth answer.
7201        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
7202        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
7203        // More than there is takes what there is and the key goes with it.
7204        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
7205        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7206    }
7207
7208    #[test]
7209    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
7210        let mut f = Fixture::new();
7211        f.run(&[b"RPUSH", b"k", b"a"]);
7212        let range = "-ERR value is out of range, must be positive\r\n";
7213        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
7214        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
7215        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
7216        // Redis calls this an arity error and not a syntax error, which is a
7217        // distinction it does not always make.
7218        assert_eq!(
7219            f.run(&[b"LPOP", b"k", b"1", b"2"]),
7220            "-ERR wrong number of arguments for 'lpop' command\r\n"
7221        );
7222        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7223    }
7224
7225    #[test]
7226    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
7227        let mut f = Fixture::new();
7228        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7229        assert_eq!(
7230            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7231            bulks(&["a", "b", "c"])
7232        );
7233        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
7234        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
7235        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
7236        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
7237        assert_eq!(
7238            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
7239            bulks(&["a", "b", "c"])
7240        );
7241        // A key that is not there is an empty range and not a nil, which is the
7242        // one place a list disagrees with a set.
7243        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
7244        assert_eq!(
7245            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
7246            "-ERR value is not an integer or out of range\r\n"
7247        );
7248    }
7249
7250    #[test]
7251    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
7252        let mut f = Fixture::new();
7253        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7254        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
7255        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
7256        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
7257        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
7258        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
7259        assert_eq!(
7260            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7261            bulks(&["a", "b", "z"])
7262        );
7263        // Both ways of missing are errors here rather than a nil, because a
7264        // list is never empty and there is nothing else the reply could be.
7265        assert_eq!(
7266            f.run(&[b"LSET", b"k", b"99", b"z"]),
7267            "-ERR index out of range\r\n"
7268        );
7269        assert_eq!(
7270            f.run(&[b"LSET", b"nope", b"0", b"z"]),
7271            "-ERR no such key\r\n"
7272        );
7273    }
7274
7275    #[test]
7276    fn linsert_says_three_things_with_one_signed_number() {
7277        let mut f = Fixture::new();
7278        // Zero for a key that is not there, which is not the same as minus one
7279        // for a pivot that is not in a list that is.
7280        assert_eq!(
7281            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
7282            ":0\r\n"
7283        );
7284        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7285        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
7286        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
7287        assert_eq!(
7288            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7289            bulks(&["X", "a", "b", "Y"])
7290        );
7291        assert_eq!(
7292            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
7293            ":-1\r\n"
7294        );
7295        assert_eq!(
7296            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
7297            "-ERR syntax error\r\n"
7298        );
7299    }
7300
7301    #[test]
7302    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
7303        let mut f = Fixture::new();
7304        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
7305        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
7306        assert_eq!(
7307            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7308            bulks(&["b", "c", "a"])
7309        );
7310        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
7311        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7312        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
7313        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
7314        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7315        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
7316    }
7317
7318    #[test]
7319    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
7320        let mut f = Fixture::new();
7321        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
7322        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
7323        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7324        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
7325        // leave `EXISTS` answering zero rather than leaving an empty one.
7326        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
7327        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7328        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
7329    }
7330
7331    #[test]
7332    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
7333        let mut f = Fixture::new();
7334        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
7335        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
7336        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
7337        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
7338        assert_eq!(
7339            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
7340            "*2\r\n:0\r\n:3\r\n"
7341        );
7342        assert_eq!(
7343            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
7344            "*3\r\n:6\r\n:3\r\n:0\r\n"
7345        );
7346        // MAXLEN counts elements looked at and not matches found, so three
7347        // stops after `a b c` and finds the one match in it.
7348        assert_eq!(
7349            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
7350            "*1\r\n:0\r\n"
7351        );
7352        // Nothing found is three different replies depending on how it was
7353        // asked and whether the key is there at all.
7354        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
7355        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
7356        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
7357        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
7358    }
7359
7360    #[test]
7361    fn lpos_words_its_three_mistakes_the_way_redis_does() {
7362        let mut f = Fixture::new();
7363        f.run(&[b"RPUSH", b"p", b"a"]);
7364        // The whole sentence and not a prefix, because the older wording of it
7365        // is still all over the internet and clients match on the text.
7366        assert_eq!(
7367            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
7368            "-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"
7369        );
7370        assert_eq!(
7371            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
7372            "-ERR COUNT can't be negative\r\n"
7373        );
7374        assert_eq!(
7375            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
7376            "-ERR MAXLEN can't be negative\r\n"
7377        );
7378        assert_eq!(
7379            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
7380            "-ERR syntax error\r\n"
7381        );
7382        assert_eq!(
7383            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
7384            "-ERR syntax error\r\n"
7385        );
7386    }
7387
7388    #[test]
7389    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
7390        let mut f = Fixture::new();
7391        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7392        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
7393        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7394        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
7395        assert_eq!(
7396            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
7397            "$1\r\na\r\n"
7398        );
7399        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7400        // The same key twice is the documented way to rotate a list and falls
7401        // out of taking the element before deciding where to put it.
7402        f.run(&[b"DEL", b"r"]);
7403        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7404        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7405        assert_eq!(
7406            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7407            bulks(&["3", "1", "2"])
7408        );
7409        assert_eq!(
7410            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7411            "$-1\r\n"
7412        );
7413        assert_eq!(
7414            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7415            "-ERR syntax error\r\n"
7416        );
7417    }
7418
7419    #[test]
7420    fn a_move_checks_the_destination_before_it_takes_anything() {
7421        let mut f = Fixture::new();
7422        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7423        f.run(&[b"SET", b"str", b"v"]);
7424        assert_eq!(
7425            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7426            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7427        );
7428        // The element is still where it was, rather than having gone nowhere.
7429        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7430    }
7431
7432    #[test]
7433    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7434        // OBO is what you get from sending LMOVE that many times, BULK keeps
7435        // the source order. The two only differ when both ends are the same,
7436        // which is the whole reason the word exists.
7437        for (from, to, order, want) in [
7438            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7439            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7440            ("LEFT", "LEFT", "OBO", ["b", "a"]),
7441            ("LEFT", "LEFT", "BULK", ["a", "b"]),
7442            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7443            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7444            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7445            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7446        ] {
7447            let mut f = Fixture::new();
7448            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7449            let how = format!("{from} {to} {order}");
7450            let reply = f.run(&[
7451                b"LMOVEM",
7452                b"s",
7453                b"d",
7454                from.as_bytes(),
7455                to.as_bytes(),
7456                b"COUNT",
7457                b"2",
7458                order.as_bytes(),
7459            ]);
7460            assert_eq!(reply, bulks(&want), "the reply for {how}");
7461            assert_eq!(
7462                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7463                bulks(&want),
7464                "the destination for {how}"
7465            );
7466        }
7467    }
7468
7469    #[test]
7470    fn a_block_move_of_one_needs_no_count_at_all() {
7471        let mut f = Fixture::new();
7472        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7473        assert_eq!(
7474            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7475            bulks(&["a"])
7476        );
7477        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7478        // Six and seven arguments are neither of the two forms, so the
7479        // reference calls both of them a syntax error rather than guessing.
7480        assert_eq!(
7481            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7482            "-ERR syntax error\r\n"
7483        );
7484        assert_eq!(
7485            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7486            "-ERR syntax error\r\n"
7487        );
7488    }
7489
7490    #[test]
7491    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7492        let mut f = Fixture::new();
7493        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7494        // A null array and not a null bulk string, which `redis-cli` prints as
7495        // `(nil)` either way and only the raw wire tells apart. What it would
7496        // have sent is an array, so its nothing is an array's nothing.
7497        assert_eq!(
7498            f.run(&[
7499                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7500            ]),
7501            "*-1\r\n"
7502        );
7503        assert_eq!(
7504            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7505            bulks(&["a", "b", "c"])
7506        );
7507        // COUNT takes what there is, and an emptied source goes away.
7508        assert_eq!(
7509            f.run(&[
7510                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7511            ]),
7512            bulks(&["a", "b", "c"])
7513        );
7514        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7515        assert_eq!(
7516            f.run(&[
7517                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7518            ]),
7519            "*-1\r\n"
7520        );
7521    }
7522
7523    #[test]
7524    fn a_block_move_onto_itself_rotates_by_the_count() {
7525        let mut f = Fixture::new();
7526        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7527        assert_eq!(
7528            f.run(&[
7529                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7530            ]),
7531            bulks(&["a", "b"])
7532        );
7533        assert_eq!(
7534            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7535            bulks(&["c", "a", "b"])
7536        );
7537    }
7538
7539    #[test]
7540    fn a_block_move_reads_the_count_before_the_ordering_word() {
7541        let mut f = Fixture::new();
7542        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7543        f.run(&[b"SET", b"str", b"v"]);
7544        let count = "-ERR count should be greater than 0\r\n";
7545        assert_eq!(
7546            f.run(&[
7547                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7548            ]),
7549            count
7550        );
7551        assert_eq!(
7552            f.run(&[
7553                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7554            ]),
7555            count
7556        );
7557        assert_eq!(
7558            f.run(&[
7559                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7560            ]),
7561            "-ERR syntax error\r\n"
7562        );
7563        assert_eq!(
7564            f.run(&[
7565                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7566            ]),
7567            "-ERR syntax error\r\n"
7568        );
7569        // Every argument is read before the keys are looked at, so a bad count
7570        // beats a wrong type even when the type is wrong on the source.
7571        assert_eq!(
7572            f.run(&[
7573                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7574            ]),
7575            count
7576        );
7577        assert_eq!(
7578            f.run(&[
7579                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7580            ]),
7581            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7582        );
7583        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7584    }
7585
7586    #[test]
7587    fn lmpop_answers_from_the_first_key_that_has_anything() {
7588        let mut f = Fixture::new();
7589        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7590        // The name of the key that answered comes back with the elements,
7591        // because the client cannot work out which one it was.
7592        assert_eq!(
7593            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7594            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7595        );
7596        assert_eq!(
7597            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7598            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7599        );
7600        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7601        // A null array and not a null, even though what it stands in for is an
7602        // array holding a key name and then another array.
7603        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7604    }
7605
7606    #[test]
7607    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7608        let mut f = Fixture::new();
7609        f.run(&[b"RPUSH", b"k", b"a"]);
7610        assert_eq!(
7611            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7612            "-ERR numkeys should be greater than 0\r\n"
7613        );
7614        assert_eq!(
7615            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7616            "-ERR numkeys should be greater than 0\r\n"
7617        );
7618        assert_eq!(
7619            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7620            "-ERR count should be greater than 0\r\n"
7621        );
7622        // A key count that eats the direction is a syntax error and not a
7623        // sentence about key counts, because the direction is simply not there.
7624        assert_eq!(
7625            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7626            "-ERR syntax error\r\n"
7627        );
7628        assert_eq!(
7629            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7630            "-ERR syntax error\r\n"
7631        );
7632        assert_eq!(
7633            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7634            "-ERR syntax error\r\n"
7635        );
7636        assert_eq!(
7637            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7638            "-ERR syntax error\r\n"
7639        );
7640        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7641    }
7642
7643    #[test]
7644    fn every_list_command_says_wrongtype_and_writes_nothing() {
7645        let mut f = Fixture::new();
7646        f.run(&[b"SET", b"str", b"v"]);
7647        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7648        for cmd in [
7649            &[b"LPUSH".as_slice(), b"str", b"a"][..],
7650            &[b"RPUSH", b"str", b"a"],
7651            &[b"LPUSHX", b"str", b"a"],
7652            &[b"RPUSHX", b"str", b"a"],
7653            &[b"LPOP", b"str"],
7654            &[b"LPOP", b"str", b"2"],
7655            &[b"RPOP", b"str"],
7656            &[b"LLEN", b"str"],
7657            &[b"LRANGE", b"str", b"0", b"-1"],
7658            &[b"LINDEX", b"str", b"0"],
7659            &[b"LSET", b"str", b"0", b"a"],
7660            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7661            &[b"LREM", b"str", b"0", b"a"],
7662            &[b"LTRIM", b"str", b"0", b"-1"],
7663            &[b"LPOS", b"str", b"a"],
7664            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7665            &[b"RPOPLPUSH", b"str", b"d"],
7666            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7667            &[b"LMPOP", b"1", b"str", b"LEFT"],
7668        ] {
7669            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7670        }
7671        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7672        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7673    }
7674
7675    /// A timeout is not an integer and it is not an ordinary float either: the
7676    /// three sentences it can answer with are its own, and which one a given
7677    /// argument gets is not what reading the code would suggest.
7678    #[test]
7679    fn a_timeout_has_three_ways_of_being_wrong() {
7680        let mut f = Fixture::new();
7681        let not_float = "-ERR timeout is not a float or out of range\r\n";
7682        let range = "-ERR timeout is out of range\r\n";
7683        for (bad, want) in [
7684            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7685            (&[b"BLPOP", b"k", b"nan"], not_float),
7686            (&[b"BLPOP", b"k", b""], not_float),
7687            // Whitespace on either side, which `strtold` would take and Redis
7688            // does not.
7689            (&[b"BLPOP", b"k", b" 1"], not_float),
7690            (&[b"BLPOP", b"k", b"1 "], not_float),
7691            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7692            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7693            // These three parse, so they are not the not-a-float error, and all
7694            // three are further off than an i64 of milliseconds reaches.
7695            (&[b"BLPOP", b"k", b"1e400"], range),
7696            (&[b"BLPOP", b"k", b"inf"], range),
7697            (&[b"BLPOP", b"k", b"9999999999999999"], range),
7698            (&[b"BRPOP", b"k", b"abc"], not_float),
7699            (
7700                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7701                not_float,
7702            ),
7703            (
7704                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7705                "-ERR timeout is negative\r\n",
7706            ),
7707            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7708        ] {
7709            assert_eq!(f.run(bad), want, "for {bad:?}");
7710        }
7711    }
7712
7713    /// A timeout of exactly zero means no timeout, and there are two ways of
7714    /// writing exactly zero.
7715    #[test]
7716    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7717        let mut f = Fixture::new();
7718        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7719            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7720            assert_eq!(flow, Flow::Block, "for {timeout:?}");
7721            assert!(out.is_empty(), "for {timeout:?}");
7722        }
7723        // Positive, so it is a real deadline, and the deadline is this
7724        // millisecond. Nothing is written here either: the reply comes from the
7725        // sweep, which is the engine's and not this layer's.
7726        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7727        assert_eq!(flow, Flow::Block);
7728        assert!(out.is_empty());
7729    }
7730
7731    #[test]
7732    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7733        let mut f = Fixture::new();
7734        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7735
7736        // The one difference from LPOP: the reply names the key that answered,
7737        // which is what makes BLPOP over several keys usable.
7738        assert_eq!(
7739            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7740            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7741        );
7742        assert_eq!(
7743            f.run(&[b"BRPOP", b"L", b"0"]),
7744            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7745        );
7746        assert_eq!(
7747            f.run(&[
7748                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7749            ]),
7750            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7751        );
7752        assert_eq!(
7753            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7754            "$1\r\nd\r\n"
7755        );
7756        assert_eq!(
7757            f.run(&[b"EXISTS", b"L"]),
7758            ":0\r\n",
7759            "and the key went with it"
7760        );
7761        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7762        // Onto itself, which is how a list is rotated and is a real thing to ask
7763        // a blocking move for.
7764        f.run(&[b"RPUSH", b"D", b"x"]);
7765        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7766        assert_eq!(
7767            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7768            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7769        );
7770    }
7771
7772    #[test]
7773    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7774        let mut f = Fixture::new();
7775        f.run(&[b"RPUSH", b"k", b"a"]);
7776        for (bad, want) in [
7777            (
7778                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7779                "-ERR numkeys should be greater than 0\r\n",
7780            ),
7781            (
7782                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7783                "-ERR numkeys should be greater than 0\r\n",
7784            ),
7785            // Two keys named and one given, so the word that should have been
7786            // the direction is a key and there is no direction left.
7787            (
7788                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7789                "-ERR syntax error\r\n",
7790            ),
7791            (
7792                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7793                "-ERR syntax error\r\n",
7794            ),
7795            (
7796                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7797                "-ERR syntax error\r\n",
7798            ),
7799            (
7800                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7801                "-ERR syntax error\r\n",
7802            ),
7803            // A count that is not a number at all gets the same sentence a zero
7804            // or a negative one gets, rather than the usual one about integers.
7805            (
7806                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7807                "-ERR count should be greater than 0\r\n",
7808            ),
7809            (
7810                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7811                "-ERR count should be greater than 0\r\n",
7812            ),
7813        ] {
7814            assert_eq!(f.run(bad), want, "for {bad:?}");
7815        }
7816        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7817    }
7818
7819    #[test]
7820    fn a_blocking_move_reads_its_directions_before_its_timeout() {
7821        let mut f = Fixture::new();
7822        // Both are wrong. Redis checks the directions first, so this is the
7823        // syntax error and not a complaint about the timeout.
7824        assert_eq!(
7825            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7826            "-ERR syntax error\r\n"
7827        );
7828        assert_eq!(
7829            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7830            "-ERR syntax error\r\n"
7831        );
7832    }
7833
7834    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
7835    /// wait, which is the same relationship every other command in this file has
7836    /// with the one it wraps.
7837    #[test]
7838    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7839        let mut f = Fixture::new();
7840        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7841        assert_eq!(
7842            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7843            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7844        );
7845        assert_eq!(
7846            f.run(&[
7847                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7848            ]),
7849            bulks(&["e", "d"])
7850        );
7851        assert_eq!(
7852            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7853            bulks(&["a", "e", "d"])
7854        );
7855        // `EXACTLY` with enough there does not wait either.
7856        assert_eq!(
7857            f.run(&[
7858                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7859            ]),
7860            bulks(&["b", "c"])
7861        );
7862        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7863    }
7864
7865    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
7866    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
7867    /// whole block has arrived.
7868    #[test]
7869    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7870        let mut f = Fixture::new();
7871        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7872        // Two there and three asked for. `COUNT` takes the two.
7873        assert_eq!(
7874            f.flow(&[
7875                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7876            ]),
7877            (Flow::Continue, bulks(&["a", "b"]))
7878        );
7879
7880        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7881        // The same line with `EXACTLY` parks instead, and takes nothing on the
7882        // way past.
7883        assert_eq!(
7884            f.flow(&[
7885                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7886            ])
7887            .0,
7888            Flow::Block
7889        );
7890        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7891    }
7892
7893    #[test]
7894    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7895        let mut f = Fixture::new();
7896        let syntax = "-ERR syntax error\r\n";
7897        // All three are wrong and the directions are read first.
7898        assert_eq!(
7899            f.run(&[
7900                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7901            ]),
7902            syntax
7903        );
7904        // Directions fine, timeout and count both wrong, so the timeout wins.
7905        assert_eq!(
7906            f.run(&[
7907                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7908            ]),
7909            "-ERR timeout is not a float or out of range\r\n"
7910        );
7911        assert_eq!(
7912            f.run(&[
7913                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7914            ]),
7915            "-ERR timeout is negative\r\n"
7916        );
7917        // And with the timeout fine, the count before the ordering word.
7918        assert_eq!(
7919            f.run(&[
7920                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7921            ]),
7922            "-ERR count should be greater than 0\r\n"
7923        );
7924        assert_eq!(
7925            f.run(&[
7926                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7927            ]),
7928            syntax
7929        );
7930        // Seven and eight arguments are neither of the two forms, the same way
7931        // six and seven are for `LMOVEM`.
7932        assert_eq!(
7933            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7934            syntax
7935        );
7936        assert_eq!(
7937            f.run(&[
7938                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7939            ]),
7940            syntax
7941        );
7942    }
7943
7944    /// The four ways a blocking command sees a key of another type, and the one
7945    /// way it does not.
7946    #[test]
7947    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7948        let mut f = Fixture::new();
7949        f.run(&[b"SET", b"S", b"v"]);
7950        f.run(&[b"RPUSH", b"D", b"x"]);
7951        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7952
7953        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7954        // Every key is checked even when an earlier one would have blocked, so
7955        // an empty key in front of a string does not hide it.
7956        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7957        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7958        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7959        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7960        // The destination, which is only reached because the source has
7961        // something in it.
7962        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7963        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7964        assert_eq!(
7965            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7966            wrong
7967        );
7968        assert_eq!(
7969            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7970            wrong
7971        );
7972
7973        // And the one that does not: an empty source means the destination is
7974        // never looked at, so this waits rather than erroring, and on a real
7975        // server it times out.
7976        assert_eq!(
7977            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7978                .0,
7979            Flow::Block
7980        );
7981        // `BLMOVEM` has a second way of not being ready, and it hides the
7982        // destination just as well: the source is a list with two elements in it
7983        // and `EXACTLY` wants three, so the string never gets looked at.
7984        assert_eq!(
7985            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7986                .0,
7987            Flow::Block
7988        );
7989        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7990        assert_eq!(
7991            f.flow(&[
7992                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7993            ])
7994            .0,
7995            Flow::Block
7996        );
7997    }
7998
7999    /// The same churn the set and the string get, because a list that leaks a
8000    /// chunk per push looks exactly like one that does not until it has run for
8001    /// an afternoon.
8002    #[test]
8003    fn churning_lists_does_not_grow_the_server() {
8004        let mut f = Fixture::new();
8005        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
8006        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
8007            .into_iter()
8008            .chain(vals.iter().map(Vec::as_slice))
8009            .collect();
8010
8011        f.run(&args);
8012        f.run(&[b"DEL", b"k"]);
8013        f.server.compact_step();
8014        let after_first = f.server.memory_bytes();
8015
8016        for _ in 0..200 {
8017            f.run(&args);
8018            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
8019            f.server.compact_step();
8020        }
8021        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8022        assert!(
8023            f.server.memory_bytes() <= after_first * 2,
8024            "held {} after two hundred passes against {after_first} after one",
8025            f.server.memory_bytes()
8026        );
8027    }
8028
8029    // ------------------------------------------------------------ sorted set
8030
8031    #[test]
8032    fn a_sorted_set_takes_scores_and_gives_them_back() {
8033        let mut f = Fixture::new();
8034        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
8035        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
8036        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
8037        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
8038        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
8039        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
8040        assert_eq!(
8041            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
8042            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
8043        );
8044        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
8045        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
8046        // The key goes when the last member does.
8047        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
8048        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8049    }
8050
8051    #[test]
8052    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
8053        let mut f = Fixture::new();
8054        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
8055        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
8056        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
8057        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
8058
8059        f.out = Out::new(Proto::Resp3);
8060        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
8061        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
8062        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
8063        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
8064    }
8065
8066    #[test]
8067    fn the_zadd_options_gate_what_gets_written() {
8068        let mut f = Fixture::new();
8069        f.run(&[b"ZADD", b"z", b"5", b"a"]);
8070        // NX leaves a member that is there alone, XX will not create one.
8071        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
8072        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
8073        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
8074        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
8075        // GT and LT only move a score one way.
8076        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
8077        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
8078        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
8079        // CH counts a moved score and plain ZADD does not.
8080        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
8081        assert_eq!(
8082            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
8083            ":2\r\n"
8084        );
8085    }
8086
8087    #[test]
8088    fn zadd_incr_answers_a_score_or_nothing_at_all() {
8089        let mut f = Fixture::new();
8090        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
8091        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
8092        // A gate that refuses is the string nil, because the reply it stands in
8093        // for is a score.
8094        assert_eq!(
8095            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
8096            "$-1\r\n"
8097        );
8098        assert_eq!(
8099            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
8100            "$-1\r\n"
8101        );
8102        assert_eq!(
8103            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
8104            "$-1\r\n"
8105        );
8106        assert_eq!(
8107            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
8108            "$1\r\n8\r\n"
8109        );
8110        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
8111        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
8112    }
8113
8114    #[test]
8115    fn the_two_infinities_will_not_be_added_together() {
8116        let mut f = Fixture::new();
8117        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
8118        let nan = "-ERR resulting score is not a number (NaN)\r\n";
8119        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
8120        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
8121        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
8122        // And a key made for an increment that then fails does not stay behind.
8123        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
8124    }
8125
8126    #[test]
8127    fn zadd_says_its_mistakes_the_way_redis_says_them() {
8128        let mut f = Fixture::new();
8129        // The pairs are counted before the options are looked at, so this is a
8130        // syntax error about having none and not a complaint about NX and XX.
8131        assert_eq!(
8132            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
8133            "-ERR syntax error\r\n"
8134        );
8135        assert_eq!(
8136            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
8137            "-ERR XX and NX options at the same time are not compatible\r\n"
8138        );
8139        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
8140        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
8141        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
8142        assert_eq!(
8143            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
8144            "-ERR INCR option supports a single increment-element pair\r\n"
8145        );
8146        // An odd number of arguments after the options.
8147        assert_eq!(
8148            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
8149            "-ERR syntax error\r\n"
8150        );
8151        // Every score is read before the first is stored.
8152        assert_eq!(
8153            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
8154            "-ERR value is not a valid float\r\n"
8155        );
8156        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8157    }
8158
8159    #[test]
8160    fn a_rank_says_where_a_member_sits_from_either_end() {
8161        let mut f = Fixture::new();
8162        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8163        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
8164        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
8165        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
8166        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
8167        // WITHSCORE changes both shapes: the answer and the nothing.
8168        assert_eq!(
8169            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
8170            "*2\r\n:1\r\n$1\r\n2\r\n"
8171        );
8172        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
8173        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
8174        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
8175        // A bad option is a syntax error and one argument too many is an arity
8176        // error, which is Redis's split.
8177        assert_eq!(
8178            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
8179            "-ERR syntax error\r\n"
8180        );
8181        assert_eq!(
8182            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
8183            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
8184        );
8185    }
8186
8187    #[test]
8188    fn the_two_counts_read_their_two_kinds_of_bound() {
8189        let mut f = Fixture::new();
8190        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8191        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
8192        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
8193        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
8194        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
8195        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
8196        assert_eq!(
8197            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
8198            "-ERR min or max is not a float\r\n"
8199        );
8200
8201        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
8202        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
8203        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
8204        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
8205        // A bare member is not a bound, because a member can start with any
8206        // byte and there would be no way to say the bracket if it were optional.
8207        assert_eq!(
8208            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
8209            "-ERR min or max not valid string range item\r\n"
8210        );
8211    }
8212
8213    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
8214    ///
8215    /// Every byte in here was read off a real 8.10.1 rather than worked out,
8216    /// because the interesting part of this command is not what it selects, it
8217    /// is which of the two ends the client is expected to name first.
8218    #[test]
8219    fn one_range_command_selects_by_rank_or_score_or_name() {
8220        let mut f = Fixture::new();
8221        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8222        assert_eq!(
8223            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8224            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8225        );
8226        assert_eq!(
8227            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
8228            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8229        );
8230        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
8231        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
8232        // REV over ranks reverses the walk and leaves the two arguments alone,
8233        // because a rank counts from the end the walk starts at.
8234        assert_eq!(
8235            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
8236            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8237        );
8238        assert_eq!(
8239            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
8240            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8241        );
8242        // And REV over scores does swap them, since a bound does not count from
8243        // anywhere. This is the one line of the parse that tells the two apart.
8244        assert_eq!(
8245            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
8246            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8247        );
8248        assert_eq!(
8249            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
8250            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8251        );
8252        assert_eq!(
8253            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
8254            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8255        );
8256    }
8257
8258    /// The older spellings, which are the same six windows with the mode in the
8259    /// name and the high end named first on the three that go backwards.
8260    #[test]
8261    fn the_older_range_spellings_name_their_high_end_first() {
8262        let mut f = Fixture::new();
8263        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8264        assert_eq!(
8265            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
8266            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8267        );
8268        assert_eq!(
8269            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
8270            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8271        );
8272        assert_eq!(
8273            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
8274            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8275        );
8276        assert_eq!(
8277            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
8278            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8279        );
8280        // The two arguments the wrong way round is an empty answer and not an
8281        // error, which is what the swap being in the parse rather than in the
8282        // window buys.
8283        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
8284        assert_eq!(
8285            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
8286            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8287        );
8288        assert_eq!(
8289            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
8290            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
8291        );
8292        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
8293        // way of spelling the mode, they are a syntax error.
8294        for cmd in [
8295            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
8296            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
8297            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
8298        ] {
8299            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
8300        }
8301    }
8302
8303    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
8304    /// only some of them accept.
8305    #[test]
8306    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
8307        let mut f = Fixture::new();
8308        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8309        assert_eq!(
8310            f.run(&[
8311                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
8312            ]),
8313            "*1\r\n$1\r\nb\r\n"
8314        );
8315        // A negative offset skips past everything, a negative count is no bound.
8316        assert_eq!(
8317            f.run(&[
8318                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
8319            ]),
8320            "*0\r\n"
8321        );
8322        assert_eq!(
8323            f.run(&[
8324                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
8325            ]),
8326            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8327        );
8328        // The two options in either order, which falls out of the parse loop.
8329        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";
8330        assert_eq!(
8331            f.run(&[
8332                b"ZRANGEBYSCORE",
8333                b"z",
8334                b"1",
8335                b"3",
8336                b"WITHSCORES",
8337                b"LIMIT",
8338                b"0",
8339                b"2"
8340            ]),
8341            both
8342        );
8343        assert_eq!(
8344            f.run(&[
8345                b"ZRANGEBYSCORE",
8346                b"z",
8347                b"1",
8348                b"3",
8349                b"LIMIT",
8350                b"0",
8351                b"2",
8352                b"WITHSCORES"
8353            ]),
8354            both
8355        );
8356        // LIMIT on a range by rank is refused after the whole option list has
8357        // been read, so this complains about LIMIT and not about WITHSCORES.
8358        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
8359        assert_eq!(
8360            f.run(&[
8361                b"ZREVRANGE",
8362                b"z",
8363                b"0",
8364                b"-1",
8365                b"WITHSCORES",
8366                b"LIMIT",
8367                b"0",
8368                b"1"
8369            ]),
8370            needs_by
8371        );
8372        assert_eq!(
8373            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
8374            needs_by
8375        );
8376        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
8377        assert_eq!(
8378            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
8379            not_bylex
8380        );
8381        assert_eq!(
8382            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
8383            not_bylex
8384        );
8385        // Two modes at once, an option nobody knows, a LIMIT missing its count,
8386        // and the three number errors, which are three different sentences.
8387        for cmd in [
8388            &[
8389                b"ZRANGE".as_slice(),
8390                b"z",
8391                b"0",
8392                b"-1",
8393                b"BYSCORE",
8394                b"BYLEX",
8395            ][..],
8396            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
8397            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
8398        ] {
8399            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8400        }
8401        assert_eq!(
8402            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8403            "-ERR min or max is not a float\r\n"
8404        );
8405        assert_eq!(
8406            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8407            "-ERR min or max not valid string range item\r\n"
8408        );
8409        assert_eq!(
8410            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8411            "-ERR value is not an integer or out of range\r\n"
8412        );
8413    }
8414
8415    /// `WITHSCORES` is the one place in this group where the two protocols
8416    /// disagree about the shape of the reply and not just the type of a value.
8417    #[test]
8418    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8419        let mut f = Fixture::new();
8420        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8421        assert_eq!(
8422            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8423            "*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"
8424        );
8425        f.out = Out::new(Proto::Resp3);
8426        assert_eq!(
8427            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8428            "*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"
8429        );
8430        assert_eq!(
8431            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8432            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8433        );
8434    }
8435
8436    /// The store form, which is the same parse with the destination in front.
8437    #[test]
8438    fn a_range_store_writes_the_window_into_another_key() {
8439        let mut f = Fixture::new();
8440        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8441        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8442        // A window that selects nothing deletes the destination rather than
8443        // leaving an empty sorted set, because an empty one does not exist.
8444        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8445        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8446        assert_eq!(
8447            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8448            ":2\r\n"
8449        );
8450        assert_eq!(
8451            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8452            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8453        );
8454        // The destination is allowed to be the source, because the result is
8455        // built whole before anything is written over.
8456        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8457        assert_eq!(
8458            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8459            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8460        );
8461        // It takes every option ZRANGE takes except WITHSCORES, which is a
8462        // plain syntax error here and not the sentence about BYLEX.
8463        assert_eq!(
8464            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8465            "-ERR syntax error\r\n"
8466        );
8467    }
8468
8469    /// The three removals, which are the read side's window with the walk
8470    /// turned into a removal and no options at all.
8471    #[test]
8472    fn the_three_removals_share_their_window_with_the_reads() {
8473        let mut f = Fixture::new();
8474        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8475        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8476        assert_eq!(
8477            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8478            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8479        );
8480        assert_eq!(
8481            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8482            ":1\r\n"
8483        );
8484        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8485        // The last member going takes the key with it.
8486        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8487        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8488        assert_eq!(
8489            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8490            ":0\r\n"
8491        );
8492        assert_eq!(
8493            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8494            "-ERR value is not an integer or out of range\r\n"
8495        );
8496    }
8497
8498    /// The algebra, which is one gather and three names for it.
8499    #[test]
8500    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8501        let mut f = Fixture::new();
8502        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8503        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8504        assert_eq!(
8505            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8506            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8507        );
8508        // The scores are added where a member is in both, and the answer comes
8509        // out in the order those combined scores put it in.
8510        assert_eq!(
8511            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8512            "*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"
8513        );
8514        assert_eq!(
8515            f.run(&[
8516                b"ZUNION",
8517                b"2",
8518                b"z",
8519                b"y",
8520                b"WEIGHTS",
8521                b"2",
8522                b"3",
8523                b"WITHSCORES"
8524            ]),
8525            "*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"
8526        );
8527        assert_eq!(
8528            f.run(&[
8529                b"ZUNION",
8530                b"2",
8531                b"z",
8532                b"y",
8533                b"AGGREGATE",
8534                b"MIN",
8535                b"WITHSCORES"
8536            ]),
8537            "*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"
8538        );
8539        assert_eq!(
8540            f.run(&[
8541                b"ZUNION",
8542                b"2",
8543                b"z",
8544                b"y",
8545                b"AGGREGATE",
8546                b"MAX",
8547                b"WITHSCORES"
8548            ]),
8549            "*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"
8550        );
8551        assert_eq!(
8552            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8553            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8554        );
8555        assert_eq!(
8556            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8557            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8558        );
8559        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8560        // A plain set is an input, and it behaves as a sorted set in which
8561        // every member scores one.
8562        f.run(&[b"SADD", b"p", b"a", b"d"]);
8563        assert_eq!(
8564            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8565            "*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"
8566        );
8567        // A difference never combines two scores, so it has nothing for either
8568        // of the two options to do and refuses both.
8569        for cmd in [
8570            &[
8571                b"ZDIFF".as_slice(),
8572                b"2",
8573                b"z",
8574                b"y",
8575                b"WEIGHTS",
8576                b"1",
8577                b"1",
8578            ][..],
8579            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8580        ] {
8581            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8582        }
8583    }
8584
8585    /// The count of keys, which is what lets a key be named `WEIGHTS`.
8586    #[test]
8587    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8588        let mut f = Fixture::new();
8589        f.run(&[b"ZADD", b"z", b"1", b"a"]);
8590        f.run(&[b"ZADD", b"y", b"2", b"b"]);
8591        // Redis names the command in this one, so each spelling says its own.
8592        assert_eq!(
8593            f.run(&[b"ZUNION", b"0", b"z"]),
8594            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8595        );
8596        assert_eq!(
8597            f.run(&[b"ZUNION", b"-1", b"z"]),
8598            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8599        );
8600        assert_eq!(
8601            f.run(&[b"ZINTERCARD", b"0", b"z"]),
8602            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8603        );
8604        // A count bigger than the line is a plain syntax error, which reads
8605        // oddly and is what Redis says.
8606        assert_eq!(
8607            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8608            "-ERR syntax error\r\n"
8609        );
8610        assert_eq!(
8611            f.run(&[b"ZUNION", b"x", b"z"]),
8612            "-ERR value is not an integer or out of range\r\n"
8613        );
8614        // A WEIGHTS list that is not one per key is a syntax error, and a
8615        // weight that is not a number gets a sentence of its own.
8616        assert_eq!(
8617            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8618            "-ERR syntax error\r\n"
8619        );
8620        assert_eq!(
8621            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8622            "-ERR weight value is not a float\r\n"
8623        );
8624        assert_eq!(
8625            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8626            "-ERR syntax error\r\n"
8627        );
8628    }
8629
8630    /// The three store forms, which answer a count and take no WITHSCORES.
8631    #[test]
8632    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8633        let mut f = Fixture::new();
8634        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8635        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8636        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8637        assert_eq!(
8638            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8639            "*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"
8640        );
8641        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8642        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8643        // An empty result deletes the destination rather than leaving an empty
8644        // sorted set, because an empty one does not exist.
8645        assert_eq!(
8646            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8647            ":0\r\n"
8648        );
8649        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8650        // The destination is allowed to name its own source.
8651        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8652        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8653        for cmd in [
8654            &[
8655                b"ZUNIONSTORE".as_slice(),
8656                b"d",
8657                b"2",
8658                b"z",
8659                b"y",
8660                b"WITHSCORES",
8661            ][..],
8662            &[
8663                b"ZDIFFSTORE",
8664                b"d",
8665                b"2",
8666                b"z",
8667                b"y",
8668                b"WEIGHTS",
8669                b"1",
8670                b"1",
8671            ],
8672        ] {
8673            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8674        }
8675    }
8676
8677    /// `ZINTERCARD`, which counts without building anything.
8678    #[test]
8679    fn intercard_counts_and_stops_at_its_limit() {
8680        let mut f = Fixture::new();
8681        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8682        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8683        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8684        // A limit of zero is no limit, which is Redis's reading of it.
8685        assert_eq!(
8686            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8687            ":2\r\n"
8688        );
8689        assert_eq!(
8690            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8691            ":1\r\n"
8692        );
8693        // A negative limit and a limit that is not a number at all get the same
8694        // sentence, which looks like a mistake in Redis and is copied as one.
8695        let bad = "-ERR LIMIT can't be negative\r\n";
8696        assert_eq!(
8697            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8698            bad
8699        );
8700        assert_eq!(
8701            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8702            bad
8703        );
8704        for cmd in [
8705            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8706            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8707            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8708        ] {
8709            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8710        }
8711    }
8712
8713    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
8714    #[test]
8715    fn a_draw_answers_one_member_or_an_array_of_them() {
8716        let mut f = Fixture::new();
8717        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8718        // No count is one member or a nil, a count is an array that may be
8719        // empty, and those are two reply types the client has to tell apart.
8720        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8721        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8722        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8723        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8724        // A positive count draws without replacement, so a count over the size
8725        // answers the whole set and never a member twice.
8726        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8727        assert!(all.starts_with("*3\r\n"), "{all}");
8728        for m in ["a", "b", "c"] {
8729            assert!(all.contains(m), "{all}");
8730        }
8731        // A negative one draws with replacement and answers exactly as many as
8732        // it was asked for, whatever the size of the set.
8733        assert!(
8734            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8735            "five draws with replacement"
8736        );
8737        assert!(
8738            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8739                .starts_with("*4\r\n"),
8740            "two pairs, flat on RESP2"
8741        );
8742        f.out = Out::new(Proto::Resp3);
8743        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8744        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8745        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8746        f.out = Out::new(Proto::Resp2);
8747        assert_eq!(
8748            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8749            "-ERR syntax error\r\n"
8750        );
8751        assert_eq!(
8752            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8753            "-ERR value is not an integer or out of range\r\n"
8754        );
8755    }
8756
8757    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
8758    #[test]
8759    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8760        let mut f = Fixture::new();
8761        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8762        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";
8763        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8764        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8765        assert_eq!(
8766            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8767            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8768        );
8769        assert_eq!(
8770            f.run(&[b"ZSCAN", b"nokey", b"0"]),
8771            "*2\r\n$1\r\n0\r\n*0\r\n"
8772        );
8773        // A score stays a bulk string on RESP3, which is the one place the two
8774        // protocols agree about a score and everywhere else they do not.
8775        f.out = Out::new(Proto::Resp3);
8776        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8777        f.out = Out::new(Proto::Resp2);
8778        assert_eq!(
8779            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8780            "-ERR NOVALUES option can only be used in HSCAN\r\n"
8781        );
8782        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8783        assert_eq!(
8784            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8785            "-ERR syntax error\r\n"
8786        );
8787    }
8788
8789    /// The count is what decides the shape, and its value is not.
8790    #[test]
8791    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8792        let mut f = Fixture::new();
8793        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8794        // No count, so one flat pair, and the score is a bulk string on RESP2.
8795        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8796        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8797        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8798        // A count, so pairs, and on RESP2 they are flattened into one run.
8799        assert_eq!(
8800            f.run(&[b"ZPOPMIN", b"z", b"2"]),
8801            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8802        );
8803        // An empty array rather than a null, which is where a sorted set pop and
8804        // a list pop part company, and the same answer a count of zero gives.
8805        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8806        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8807        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8808        // The last member takes the key with it.
8809        assert_eq!(
8810            f.run(&[b"ZPOPMIN", b"z", b"9"]),
8811            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8812        );
8813        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8814
8815        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8816        f.out = Out::new(Proto::Resp3);
8817        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8818        assert_eq!(
8819            f.run(&[b"ZPOPMIN", b"z", b"1"]),
8820            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8821        );
8822        f.out = Out::new(Proto::Resp2);
8823        // Both of these are the range error rather than the usual sentence about
8824        // integers, which is the odd answer and so the one worth copying.
8825        let bad = "-ERR value is out of range, must be positive\r\n";
8826        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8827        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8828        assert_eq!(
8829            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8830            "-ERR syntax error\r\n"
8831        );
8832    }
8833
8834    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
8835    #[test]
8836    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8837        let mut f = Fixture::new();
8838        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8839        assert_eq!(
8840            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8841            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8842        );
8843        // Nested on RESP2 as well, because the key name is already in front of
8844        // the pairs and there is nothing left to flatten into.
8845        assert_eq!(
8846            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8847            "*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"
8848        );
8849        // A null array and not a null, the same as LMPOP.
8850        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8851        f.out = Out::new(Proto::Resp3);
8852        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8853        f.out = Out::new(Proto::Resp2);
8854        let numkeys = "-ERR numkeys should be greater than 0\r\n";
8855        for bad in [
8856            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8857            &[b"ZMPOP", b"-1", b"z", b"MIN"],
8858            &[b"ZMPOP", b"x", b"z", b"MIN"],
8859        ] {
8860            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8861        }
8862        let count = "-ERR count should be greater than 0\r\n";
8863        for bad in [
8864            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8865            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8866            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8867        ] {
8868            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8869        }
8870        let syntax = "-ERR syntax error\r\n";
8871        for bad in [
8872            // Two keys named and one given, so the word that should have been
8873            // the direction is a key and there is no direction left.
8874            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8875            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8876            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8877            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8878        ] {
8879            assert_eq!(f.run(bad), syntax, "{bad:?}");
8880        }
8881    }
8882
8883    /// The three that wait, when there is something there and they do not have
8884    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
8885    #[test]
8886    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8887        let mut f = Fixture::new();
8888        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8889        assert_eq!(
8890            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8891            (
8892                Flow::Continue,
8893                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8894            )
8895        );
8896        assert_eq!(
8897            f.run(&[b"BZPOPMAX", b"z", b"0"]),
8898            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8899        );
8900        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8901        assert_eq!(
8902            f.run(&[
8903                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8904            ]),
8905            "*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"
8906        );
8907        f.out = Out::new(Proto::Resp3);
8908        assert_eq!(
8909            f.run(&[b"BZPOPMIN", b"z", b"0"]),
8910            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8911        );
8912        f.out = Out::new(Proto::Resp2);
8913        // Nothing to take, so the client is parked and nothing was written.
8914        assert_eq!(
8915            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8916            (Flow::Block, String::new())
8917        );
8918        assert_eq!(
8919            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8920            (Flow::Block, String::new())
8921        );
8922        // The timeout is read before the key count, so this complains about the
8923        // timeout and not about the count.
8924        assert_eq!(
8925            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8926            "-ERR timeout is not a float or out of range\r\n"
8927        );
8928        assert_eq!(
8929            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8930            "-ERR numkeys should be greater than 0\r\n"
8931        );
8932        assert_eq!(
8933            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8934            "-ERR timeout is negative\r\n"
8935        );
8936    }
8937
8938    /// A parked sorted set client is served by whatever puts a member under one
8939    /// of its keys, and is not served by something of another type landing
8940    /// there.
8941    #[test]
8942    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8943        let mut f = Fixture::new();
8944        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8945        assert_eq!(f.server.parked(), 1);
8946        // A string under the key is not what it asked for, so it stays parked
8947        // rather than being handed a WRONGTYPE on a command that was accepted.
8948        f.run(&[b"SET", b"z", b"v"]);
8949        let mut out = Out::new(Proto::Resp2);
8950        assert!(!f.server.serve_waiter(0, 0, &mut out));
8951        assert!(out.as_slice().is_empty());
8952        f.run(&[b"DEL", b"z"]);
8953        f.run(&[b"ZADD", b"z", b"5", b"m"]);
8954        assert!(f.server.serve_waiter(0, 0, &mut out));
8955        assert_eq!(
8956            core::str::from_utf8(out.as_slice()).expect("ascii"),
8957            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8958        );
8959        // And the member is gone, which is what makes a queue of workers on a
8960        // sorted set work at all.
8961        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8962    }
8963
8964    #[test]
8965    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8966        let mut f = Fixture::new();
8967        f.run(&[b"SET", b"s", b"v"]);
8968        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8969        for cmd in [
8970            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8971            &[b"ZINCRBY", b"s", b"1", b"a"],
8972            &[b"ZCARD", b"s"],
8973            &[b"ZSCORE", b"s", b"a"],
8974            &[b"ZMSCORE", b"s", b"a"],
8975            &[b"ZREM", b"s", b"a"],
8976            &[b"ZRANK", b"s", b"a"],
8977            &[b"ZREVRANK", b"s", b"a"],
8978            &[b"ZCOUNT", b"s", b"1", b"2"],
8979            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8980            &[b"ZRANGE", b"s", b"0", b"-1"],
8981            &[b"ZREVRANGE", b"s", b"0", b"-1"],
8982            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8983            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8984            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8985            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8986            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8987            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8988            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8989            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8990            &[b"ZUNION", b"1", b"s"],
8991            &[b"ZINTER", b"1", b"s"],
8992            &[b"ZDIFF", b"1", b"s"],
8993            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8994            &[b"ZINTERSTORE", b"d", b"1", b"s"],
8995            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8996            &[b"ZINTERCARD", b"1", b"s"],
8997            &[b"ZRANDMEMBER", b"s"],
8998            &[b"ZSCAN", b"s", b"0"],
8999            &[b"ZPOPMIN", b"s"],
9000            &[b"ZPOPMAX", b"s", b"2"],
9001            &[b"ZMPOP", b"1", b"s", b"MIN"],
9002            &[b"BZPOPMIN", b"s", b"0"],
9003            &[b"BZPOPMAX", b"s", b"0"],
9004            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
9005        ] {
9006            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
9007        }
9008        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
9009    }
9010
9011    /// The same churn the set, the string and the list get, because a sorted
9012    /// set that leaks a tree node per add looks exactly like one that does not
9013    /// until it has run for an afternoon.
9014    #[test]
9015    fn churning_sorted_sets_does_not_grow_the_server() {
9016        let mut f = Fixture::new();
9017        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
9018        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
9019        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
9020        for i in 0..200 {
9021            args.push(&scores[i]);
9022            args.push(&members[i]);
9023        }
9024
9025        f.run(&args);
9026        f.run(&[b"DEL", b"z"]);
9027        f.server.compact_step();
9028        let after_first = f.server.memory_bytes();
9029
9030        for _ in 0..200 {
9031            f.run(&args);
9032            f.run(&[b"DEL", b"z"]);
9033            f.server.compact_step();
9034        }
9035        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9036        assert!(
9037            f.server.memory_bytes() <= after_first * 2,
9038            "held {} after two hundred passes against {after_first} after one",
9039            f.server.memory_bytes()
9040        );
9041    }
9042
9043    // ------------------------------------------------------------------- geo
9044
9045    /// The three places every Redis geo example uses, and one more.
9046    ///
9047    /// Every reply this section asserts on came off a running 8.10.1 with these
9048    /// three loaded, byte for byte, including the number of digits in a
9049    /// coordinate and the four places on a distance.
9050    fn sicily(f: &mut Fixture) {
9051        f.run(&[
9052            b"GEOADD",
9053            b"Sicily",
9054            b"13.361389",
9055            b"38.115556",
9056            b"Palermo",
9057            b"15.087269",
9058            b"37.502669",
9059            b"Catania",
9060        ]);
9061        f.run(&[
9062            b"GEOADD",
9063            b"Sicily",
9064            b"13.583333",
9065            b"37.316667",
9066            b"Agrigento",
9067        ]);
9068    }
9069
9070    #[test]
9071    fn places_go_in_as_scores_and_come_back_as_positions() {
9072        let mut f = Fixture::new();
9073        assert_eq!(
9074            f.run(&[
9075                b"GEOADD",
9076                b"Sicily",
9077                b"13.361389",
9078                b"38.115556",
9079                b"Palermo",
9080                b"15.087269",
9081                b"37.502669",
9082                b"Catania"
9083            ]),
9084            ":2\r\n"
9085        );
9086        // A geo key is a sorted set and says so, which is not an implementation
9087        // detail either: a client removes a place with ZREM and counts them
9088        // with ZCARD, and the score is the number a real server stores.
9089        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
9090        assert_eq!(
9091            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
9092            "$16\r\n3479099956230698\r\n"
9093        );
9094        assert_eq!(
9095            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
9096            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
9097        );
9098        assert_eq!(
9099            f.run(&[
9100                b"GEOHASH",
9101                b"Sicily",
9102                b"Palermo",
9103                b"Catania",
9104                b"NonExisting"
9105            ]),
9106            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
9107        );
9108        // A key that is not there is an empty one, and the two nulls are not
9109        // the same null: GEOPOS answers the array one and GEOHASH the string
9110        // one, which a RESP2 client can tell apart.
9111        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
9112        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
9113    }
9114
9115    #[test]
9116    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
9117        let mut f = Fixture::new();
9118        sicily(&mut f);
9119        assert_eq!(
9120            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
9121            "$11\r\n166274.1516\r\n"
9122        );
9123        assert_eq!(
9124            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
9125            "$8\r\n166.2742\r\n"
9126        );
9127        assert_eq!(
9128            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
9129            "$8\r\n103.3182\r\n"
9130        );
9131        // A member that is not there and a key that is not there are the same
9132        // nil, and the unit is read before the key is looked up, so a bad unit
9133        // on a missing key is still an error.
9134        assert_eq!(
9135            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
9136            "$-1\r\n"
9137        );
9138        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
9139        assert_eq!(
9140            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
9141            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
9142        );
9143        assert_eq!(
9144            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
9145            "-ERR syntax error\r\n"
9146        );
9147    }
9148
9149    #[test]
9150    fn a_search_finds_what_is_inside_it_nearest_first() {
9151        let mut f = Fixture::new();
9152        sicily(&mut f);
9153        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
9154        assert_eq!(
9155            f.run(&[
9156                b"GEOSEARCH",
9157                b"Sicily",
9158                b"FROMLONLAT",
9159                b"15",
9160                b"37",
9161                b"BYRADIUS",
9162                b"200",
9163                b"km",
9164                b"ASC"
9165            ]),
9166            all
9167        );
9168        // The older spelling of the same search, which is the same nine boxes
9169        // and the same order.
9170        assert_eq!(
9171            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
9172            all
9173        );
9174        assert_eq!(
9175            f.run(&[
9176                b"GEORADIUS_RO",
9177                b"Sicily",
9178                b"15",
9179                b"37",
9180                b"200",
9181                b"km",
9182                b"ASC"
9183            ]),
9184            all
9185        );
9186        // A count with no ordering means the nearest ones, so DESC has to be
9187        // asked for to get the far end.
9188        assert_eq!(
9189            f.run(&[
9190                b"GEORADIUS",
9191                b"Sicily",
9192                b"15",
9193                b"37",
9194                b"200",
9195                b"km",
9196                b"DESC",
9197                b"COUNT",
9198                b"1"
9199            ]),
9200            "*1\r\n$7\r\nPalermo\r\n"
9201        );
9202        assert_eq!(
9203            f.run(&[
9204                b"GEORADIUS",
9205                b"Sicily",
9206                b"15",
9207                b"37",
9208                b"200",
9209                b"km",
9210                b"COUNT",
9211                b"1"
9212            ]),
9213            "*1\r\n$7\r\nCatania\r\n"
9214        );
9215        // Nothing inside a kilometre of that point, and nothing in a key that
9216        // is not there, and both are the empty array rather than an error.
9217        let empty = "*0\r\n";
9218        assert_eq!(
9219            f.run(&[
9220                b"GEOSEARCH",
9221                b"Sicily",
9222                b"FROMLONLAT",
9223                b"15",
9224                b"37",
9225                b"BYRADIUS",
9226                b"1",
9227                b"km"
9228            ]),
9229            empty
9230        );
9231        assert_eq!(
9232            f.run(&[
9233                b"GEOSEARCH",
9234                b"nokey",
9235                b"FROMLONLAT",
9236                b"15",
9237                b"37",
9238                b"BYRADIUS",
9239                b"1",
9240                b"km"
9241            ]),
9242            empty
9243        );
9244        assert_eq!(
9245            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
9246            empty
9247        );
9248    }
9249
9250    #[test]
9251    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
9252        let mut f = Fixture::new();
9253        sicily(&mut f);
9254        assert_eq!(
9255            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
9256            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9257        );
9258        // The member itself is nothing away from itself, which is where the
9259        // fixed point writer's zero shows up on the wire.
9260        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";
9261        assert_eq!(
9262            f.run(&[
9263                b"GEORADIUSBYMEMBER_RO",
9264                b"Sicily",
9265                b"Agrigento",
9266                b"100",
9267                b"km",
9268                b"WITHDIST"
9269            ]),
9270            with_dist
9271        );
9272        assert_eq!(
9273            f.run(&[
9274                b"GEOSEARCH",
9275                b"Sicily",
9276                b"FROMMEMBER",
9277                b"Agrigento",
9278                b"BYRADIUS",
9279                b"100",
9280                b"km",
9281                b"ASC",
9282                b"WITHDIST"
9283            ]),
9284            with_dist
9285        );
9286        assert_eq!(
9287            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
9288            "-ERR could not decode requested zset member\r\n"
9289        );
9290    }
9291
9292    #[test]
9293    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
9294        let mut f = Fixture::new();
9295        sicily(&mut f);
9296        // Three options asked for, so each result is a four element array of
9297        // the member, the distance, the hash and a pair. The order of the three
9298        // is Redis's and not the order they were written in the command.
9299        assert_eq!(
9300            f.run(&[
9301                b"GEOSEARCH",
9302                b"Sicily",
9303                b"FROMLONLAT",
9304                b"15",
9305                b"37",
9306                b"BYBOX",
9307                b"400",
9308                b"400",
9309                b"km",
9310                b"ASC",
9311                b"WITHCOORD",
9312                b"WITHDIST",
9313                b"WITHHASH"
9314            ]),
9315            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
9316             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
9317             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
9318             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
9319             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
9320             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
9321        );
9322    }
9323
9324    #[test]
9325    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
9326        let mut f = Fixture::new();
9327        sicily(&mut f);
9328        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
9329                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
9330                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
9331        assert_eq!(
9332            f.run(&[
9333                b"GEOSEARCHSTORE",
9334                b"dst",
9335                b"Sicily",
9336                b"FROMLONLAT",
9337                b"15",
9338                b"37",
9339                b"BYRADIUS",
9340                b"200",
9341                b"km",
9342                b"ASC"
9343            ]),
9344            ":3\r\n"
9345        );
9346        assert_eq!(
9347            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
9348            hashes
9349        );
9350        // The same again through the older spelling, which stores the same
9351        // scores, so a key written by either is a geo key.
9352        assert_eq!(
9353            f.run(&[
9354                b"GEORADIUS",
9355                b"Sicily",
9356                b"15",
9357                b"37",
9358                b"200",
9359                b"km",
9360                b"STORE",
9361                b"dst3"
9362            ]),
9363            ":3\r\n"
9364        );
9365        assert_eq!(
9366            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
9367            hashes
9368        );
9369        // STOREDIST stores the distance in the search unit instead, and those
9370        // are full doubles rather than the four places WITHDIST writes. The
9371        // numbers on the right are what 8.10.1 stored for this search, and they
9372        // are compared with a tolerance rather than byte for byte because the
9373        // last bit of a haversine is the platform's sin, cos and asin: this
9374        // machine and that one disagree in the sixteenth digit, and so do two
9375        // Redis builds. Everything a client actually reads back is four places
9376        // and is asserted exactly above.
9377        assert_eq!(
9378            f.run(&[
9379                b"GEOSEARCHSTORE",
9380                b"dst2",
9381                b"Sicily",
9382                b"FROMLONLAT",
9383                b"15",
9384                b"37",
9385                b"BYRADIUS",
9386                b"200",
9387                b"km",
9388                b"ASC",
9389                b"STOREDIST"
9390            ]),
9391            ":3\r\n"
9392        );
9393        for (member, want) in [
9394            ("Catania", 56.441_257_870_158_19),
9395            ("Agrigento", 130.423_487_067_147_14),
9396            ("Palermo", 190.442_429_847_757_92),
9397        ] {
9398            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
9399            let got: f64 = reply
9400                .trim_start_matches(|c: char| c != '\n')
9401                .trim()
9402                .parse()
9403                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9404            assert!(
9405                (got - want).abs() < 1e-9,
9406                "{member} scored {got} not {want}"
9407            );
9408        }
9409        // The order they went in is the order the scores put them in, which is
9410        // the point of storing the distance rather than the hash.
9411        assert_eq!(
9412            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9413            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9414        );
9415        // A search that finds nothing takes the destination with it rather than
9416        // leaving what was there, and a source key that is not there is a
9417        // search that finds nothing.
9418        assert_eq!(
9419            f.run(&[
9420                b"GEOSEARCHSTORE",
9421                b"dst",
9422                b"nokey",
9423                b"FROMLONLAT",
9424                b"15",
9425                b"37",
9426                b"BYRADIUS",
9427                b"200",
9428                b"km"
9429            ]),
9430            ":0\r\n"
9431        );
9432        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9433    }
9434
9435    #[test]
9436    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9437        let mut f = Fixture::new();
9438        sicily(&mut f);
9439        // XX on a member that is already where it is changes nothing, and NX on
9440        // one that is there refuses to move it.
9441        assert_eq!(
9442            f.run(&[
9443                b"GEOADD",
9444                b"Sicily",
9445                b"XX",
9446                b"CH",
9447                b"13.361389",
9448                b"38.115556",
9449                b"Palermo"
9450            ]),
9451            ":0\r\n"
9452        );
9453        assert_eq!(
9454            f.run(&[
9455                b"GEOADD",
9456                b"Sicily",
9457                b"NX",
9458                b"13.361389",
9459                b"38.9",
9460                b"Palermo"
9461            ]),
9462            ":0\r\n"
9463        );
9464        assert_eq!(
9465            f.run(&[
9466                b"GEOADD",
9467                b"Sicily",
9468                b"CH",
9469                b"13.361389",
9470                b"38.9",
9471                b"Palermo"
9472            ]),
9473            ":1\r\n"
9474        );
9475        // Out of range, and nothing is stored: the whole call is refused rather
9476        // than the good pairs going in and the bad one stopping it.
9477        assert_eq!(
9478            f.run(&[
9479                b"GEOADD",
9480                b"new",
9481                b"13.361389",
9482                b"38.115556",
9483                b"here",
9484                b"181",
9485                b"38",
9486                b"there"
9487            ]),
9488            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9489        );
9490        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9491        assert_eq!(
9492            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9493            "-ERR value is not a valid float\r\n"
9494        );
9495        // The count of triples is checked before the two gates are, and a call
9496        // with no triples at all reaches the same sentence.
9497        assert_eq!(
9498            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9499            "-ERR syntax error\r\n"
9500        );
9501        assert_eq!(
9502            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9503            "-ERR syntax error\r\n"
9504        );
9505        assert_eq!(
9506            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9507            "-ERR syntax error\r\n"
9508        );
9509        assert_eq!(
9510            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9511            "-ERR wrong number of arguments for 'geoadd' command\r\n"
9512        );
9513    }
9514
9515    /// The sentences a search answers, which are its contract as much as the
9516    /// results are.
9517    #[test]
9518    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9519        let mut f = Fixture::new();
9520        sicily(&mut f);
9521        let cases: &[(&[&[u8]], &str)] = &[
9522            (
9523                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9524                "-ERR need numeric radius\r\n",
9525            ),
9526            (
9527                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9528                "-ERR radius cannot be negative\r\n",
9529            ),
9530            (
9531                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9532                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9533            ),
9534            (
9535                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9536                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9537            ),
9538            (
9539                &[
9540                    b"GEOSEARCH",
9541                    b"Sicily",
9542                    b"FROMLONLAT",
9543                    b"15",
9544                    b"37",
9545                    b"BYBOX",
9546                    b"x",
9547                    b"1",
9548                    b"km",
9549                ],
9550                "-ERR need numeric width\r\n",
9551            ),
9552            (
9553                &[
9554                    b"GEOSEARCH",
9555                    b"Sicily",
9556                    b"FROMLONLAT",
9557                    b"15",
9558                    b"37",
9559                    b"BYBOX",
9560                    b"1",
9561                    b"y",
9562                    b"km",
9563                ],
9564                "-ERR need numeric height\r\n",
9565            ),
9566            (
9567                &[
9568                    b"GEOSEARCH",
9569                    b"Sicily",
9570                    b"FROMLONLAT",
9571                    b"15",
9572                    b"37",
9573                    b"BYBOX",
9574                    b"-1",
9575                    b"1",
9576                    b"km",
9577                ],
9578                "-ERR height or width cannot be negative\r\n",
9579            ),
9580            (
9581                &[
9582                    b"GEOSEARCH",
9583                    b"Sicily",
9584                    b"FROMLONLAT",
9585                    b"15",
9586                    b"37",
9587                    b"BYRADIUS",
9588                    b"1",
9589                    b"km",
9590                    b"ANY",
9591                ],
9592                "-ERR the ANY argument requires COUNT argument\r\n",
9593            ),
9594            (
9595                &[
9596                    b"GEOSEARCH",
9597                    b"Sicily",
9598                    b"FROMLONLAT",
9599                    b"15",
9600                    b"37",
9601                    b"BYRADIUS",
9602                    b"1",
9603                    b"km",
9604                    b"COUNT",
9605                    b"0",
9606                ],
9607                "-ERR COUNT must be > 0\r\n",
9608            ),
9609            (
9610                &[
9611                    b"GEOSEARCH",
9612                    b"Sicily",
9613                    b"BYRADIUS",
9614                    b"1",
9615                    b"km",
9616                    b"BYBOX",
9617                    b"1",
9618                    b"1",
9619                    b"km",
9620                ],
9621                "-ERR syntax error\r\n",
9622            ),
9623            (
9624                &[
9625                    b"GEOSEARCH",
9626                    b"Sicily",
9627                    b"FROMMEMBER",
9628                    b"Palermo",
9629                    b"FROMLONLAT",
9630                    b"1",
9631                    b"2",
9632                    b"BYRADIUS",
9633                    b"1",
9634                    b"km",
9635                ],
9636                "-ERR syntax error\r\n",
9637            ),
9638            // The two options a GEOSEARCH cannot leave out, each with its own
9639            // sentence, and the command quoted the way the client spelled it.
9640            (
9641                &[
9642                    b"geosearch",
9643                    b"Sicily",
9644                    b"BYRADIUS",
9645                    b"1",
9646                    b"km",
9647                    b"ASC",
9648                    b"WITHDIST",
9649                ],
9650                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9651            ),
9652            (
9653                &[
9654                    b"GEOSEARCH",
9655                    b"Sicily",
9656                    b"FROMLONLAT",
9657                    b"15",
9658                    b"37",
9659                    b"ASC",
9660                    b"WITHDIST",
9661                ],
9662                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9663            ),
9664            // A store cannot also be asked for the distance, and the two
9665            // families name themselves differently in the same sentence.
9666            (
9667                &[
9668                    b"GEOSEARCHSTORE",
9669                    b"d",
9670                    b"Sicily",
9671                    b"FROMLONLAT",
9672                    b"15",
9673                    b"37",
9674                    b"BYRADIUS",
9675                    b"1",
9676                    b"km",
9677                    b"WITHCOORD",
9678                ],
9679                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9680            ),
9681            (
9682                &[
9683                    b"GEORADIUS",
9684                    b"Sicily",
9685                    b"15",
9686                    b"37",
9687                    b"1",
9688                    b"km",
9689                    b"WITHDIST",
9690                    b"STORE",
9691                    b"d",
9692                ],
9693                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9694            ),
9695            // The read only forms have no store at all, so the word is a stray
9696            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
9697            (
9698                &[
9699                    b"GEORADIUS_RO",
9700                    b"Sicily",
9701                    b"15",
9702                    b"37",
9703                    b"1",
9704                    b"km",
9705                    b"STORE",
9706                    b"d",
9707                ],
9708                "-ERR syntax error\r\n",
9709            ),
9710            (
9711                &[
9712                    b"GEOSEARCH",
9713                    b"Sicily",
9714                    b"FROMLONLAT",
9715                    b"15",
9716                    b"37",
9717                    b"BYRADIUS",
9718                    b"1",
9719                    b"km",
9720                    b"STOREDIST",
9721                ],
9722                "-ERR syntax error\r\n",
9723            ),
9724        ];
9725        for (parts, want) in cases {
9726            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9727        }
9728    }
9729
9730    /// A wrong type wins over a bad argument, because the key is looked up
9731    /// first, and every one of the ten says the same thing about it.
9732    #[test]
9733    fn every_geo_command_says_wrongtype() {
9734        let mut f = Fixture::new();
9735        f.run(&[b"SET", b"s", b"v"]);
9736        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9737        let cases: &[&[&[u8]]] = &[
9738            &[b"GEOADD", b"s", b"13", b"38", b"m"],
9739            &[b"GEOPOS", b"s", b"m"],
9740            &[b"GEOHASH", b"s", b"m"],
9741            &[b"GEODIST", b"s", b"a", b"b"],
9742            &[
9743                b"GEOSEARCH",
9744                b"s",
9745                b"FROMLONLAT",
9746                b"15",
9747                b"37",
9748                b"BYRADIUS",
9749                b"1",
9750                b"km",
9751            ],
9752            &[
9753                b"GEOSEARCHSTORE",
9754                b"d",
9755                b"s",
9756                b"FROMLONLAT",
9757                b"15",
9758                b"37",
9759                b"BYRADIUS",
9760                b"1",
9761                b"km",
9762            ],
9763            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9764            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9765            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9766            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9767        ];
9768        for case in cases {
9769            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9770        }
9771        // And it wins over an argument that will not parse, which is the whole
9772        // reason the lookup comes first.
9773        assert_eq!(
9774            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9775            wrong
9776        );
9777    }
9778
9779    // ----------------------------------------------------------------- array
9780
9781    #[test]
9782    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9783        let mut f = Fixture::new();
9784        // Three consecutive positions from a high index, and the reply is how
9785        // many of them were empty before rather than how many were written.
9786        assert_eq!(
9787            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9788            ":3\r\n"
9789        );
9790        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9791        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9792        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9793        // A hole and a key that is not there are the same answer.
9794        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9795        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9796        assert_eq!(
9797            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9798            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9799        );
9800        // Scattered pairs in one command, last write wins within it.
9801        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9802        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9803    }
9804
9805    /// The two numbers an array reports are not the same number, and one of
9806    /// them does not fit a signed integer.
9807    #[test]
9808    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9809        let mut f = Fixture::new();
9810        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9811        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9812        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9813        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9814        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9815        // Deleting in the middle leaves the high water mark where it was.
9816        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9817        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9818        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9819
9820        // The top of the space is addressable, and its length is a number with
9821        // bit sixty three set, so the reply has to be unsigned or it comes back
9822        // negative.
9823        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9824        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9825        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9826        // And one past it does not exist, so a write that would reach it fails
9827        // before any of it lands.
9828        assert_eq!(
9829            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9830            "-ERR array index overflow\r\n"
9831        );
9832        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9833    }
9834
9835    /// One reply per position and not one per element, which is the whole
9836    /// reason the range is capped.
9837    #[test]
9838    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9839        let mut f = Fixture::new();
9840        f.run(&[b"ARSET", b"a", b"1", b"x"]);
9841        assert_eq!(
9842            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9843            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9844        );
9845        // The two ends may come in either order, and the answer is reversed
9846        // rather than empty.
9847        assert_eq!(
9848            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9849            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9850        );
9851        // A key that is not there reads like an array of nothing but holes.
9852        assert_eq!(
9853            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9854            "*2\r\n$-1\r\n$-1\r\n"
9855        );
9856        // A range wider than a million positions is refused and not trimmed,
9857        // because against a missing key it is a request for as many nulls as
9858        // the range is wide.
9859        assert_eq!(
9860            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9861            "-ERR range exceeds maximum of 1000000 items\r\n"
9862        );
9863    }
9864
9865    /// Every index in the argument list is read before the key is touched, so
9866    /// a bad one at the end leaves nothing half written.
9867    #[test]
9868    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9869        let mut f = Fixture::new();
9870        assert_eq!(
9871            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9872            "-ERR invalid array index\r\n"
9873        );
9874        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9875        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9876        assert_eq!(
9877            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9878            "-ERR invalid array index\r\n"
9879        );
9880        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9881        // An index is unsigned here, so the numbers a list would take are not
9882        // the last element, they are errors.
9883        assert_eq!(
9884            f.run(&[b"ARGET", b"a", b"-1"]),
9885            "-ERR invalid array index\r\n"
9886        );
9887        // And a pair list with an odd tail is an arity error rather than a
9888        // syntax one.
9889        assert_eq!(
9890            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9891            "-ERR wrong number of arguments for 'armset' command\r\n"
9892        );
9893        assert_eq!(
9894            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9895            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9896        );
9897    }
9898
9899    #[test]
9900    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9901        let mut f = Fixture::new();
9902        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9903        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9904        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9905        // Two ranges in one command, and the second one covers the whole space
9906        // without walking it.
9907        assert_eq!(
9908            f.run(&[
9909                b"ARDELRANGE",
9910                b"a",
9911                b"100",
9912                b"200",
9913                b"0",
9914                b"18446744073709551614"
9915            ]),
9916            ":2\r\n"
9917        );
9918        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9919        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9920        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9921    }
9922
9923    /// A value goes out as the bytes it came in as, whichever of the three ways
9924    /// the array found to store it.
9925    #[test]
9926    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9927        let mut f = Fixture::new();
9928        let long = vec![b'v'; 200];
9929        f.run(&[
9930            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9931            b"short", b"5", &long, b"6", b"-0",
9932        ]);
9933        // 42 is an integer, 007 is not one because it does not print back the
9934        // same, 3.5 survives a double and 3.14 does not, and the last two are a
9935        // word packed string and a blob.
9936        assert_eq!(
9937            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9938            format!(
9939                "*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",
9940                String::from_utf8_lossy(&long)
9941            )
9942        );
9943    }
9944
9945    #[test]
9946    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9947        let mut f = Fixture::new();
9948        f.run(&[b"ARSET", b"a", b"0", b"x"]);
9949        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9950        assert_eq!(
9951            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9952            "$12\r\nsliced-array\r\n"
9953        );
9954        // And it is a body like any other, so the key commands work on it.
9955        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9956        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9957        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9958        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9959        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9960        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9961    }
9962
9963    #[test]
9964    fn every_array_command_refuses_a_key_holding_something_else() {
9965        let mut f = Fixture::new();
9966        f.run(&[b"SET", b"s", b"v"]);
9967        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9968        for cmd in [
9969            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9970            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9971            &[b"ARGET".as_ref(), b"s", b"0"][..],
9972            &[b"ARMGET".as_ref(), b"s", b"0"][..],
9973            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9974            &[b"ARLEN".as_ref(), b"s"][..],
9975            &[b"ARCOUNT".as_ref(), b"s"][..],
9976            &[b"ARDEL".as_ref(), b"s", b"0"][..],
9977            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9978            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9979            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9980            &[b"ARNEXT".as_ref(), b"s"][..],
9981            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9982            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9983            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9984            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9985            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9986            &[b"ARINFO".as_ref(), b"s"][..],
9987        ] {
9988            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9989        }
9990    }
9991
9992    /// Two of the array commands look the key up before they read the index and
9993    /// the rest read the index first, so the same broken argument gets two
9994    /// different errors depending on which command it went to.
9995    #[test]
9996    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9997        let mut f = Fixture::new();
9998        f.run(&[b"SET", b"s", b"v"]);
9999        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10000        let bad = "-ERR invalid array index\r\n";
10001        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
10002        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
10003        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
10004        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
10005        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
10006        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
10007        // And on a key that is an array the index is just an index.
10008        f.run(&[b"ARSET", b"a", b"0", b"x"]);
10009        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
10010        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
10011    }
10012
10013    #[test]
10014    fn an_append_follows_a_cursor_the_client_can_move() {
10015        let mut f = Fixture::new();
10016        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
10017        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
10018        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
10019        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
10020        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
10021
10022        // A seek says where the next one goes, and a missing key has no cursor
10023        // to move and is not created by the asking.
10024        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
10025        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
10026        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
10027        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
10028        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
10029        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
10030        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
10031
10032        // The top of the space is the one index only ARSEEK will take, and it
10033        // leaves the cursor with nowhere to go.
10034        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
10035        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
10036        assert_eq!(
10037            f.run(&[b"ARINSERT", b"a", b"x"]),
10038            "-ERR insert index overflow\r\n"
10039        );
10040        assert_eq!(
10041            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
10042            "-ERR invalid array index\r\n"
10043        );
10044    }
10045
10046    #[test]
10047    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
10048        let mut f = Fixture::new();
10049        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
10050        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
10051        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
10052        assert_eq!(
10053            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
10054            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
10055        );
10056        // Growing it after it has wrapped puts the survivors back in the order
10057        // they arrived, which is the whole point of paying for the rebuild.
10058        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
10059        assert_eq!(
10060            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
10061            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
10062        );
10063        // The size is read before the key, so a bad one is a bad size wherever
10064        // it is sent.
10065        assert_eq!(
10066            f.run(&[b"ARRING", b"r", b"0", b"x"]),
10067            "-ERR size must be positive\r\n"
10068        );
10069        assert_eq!(
10070            f.run(&[b"ARRING", b"r", b"big", b"x"]),
10071            "-ERR invalid size\r\n"
10072        );
10073    }
10074
10075    #[test]
10076    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
10077        let mut f = Fixture::new();
10078        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
10079        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
10080        assert_eq!(
10081            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
10082            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
10083        );
10084        assert_eq!(
10085            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
10086            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
10087        );
10088        assert_eq!(
10089            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
10090            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
10091            "more than there is gets what there is"
10092        );
10093        // Nothing asked for is an empty reply, and Redis answers that before it
10094        // has read the option or looked at the key.
10095        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
10096        assert_eq!(
10097            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
10098            "-ERR syntax error\r\n"
10099        );
10100        assert_eq!(
10101            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
10102            "-ERR invalid COUNT\r\n"
10103        );
10104
10105        // With no cursor the tail of the array is the anchor, and a hole inside
10106        // the window is reported as one.
10107        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
10108        assert_eq!(
10109            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
10110            "*2\r\n$-1\r\n$1\r\nz\r\n"
10111        );
10112    }
10113
10114    #[test]
10115    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
10116        let mut f = Fixture::new();
10117        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
10118        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
10119        // The whole index space, which ARGETRANGE refuses and this one answers
10120        // in three visits because holes cost nothing.
10121        assert_eq!(
10122            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
10123            "*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"
10124        );
10125        assert_eq!(
10126            f.run(&[
10127                b"ARSCAN",
10128                b"a",
10129                b"18446744073709551614",
10130                b"0",
10131                b"LIMIT",
10132                b"1"
10133            ]),
10134            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
10135        );
10136        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
10137        assert_eq!(
10138            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
10139            "-ERR LIMIT must be positive\r\n"
10140        );
10141        assert_eq!(
10142            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
10143            "-ERR syntax error\r\n"
10144        );
10145        assert_eq!(
10146            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
10147            "-ERR wrong number of arguments for 'arscan' command\r\n"
10148        );
10149    }
10150
10151    #[test]
10152    fn a_grep_answers_the_indexes_whose_elements_match() {
10153        let mut f = Fixture::new();
10154        assert_eq!(
10155            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
10156            "*0\r\n"
10157        );
10158        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
10159
10160        // The two bounds take the ends of the array as well as an index, and a
10161        // reversed range is walked backwards the way ARSCAN walks one.
10162        assert_eq!(
10163            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
10164            "*3\r\n:0\r\n:1\r\n:2\r\n"
10165        );
10166        assert_eq!(
10167            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
10168            "*3\r\n:2\r\n:1\r\n:0\r\n"
10169        );
10170        assert_eq!(
10171            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
10172            "*2\r\n:1\r\n:2\r\n"
10173        );
10174
10175        // One test each. NOCASE reaches all four of them and it may be written
10176        // after the pattern it applies to.
10177        assert_eq!(
10178            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
10179            "*1\r\n:0\r\n"
10180        );
10181        assert_eq!(
10182            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
10183            "*2\r\n:0\r\n:3\r\n"
10184        );
10185        assert_eq!(
10186            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
10187            "*1\r\n:2\r\n"
10188        );
10189        assert_eq!(
10190            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
10191            "*2\r\n:1\r\n:2\r\n"
10192        );
10193
10194        // OR is the default and AND has to be asked for, and either way the
10195        // last of a repeated option wins.
10196        let both: &[&[u8]] = &[
10197            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
10198        ];
10199        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
10200        assert_eq!(
10201            f.run(&[
10202                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
10203            ]),
10204            "*0\r\n"
10205        );
10206        assert_eq!(
10207            f.run(&[
10208                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
10209            ]),
10210            "*2\r\n:0\r\n:1\r\n"
10211        );
10212
10213        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
10214        // not the positions it had to look at.
10215        assert_eq!(
10216            f.run(&[
10217                b"ARGREP",
10218                b"a",
10219                b"-",
10220                b"+",
10221                b"MATCH",
10222                b"a",
10223                b"WITHVALUES",
10224                b"LIMIT",
10225                b"2"
10226            ]),
10227            "*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"
10228        );
10229        assert_eq!(
10230            f.run(&[
10231                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
10232            ]),
10233            "*1\r\n:3\r\n"
10234        );
10235    }
10236
10237    /// Everything ARGREP refuses, in the order it refuses it.
10238    #[test]
10239    fn a_grep_reports_a_broken_command_the_way_redis_does() {
10240        let mut f = Fixture::new();
10241        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
10242        let syntax = "-ERR syntax error\r\n";
10243
10244        // The bounds are read before the plan, so a bad index beats a bad
10245        // predicate whichever way round the two are written.
10246        assert_eq!(
10247            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
10248            "-ERR invalid array index\r\n"
10249        );
10250        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
10251        // A keyword with nothing after it, and a command that asks for nothing.
10252        assert_eq!(
10253            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
10254            syntax
10255        );
10256        assert_eq!(
10257            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
10258            syntax
10259        );
10260        assert_eq!(
10261            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
10262            syntax,
10263            "a command with no predicate in it at all"
10264        );
10265        assert_eq!(
10266            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
10267            "-ERR LIMIT must be positive\r\n"
10268        );
10269        assert_eq!(
10270            f.run(&[
10271                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
10272            ]),
10273            "-ERR value is not an integer or out of range\r\n"
10274        );
10275        assert_eq!(
10276            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
10277            "-ERR regular expression is empty\r\n"
10278        );
10279        assert_eq!(
10280            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
10281            "-ERR invalid regular expression: Missing ')'\r\n"
10282        );
10283        assert_eq!(
10284            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
10285            "-ERR regular expression backreferences are not supported\r\n"
10286        );
10287        // The arity is minus six, so a predicate keyword with no pattern after
10288        // it is short by one and never reaches the parser.
10289        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
10290        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
10291        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
10292    }
10293
10294    #[test]
10295    fn an_op_reduces_a_range_to_one_number() {
10296        let mut f = Fixture::new();
10297        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
10298        assert_eq!(
10299            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
10300            "$4\r\n-0.5\r\n"
10301        );
10302        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
10303        assert_eq!(
10304            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
10305            "$3\r\n2.5\r\n"
10306        );
10307        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
10308        assert_eq!(
10309            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
10310            ":1\r\n"
10311        );
10312        // An aggregate is written with seventeen significant digits, which is
10313        // Redis's own choice and not what a score comes back as.
10314        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
10315        assert_eq!(
10316            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
10317            "$19\r\n0.30000000000000004\r\n"
10318        );
10319        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
10320        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
10321
10322        // Nothing to work with is a null, and a missing key is a null for the
10323        // aggregates and a zero for the two that count.
10324        f.run(&[b"ARSET", b"w", b"0", b"word"]);
10325        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
10326        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
10327        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
10328
10329        assert_eq!(
10330            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
10331            "-ERR unknown operation\r\n"
10332        );
10333        assert_eq!(
10334            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
10335            "-ERR MATCH requires a value argument\r\n"
10336        );
10337        assert_eq!(
10338            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
10339            "-ERR wrong number of arguments for 'arop' command\r\n"
10340        );
10341    }
10342
10343    #[test]
10344    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
10345        let mut f = Fixture::new();
10346        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
10347        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
10348        let short = f.run(&[b"ARINFO", b"a"]);
10349        assert!(
10350            short.starts_with("*14\r\n"),
10351            "seven pairs on RESP2: {short}"
10352        );
10353        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
10354        assert!(
10355            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
10356            "{short}"
10357        );
10358        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
10359        let full = f.run(&[b"ARINFO", b"a", b"full"]);
10360        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
10361        // Two values one apart are held sparsely, so the dense count is zero and
10362        // the two dense averages have nothing to average.
10363        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
10364        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
10365        assert!(
10366            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
10367            "{full}"
10368        );
10369        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
10370
10371        // On RESP3 the same reply is a map and the averages are doubles.
10372        let mut g = Fixture::new();
10373        g.run(&[b"HELLO", b"3"]);
10374        g.run(&[b"ARINSERT", b"a", b"x"]);
10375        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
10376        assert!(map.starts_with("%12\r\n"), "{map}");
10377        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
10378        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
10379    }
10380
10381    #[test]
10382    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
10383        let mut f = Fixture::new();
10384        // Whole numbers up to two to the sixty second come back as integers,
10385        // and past that the digit generator takes over and uses an exponent.
10386        for (score, want) in [
10387            ("3", "3"),
10388            ("3.5", "3.5"),
10389            ("0.3", "0.3"),
10390            ("1e30", "1e+30"),
10391            ("1e19", "1e+19"),
10392            ("1e-7", "1e-7"),
10393            ("0.000001", "0.000001"),
10394            ("4611686018427387904", "4611686018427387904"),
10395            ("-0", "-0"),
10396        ] {
10397            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
10398            assert_eq!(
10399                f.run(&[b"ZSCORE", b"z", b"m"]),
10400                format!("${}\r\n{want}\r\n", want.len()),
10401                "score {score}"
10402            );
10403        }
10404
10405        // The same bytes on RESP3, where the reply is a double rather than a
10406        // bulk string.
10407        let mut g = Fixture::new();
10408        g.run(&[b"HELLO", b"3"]);
10409        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10410        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10411        // The two float increments are not this printer. They go through
10412        // ld2string in its human mode, which is a fixed point conversion with
10413        // the trailing zeros taken off, so they never write an exponent, and
10414        // they reply with a bulk string on both protocols.
10415        assert_eq!(
10416            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10417            "$31\r\n1000000000000000000000000000000\r\n"
10418        );
10419        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10420        assert_eq!(
10421            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10422            "$20\r\n10000000000000000000\r\n"
10423        );
10424    }
10425
10426    // ----------------------------------------------------------------- graph
10427
10428    #[test]
10429    fn a_node_comes_back_with_the_fields_it_went_in_with() {
10430        let mut f = Fixture::new();
10431        assert_eq!(
10432            f.run(&[
10433                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10434            ]),
10435            ":1\r\n"
10436        );
10437        // The year comes back as the four bytes that were sent and not as a
10438        // number, because every property is text and there is nothing on the
10439        // wire that says which of `1815` and `"1815"` the client meant. The
10440        // fields are in the document's order, which is sorted by name, because
10441        // that is what makes a field lookup a binary search.
10442        assert_eq!(
10443            f.run(&[b"G.NGET", b"social", b"ada"]),
10444            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10445        );
10446        // A second write to the same id replaces the document and says so with
10447        // a zero, so an ingest can count what it created.
10448        assert_eq!(
10449            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10450            ":0\r\n"
10451        );
10452        assert_eq!(
10453            f.run(&[b"G.NGET", b"social", b"ada"]),
10454            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10455        );
10456        // A node with no properties is an empty map and not a null, which is
10457        // how a client tells an isolated node from one that is not there.
10458        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10459        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10460        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10461        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10462
10463        // A field with no value creates nothing, because the pairs are checked
10464        // before the key is touched.
10465        assert_eq!(
10466            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10467            "-ERR syntax error\r\n"
10468        );
10469        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10470
10471        // On RESP3 the same reply is a map.
10472        let mut g = Fixture::new();
10473        g.run(&[b"HELLO", b"3"]);
10474        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10475        assert_eq!(
10476            g.run(&[b"G.NGET", b"social", b"ada"]),
10477            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10478        );
10479    }
10480
10481    #[test]
10482    fn an_edge_creates_the_ends_it_needs() {
10483        let mut f = Fixture::new();
10484        assert_eq!(
10485            f.run(&[
10486                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10487            ]),
10488            ":1\r\n"
10489        );
10490        // Neither end was written first and both are there, as empty nodes.
10491        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10492        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10493        assert_eq!(
10494            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10495            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10496        );
10497        assert_eq!(
10498            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10499            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10500        );
10501        // The same pair under the same label again updates the edge rather than
10502        // making a second one.
10503        assert_eq!(
10504            f.run(&[
10505                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10506            ]),
10507            ":0\r\n"
10508        );
10509        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10510        // A different label between the same pair is a different edge.
10511        assert_eq!(
10512            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10513            ":1\r\n"
10514        );
10515        assert_eq!(
10516            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10517            ":1\r\n"
10518        );
10519
10520        assert_eq!(
10521            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10522            ":1\r\n"
10523        );
10524        assert_eq!(
10525            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10526            ":0\r\n"
10527        );
10528        // A label nothing has used, an end that is not there, and a key that is
10529        // not there are all a zero rather than an error.
10530        assert_eq!(
10531            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10532            ":0\r\n"
10533        );
10534        assert_eq!(
10535            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10536            ":0\r\n"
10537        );
10538        assert_eq!(
10539            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10540            ":0\r\n"
10541        );
10542    }
10543
10544    /// A run is paged the way `SCAN` is paged, so a client that can walk one
10545    /// can walk the other.
10546    #[test]
10547    fn a_hop_answers_a_cursor_and_a_page() {
10548        let mut f = Fixture::new();
10549        for i in 0..25u32 {
10550            let dst = format!("n{i}");
10551            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10552        }
10553        // Ten without being asked, and the cursor is where to carry on from.
10554        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10555        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10556
10557        let mut seen = 0;
10558        let mut cursor = String::from("0");
10559        loop {
10560            let page = f.run(&[
10561                b"G.OUT",
10562                b"social",
10563                b"hub",
10564                b"FOLLOWS",
10565                b"COUNT",
10566                b"7",
10567                b"CURSOR",
10568                cursor.as_bytes(),
10569            ]);
10570            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10571            cursor = head
10572                .rsplit("\r\n")
10573                .next()
10574                .expect("the cursor line")
10575                .to_string();
10576            seen += rest
10577                .split_once("\r\n")
10578                .expect("the page length")
10579                .0
10580                .parse::<usize>()
10581                .expect("a length");
10582            if cursor == "0" {
10583                break;
10584            }
10585        }
10586        assert_eq!(seen, 25, "every neighbour once across the pages");
10587
10588        // A cursor past the end is an empty page and not an error, and so is a
10589        // key or a label that is not there.
10590        assert_eq!(
10591            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10592            "*2\r\n$1\r\n0\r\n*0\r\n"
10593        );
10594        assert_eq!(
10595            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10596            "*2\r\n$1\r\n0\r\n*0\r\n"
10597        );
10598        assert_eq!(
10599            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10600            "*2\r\n$1\r\n0\r\n*0\r\n"
10601        );
10602        assert_eq!(
10603            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10604            "-ERR COUNT must be a positive integer\r\n"
10605        );
10606        assert_eq!(
10607            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10608            "-ERR syntax error\r\n"
10609        );
10610    }
10611
10612    #[test]
10613    fn a_degree_counts_one_way_or_both() {
10614        let mut f = Fixture::new();
10615        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10616        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10617        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10618        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10619        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10620        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10621        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10622        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10623        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10624        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10625        assert_eq!(
10626            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10627            "-ERR syntax error\r\n"
10628        );
10629    }
10630
10631    /// A walk answers which nodes it can reach and not by how many routes, so a
10632    /// node two ways out is in the frontier once.
10633    #[test]
10634    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10635        let mut f = Fixture::new();
10636        for (src, dst) in [
10637            ("ada", "grace"),
10638            ("ada", "alan"),
10639            ("grace", "edsger"),
10640            ("alan", "edsger"),
10641            ("edsger", "barbara"),
10642        ] {
10643            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10644        }
10645        // Two hops without being asked, the start left out, and edsger once
10646        // even though both of the first hop's nodes point at it.
10647        assert_eq!(
10648            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10649            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10650        );
10651        assert_eq!(
10652            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10653            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10654        );
10655        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10656        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10657        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10658        // COUNT stops the walk rather than trimming what it found.
10659        assert_eq!(
10660            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10661            "*1\r\n$5\r\ngrace\r\n"
10662        );
10663        // A node nothing leaves is an empty array and not an error.
10664        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10665        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10666        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10667        assert_eq!(
10668            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10669            "-ERR DEPTH must be a positive integer\r\n"
10670        );
10671        assert_eq!(
10672            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10673            "-ERR syntax error\r\n"
10674        );
10675    }
10676
10677    /// The two sided search, which is the whole reason `G.PATH` is a command
10678    /// and not something a client builds out of `G.OUT`.
10679    #[test]
10680    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10681        let mut f = Fixture::new();
10682        // A chain of six, and a shortcut that makes a shorter way round under a
10683        // second label so the search has to take either kind of hop.
10684        for i in 0..6u32 {
10685            let src = format!("n{i}");
10686            let dst = format!("n{}", i + 1);
10687            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10688        }
10689        assert_eq!(
10690            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10691            "*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"
10692        );
10693        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10694        assert_eq!(
10695            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10696            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10697        );
10698        // A node to itself is a path of one, and a depth too short to reach is
10699        // no path at all.
10700        assert_eq!(
10701            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10702            "*1\r\n$2\r\nn2\r\n"
10703        );
10704        assert_eq!(
10705            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10706            "*0\r\n"
10707        );
10708        // Direction counts: the chain only goes one way.
10709        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10710        // An unreachable node, a node that is not there, and a key that is not
10711        // there are the same empty answer.
10712        f.run(&[b"G.NADD", b"road", b"island"]);
10713        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10714        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10715        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10716        assert_eq!(
10717            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10718            "-ERR syntax error\r\n"
10719        );
10720    }
10721
10722    /// The point of the escape in the record tag: the keyspace owns a graph key
10723    /// the way it owns every other key, and none of these commands know a graph
10724    /// exists.
10725    #[test]
10726    fn the_keyspace_sees_a_graph_key_like_any_other() {
10727        let mut f = Fixture::new();
10728        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10729        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10730        assert_eq!(
10731            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10732            "$9\r\nadjacency\r\n"
10733        );
10734        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10735        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10736        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10737        // A graph is counted against the server the way every other body is,
10738        // which is what `maxmemory` will read when this key is a million nodes.
10739        // There is no `MEMORY USAGE` command yet, so this asks the server.
10740        let held = f.server.memory_bytes();
10741        for i in 0..200u32 {
10742            let dst = format!("n{i}");
10743            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10744        }
10745        assert!(
10746            f.server.memory_bytes() > held,
10747            "two hundred edges cost something: {held} then {}",
10748            f.server.memory_bytes()
10749        );
10750        f.run(&[b"DEL", b"big"]);
10751
10752        // An expiry, then a rename, then a move to another database, all of
10753        // which are the keyspace moving a record it cannot look inside.
10754        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10755        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10756        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10757        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10758        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10759        f.run(&[b"SELECT", b"1"]);
10760        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10761
10762        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10763        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10764        f.run(&[b"G.NADD", b"g", b"n"]);
10765        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10766        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10767    }
10768
10769    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
10770    /// rather than answering the way they answer for a key that is not there.
10771    #[test]
10772    fn a_graph_cannot_be_copied_or_dumped() {
10773        let mut f = Fixture::new();
10774        f.run(&[b"G.NADD", b"social", b"ada"]);
10775        assert_eq!(
10776            f.run(&[b"COPY", b"social", b"other"]),
10777            "-ERR COPY is not supported for a graph\r\n"
10778        );
10779        assert_eq!(
10780            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10781            "-ERR COPY is not supported for a graph\r\n"
10782        );
10783        assert_eq!(
10784            f.run(&[b"DUMP", b"social"]),
10785            "-ERR DUMP is not supported for a graph\r\n"
10786        );
10787        // A refused copy leaves both keys exactly as they were.
10788        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10789    }
10790
10791    /// A graph key is a key, so the commands for the other types refuse it and
10792    /// the graph commands refuse theirs.
10793    #[test]
10794    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10795        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10796        let mut f = Fixture::new();
10797        f.run(&[b"G.NADD", b"social", b"ada"]);
10798        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10799        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10800        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10801
10802        f.run(&[b"SET", b"str", b"v"]);
10803        for cmd in [
10804            vec![b"G.NADD".as_ref(), b"str", b"n"],
10805            vec![b"G.NGET".as_ref(), b"str", b"n"],
10806            vec![b"G.NDEL".as_ref(), b"str", b"n"],
10807            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10808            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10809            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10810            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10811            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10812            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10813            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10814        ] {
10815            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10816        }
10817    }
10818
10819    /// Every other collection here takes its key with it when its last member
10820    /// goes, and a graph is no different.
10821    #[test]
10822    fn a_graph_goes_when_its_last_node_does() {
10823        let mut f = Fixture::new();
10824        f.run(&[
10825            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10826        ]);
10827        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10828        // The node and the edges that hung off it are both gone.
10829        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10830        assert_eq!(
10831            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10832            ":0\r\n"
10833        );
10834        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10835        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10836
10837        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10838        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10839        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10840        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10841
10842        // The id the removed node had is not handed out again, so a client
10843        // holding an id from an earlier reply cannot have it mean another node.
10844        f.run(&[b"G.NADD", b"social", b"first"]);
10845        f.run(&[b"G.NADD", b"social", b"second"]);
10846        f.run(&[b"G.NDEL", b"social", b"first"]);
10847        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10848        assert_eq!(
10849            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10850            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10851        );
10852    }
10853
10854    // ------------------------------------------------------------------ json
10855
10856    /// The two path syntaxes answer different shapes, which is the thing a
10857    /// client is most likely to be broken by and so the thing to pin first.
10858    #[test]
10859    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10860        let mut f = Fixture::new();
10861        let doc = br#"{"a":1,"b":{"c":true}}"#;
10862        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10863        // No path at all is the legacy root and not `$`, so the document comes
10864        // back as itself rather than wrapped.
10865        assert_eq!(
10866            f.run(&[b"JSON.GET", b"doc"]),
10867            bulk(r#"{"a":1,"b":{"c":true}}"#)
10868        );
10869        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10870        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10871        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10872        // A path that matched nothing is an empty set on one syntax and an
10873        // error on the other, and the error does not quote the path.
10874        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10875        assert_eq!(
10876            f.run(&[b"JSON.GET", b"doc", b".nope"]),
10877            "-ERR Path does not exist\r\n"
10878        );
10879        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10880        // The key is a document to the rest of the keyspace, under the name
10881        // RedisJSON registers, and every generic command works on it.
10882        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10883        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10884        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10885        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10886        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10887    }
10888
10889    /// The two error lines RedisJSON sends without a prefix in front of them.
10890    ///
10891    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
10892    /// two do not, on a real server, and a differential harness compares the
10893    /// whole line.
10894    #[test]
10895    fn the_two_json_errors_that_carry_no_prefix() {
10896        let mut f = Fixture::new();
10897        f.run(&[b"SET", b"plain", b"x"]);
10898        let wrong = "-Existing key has wrong Redis type\r\n";
10899        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10900        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10901        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10902        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10903        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10904
10905        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10906        // A wildcard that matched something writes to all of it. A wildcard
10907        // that matched nothing would have to invent a place, and that is the
10908        // other unprefixed line.
10909        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10910        assert_eq!(
10911            f.run(&[b"JSON.GET", b"doc"]),
10912            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10913        );
10914        assert_eq!(
10915            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10916            "-Err wrong static path\r\n"
10917        );
10918    }
10919
10920    /// What `JSON.SET` does with a path that named nowhere.
10921    #[test]
10922    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10923        let mut f = Fixture::new();
10924        // A key that is not there can only be written whole.
10925        assert_eq!(
10926            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10927            "-ERR new objects must be created at the root\r\n"
10928        );
10929        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10930        // The root check comes before NX and XX, which is the order a real
10931        // server checks them in.
10932        assert_eq!(
10933            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10934            "-ERR new objects must be created at the root\r\n"
10935        );
10936        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10937        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10938
10939        f.run(&[
10940            b"JSON.SET",
10941            b"doc",
10942            b"$",
10943            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10944        ]);
10945        // One step past a container that is there is a place to write.
10946        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10947        // One step past something that is not, or past something that is not an
10948        // object, is not an error and is not a write either.
10949        assert_eq!(
10950            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10951            "$-1\r\n"
10952        );
10953        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10954        // An index past the end does not append. JSON.ARRAPPEND appends.
10955        assert_eq!(
10956            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10957            "-ERR array index out of range\r\n"
10958        );
10959        assert_eq!(
10960            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10961            "-ERR array index out of range\r\n"
10962        );
10963        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10964        // NX on a path that is there and XX on a path that is not are both a
10965        // nil and neither changes anything.
10966        assert_eq!(
10967            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10968            "$-1\r\n"
10969        );
10970        assert_eq!(
10971            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10972            "$-1\r\n"
10973        );
10974        assert_eq!(
10975            f.run(&[b"JSON.GET", b"doc"]),
10976            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10977        );
10978        // Text that is not JSON is refused before the key is touched. The
10979        // line has no `ERR` in front of it, which is this command's and not
10980        // every command's, and is in D-37.
10981        assert!(
10982            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10983                .starts_with("-this is not the start of a value")
10984        );
10985        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10986    }
10987
10988    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
10989    /// answers a count or a word rather than text.
10990    #[test]
10991    fn the_json_commands_that_do_not_answer_text() {
10992        let mut f = Fixture::new();
10993        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10994        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10995
10996        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10997        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10998        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10999        assert_eq!(
11000            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
11001            format!("*1\r\n{}", bulk("integer"))
11002        );
11003        // The one place a legacy path that matched nothing is a nil rather than
11004        // an error, which lines up with a key that is not there.
11005        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
11006        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
11007
11008        // A boolean flips and answers the value it now has, as an integer on
11009        // one syntax and as the word on the other.
11010        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
11011        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
11012        // Something that is not a boolean is a hole on one syntax and one
11013        // sentence covering both cases on the other.
11014        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
11015        assert_eq!(
11016            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
11017            "-ERR Path does not exist or not a bool\r\n"
11018        );
11019        assert_eq!(
11020            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
11021            "-ERR Path does not exist or not a bool\r\n"
11022        );
11023        assert_eq!(
11024            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
11025            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11026        );
11027
11028        // Clearing empties containers and zeroes numbers and leaves everything
11029        // else alone, and counts only what it changed.
11030        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
11031        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
11032        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
11033        assert_eq!(
11034            f.run(&[b"JSON.GET", b"doc"]),
11035            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
11036        );
11037
11038        // Deleting counts what it removed, and deleting the root is deleting
11039        // the key.
11040        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
11041        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
11042        // Deleting the last member of the root container deletes the key, the
11043        // same way popping the last element off a list does. It is a rule about
11044        // deleting and not about shape: a document written as an empty object
11045        // by JSON.SET stays, because nothing was removed from it.
11046        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
11047        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
11048        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
11049        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
11050        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
11051        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
11052        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
11053        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
11054    }
11055
11056    /// `JSON.GET` with more than one path, and with a layout.
11057    ///
11058    /// The wrapper the reply is built in is laid out too, so what a path
11059    /// matched starts one level in for a single JSONPath and two for one of
11060    /// several, and getting that wrong is the kind of thing only a byte for
11061    /// byte comparison catches.
11062    #[test]
11063    fn json_get_lays_out_the_wrapper_it_builds() {
11064        let mut f = Fixture::new();
11065        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
11066
11067        assert_eq!(
11068            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
11069            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
11070        );
11071        // Legacy paths are not wrapped, even when there are several of them.
11072        assert_eq!(
11073            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
11074            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
11075        );
11076        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
11077        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
11078        one.extend_from_slice(fmt);
11079        one.push(b"$.b");
11080        assert_eq!(
11081            f.run(&one),
11082            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
11083        );
11084        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
11085        two.extend_from_slice(fmt);
11086        two.push(b"$.a");
11087        two.push(b"$.nope");
11088        assert_eq!(
11089            f.run(&two),
11090            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
11091        );
11092        // The options are read before the paths and in any order, and a
11093        // document with nothing to lay out is the same either way.
11094        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
11095        root.push(b".a");
11096        assert_eq!(f.run(&root), bulk("1"));
11097    }
11098
11099    /// `JSON.MGET`, which is the only command here that reads more than one key
11100    /// and so the only one whose answer has holes in it.
11101    #[test]
11102    fn json_mget_answers_once_per_key_whatever_is_under_them() {
11103        let mut f = Fixture::new();
11104        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
11105        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
11106        f.run(&[b"SET", b"plain", b"x"]);
11107        assert_eq!(
11108            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
11109            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
11110        );
11111        // A key that is not there and a key holding something else are both a
11112        // hole rather than an error, the way MGET treats a hash.
11113        assert_eq!(
11114            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
11115            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
11116        );
11117        // A legacy path that matched nothing is a hole too, because one bad
11118        // answer should not lose the others.
11119        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
11120    }
11121
11122    /// The four commands that ask how big something is, and the four different
11123    /// sets of answers they give for the same three failures.
11124    ///
11125    /// There is no pattern in this and there is no reading it off the
11126    /// documentation either. It was read off a running RedisJSON one line at a
11127    /// time, and it is written down here because the error text is what a client
11128    /// library branches on.
11129    #[test]
11130    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
11131        let mut f = Fixture::new();
11132        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
11133        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11134
11135        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
11136        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
11137        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
11138        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
11139        assert_eq!(
11140            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
11141            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
11142        );
11143        // A JSONPath answers one entry per match and a hole for a match of the
11144        // wrong kind, which is the one shape all four agree on.
11145        assert_eq!(
11146            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
11147            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
11148        );
11149
11150        // A legacy path that matched nothing. Two of them are an error and two
11151        // of them are a nil, and the two errors do not use the same sentence.
11152        assert_eq!(
11153            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
11154            "-ERR Path does not exist\r\n"
11155        );
11156        assert_eq!(
11157            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
11158            "-ERR Path does not exist\r\n"
11159        );
11160        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
11161        // A nil bulk and not an empty array, even though the answer would have
11162        // been an array, which is what RedisJSON sends here too.
11163        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
11164        // The JSONPath spelling of the same question is an empty array, since
11165        // no match is not a failure on that syntax.
11166        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
11167
11168        // A legacy path that matched the wrong kind of value. Now two of them
11169        // are an ERR and two of them are a WRONGTYPE, and it is not the same
11170        // two.
11171        assert_eq!(
11172            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
11173            "-ERR Path does not exist or not an array\r\n"
11174        );
11175        assert_eq!(
11176            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
11177            "-ERR Path does not exist or not an object\r\n"
11178        );
11179        assert_eq!(
11180            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
11181            "-WRONGTYPE wrong type of path value - expected object\r\n"
11182        );
11183        assert_eq!(
11184            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
11185            "-WRONGTYPE wrong type of path value - expected string\r\n"
11186        );
11187
11188        // A key that is not there, where the two syntaxes swap over: the legacy
11189        // path is the quiet answer and the JSONPath is the error.
11190        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
11191        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
11192        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
11193        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
11194        assert_eq!(
11195            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
11196            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11197        );
11198        // Except this one, which answers about the path instead.
11199        assert_eq!(
11200            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
11201            "-ERR Path does not exist or not an object\r\n"
11202        );
11203    }
11204
11205    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
11206    ///
11207    /// The four of them share one error line for a path that named something
11208    /// that is not an array, and they disagree about what an index outside the
11209    /// array means: insert refuses it and the other two clamp.
11210    #[test]
11211    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
11212        let mut f = Fixture::new();
11213        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
11214
11215        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
11216        assert_eq!(
11217            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
11218            "*1\r\n:6\r\n"
11219        );
11220        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
11221
11222        // A negative index counts back from the end, and the end itself is a
11223        // place to insert at, so an insert at the length is an append.
11224        assert_eq!(
11225            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
11226            ":7\r\n"
11227        );
11228        assert_eq!(
11229            f.run(&[b"JSON.GET", b"doc", b".a"]),
11230            bulk("[1,2,3,4,5,0,6]")
11231        );
11232        assert_eq!(
11233            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
11234            ":8\r\n"
11235        );
11236        // One past the end is not, and neither is one before the front.
11237        assert_eq!(
11238            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
11239            "-ERR index out of bounds\r\n"
11240        );
11241        assert_eq!(
11242            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
11243            "-ERR index out of bounds\r\n"
11244        );
11245
11246        // Trim takes both ends inclusive and clamps both of them, so a start
11247        // past the end leaves an empty array rather than an error.
11248        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
11249        assert_eq!(
11250            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
11251            ":3\r\n"
11252        );
11253        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
11254        assert_eq!(
11255            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
11256            ":2\r\n"
11257        );
11258        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
11259        assert_eq!(
11260            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
11261            ":0\r\n"
11262        );
11263        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11264
11265        // Pop clamps as well, its default is the last element, and an empty
11266        // array pops a nil rather than failing.
11267        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
11268        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
11269        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
11270        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
11271        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
11272
11273        // One sentence covers a path that matched nothing and a path that
11274        // matched the wrong kind of value, for all four of them.
11275        for call in [
11276            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
11277            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
11278            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
11279            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
11280        ] {
11281            for path in [&b".n"[..], &b".nope"[..]] {
11282                let args: Vec<&[u8]> = call
11283                    .iter()
11284                    .map(|a| if *a == b"PATH" { path } else { *a })
11285                    .collect();
11286                assert_eq!(
11287                    f.run(&args),
11288                    "-ERR Path does not exist or not an array\r\n",
11289                    "{} {}",
11290                    String::from_utf8_lossy(call[0]),
11291                    String::from_utf8_lossy(path)
11292                );
11293            }
11294        }
11295
11296        // A key that is not there is the same sentence for all four, on either
11297        // syntax, and it is about the key and not about the path.
11298        assert_eq!(
11299            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
11300            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11301        );
11302        assert_eq!(
11303            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
11304            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11305        );
11306
11307        // The values are parsed before the key is touched, so text that is not
11308        // JSON leaves the document alone.
11309        // Text that is not JSON is refused before the key is touched, and
11310        // the line has no `ERR` in front of it, which is D-37.
11311        assert!(
11312            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
11313                .starts_with("-this is not the start of a value")
11314        );
11315        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11316    }
11317
11318    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
11319    /// path matched cannot take the index, which is D-36.
11320    ///
11321    /// RedisJSON walks the matches, inserts into each one it can, and returns
11322    /// the error on the first one it cannot, leaving the earlier inserts in the
11323    /// document. A write here is one list of edits applied together, so either
11324    /// all of them happen or none of them do.
11325    #[test]
11326    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
11327        let mut f = Fixture::new();
11328        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
11329        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11330        assert_eq!(
11331            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
11332            "-ERR index out of bounds\r\n"
11333        );
11334        assert_eq!(
11335            f.run(&[b"JSON.GET", b"doc"]),
11336            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
11337        );
11338        // Every match can take the index, so every match gets it.
11339        assert_eq!(
11340            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
11341            "*3\r\n:4\r\n:3\r\n:2\r\n"
11342        );
11343        assert_eq!(
11344            f.run(&[b"JSON.GET", b"doc"]),
11345            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
11346        );
11347    }
11348
11349    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
11350    /// last element rather than to one past it.
11351    ///
11352    /// Both of those read like mistakes and both are what RedisJSON does. The
11353    /// start is the one that bites: a start of five into an array of four still
11354    /// looks at the fourth, so a search that should have run out of array comes
11355    /// back with an answer.
11356    #[test]
11357    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
11358        let mut f = Fixture::new();
11359        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
11360
11361        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
11362        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
11363        assert_eq!(
11364            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
11365            "*1\r\n:1\r\n"
11366        );
11367
11368        // Zero as the stop means the end rather than the front, so leaving it
11369        // off and passing it are the same thing.
11370        assert_eq!(
11371            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
11372            ":3\r\n"
11373        );
11374        // The stop is exclusive, so a stop of three does not look at index
11375        // three.
11376        assert_eq!(
11377            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
11378            ":-1\r\n"
11379        );
11380
11381        // The start clamps to the last element in both directions, which is why
11382        // a start of four, five or minus one all find the 1 at index three.
11383        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
11384            assert_eq!(
11385                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
11386                ":3\r\n",
11387                "{}",
11388                String::from_utf8_lossy(start)
11389            );
11390        }
11391        assert_eq!(
11392            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
11393            ":0\r\n"
11394        );
11395        // An empty array is the one case that comes back with nothing, since
11396        // the stop is zero and the loop never starts.
11397        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
11398        assert_eq!(
11399            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11400            ":-1\r\n"
11401        );
11402
11403        // The comparison is structural rather than one of the encoded bytes,
11404        // because an object in a stored document holds its keys as intern table
11405        // ids where one parsed off the wire holds them as bytes.
11406        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11407        assert_eq!(
11408            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11409            ":0\r\n"
11410        );
11411        assert_eq!(
11412            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11413            ":1\r\n"
11414        );
11415        assert_eq!(
11416            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11417            ":-1\r\n"
11418        );
11419
11420        // Its errors are a third set again: a missing legacy path is the short
11421        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
11422        // not there is about the path on either syntax.
11423        assert_eq!(
11424            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11425            "-ERR Path does not exist\r\n"
11426        );
11427        assert_eq!(
11428            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11429            "-WRONGTYPE wrong type of path value - expected array\r\n"
11430        );
11431        assert_eq!(
11432            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11433            "-ERR Path does not exist\r\n"
11434        );
11435        assert_eq!(
11436            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11437            "-ERR Path does not exist\r\n"
11438        );
11439    }
11440
11441    /// The number family answers text and keeps an integer an integer until
11442    /// something in the sum is not one.
11443    #[test]
11444    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11445        let mut f = Fixture::new();
11446        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11447        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11448
11449        // A legacy path answers the new value as JSON text in a bulk string,
11450        // not as a number, which is the shape all three of them use.
11451        assert_eq!(
11452            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11453            bulk("9").as_str()
11454        );
11455        // A JSONPath answers a bulk string holding a JSON array.
11456        assert_eq!(
11457            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11458            bulk("[11]").as_str()
11459        );
11460        // Two integers stay an integer and a double anywhere in it makes the
11461        // answer a double, which the document then holds.
11462        assert_eq!(
11463            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11464            bulk("13.0").as_str()
11465        );
11466        assert_eq!(
11467            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11468            bulk("number").as_str()
11469        );
11470        assert_eq!(
11471            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11472            bulk("3.0").as_str()
11473        );
11474        assert_eq!(
11475            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11476            bulk("-8").as_str()
11477        );
11478        // A power of a half is a square root, and the square root of a negative
11479        // number is the error that says the answer is not a number.
11480        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11481        assert_eq!(
11482            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11483            bulk("1.224744871391589").as_str()
11484        );
11485        assert_eq!(
11486            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11487            "-ERR result is not a number\r\n"
11488        );
11489        // An integer answer that does not fit is refused rather than promoted,
11490        // and a negative exponent lands in the same error because there is no
11491        // integer answer to two to the minus one.
11492        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11493        assert_eq!(
11494            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11495            "-ERR numeric overflow\r\n"
11496        );
11497        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11498        assert_eq!(
11499            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11500            "-ERR numeric overflow\r\n"
11501        );
11502        // A double that leaves the finite numbers is the other error.
11503        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11504        assert_eq!(
11505            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11506            "-ERR result is not a number\r\n"
11507        );
11508
11509        // A match that is not a number is a null inside the array on a
11510        // JSONPath, and a legacy path that found no number at all is the error
11511        // with the module's own typo in it.
11512        assert_eq!(
11513            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11514            bulk("[null]").as_str()
11515        );
11516        assert_eq!(
11517            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11518            bulk("[]").as_str()
11519        );
11520        assert_eq!(
11521            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11522            "-ERR Path does not exist or does not contains a number\r\n"
11523        );
11524        assert_eq!(
11525            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11526            "-ERR Path does not exist or does not contains a number\r\n"
11527        );
11528        // The operand is JSON and has to be a number. Valid JSON that is not
11529        // one is a line of its own, and it goes out without a prefix.
11530        assert_eq!(
11531            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11532            "-bad input number\r\n"
11533        );
11534        assert_eq!(
11535            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11536            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11537        );
11538        assert_eq!(
11539            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11540            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11541        );
11542    }
11543
11544    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
11545    /// which nothing else in the group does.
11546    #[test]
11547    fn json_strappend_reads_its_shape_off_the_argument_count() {
11548        let mut f = Fixture::new();
11549        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11550
11551        assert_eq!(
11552            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11553            ":3\r\n"
11554        );
11555        assert_eq!(
11556            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11557            "*1\r\n:4\r\n"
11558        );
11559        // The length is in bytes and not in characters, so one two byte letter
11560        // takes it up by two.
11561        assert_eq!(
11562            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11563            ":6\r\n"
11564        );
11565        // Three arguments means the value is the last one and the path is the
11566        // root, so this appends to a document that is a string on its own.
11567        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11568        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11569        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11570
11571        // The value is JSON and has to be a JSON string. A number is a
11572        // WRONGTYPE about a path value even though it was the value that was
11573        // wrong, which is the module's wording and not a slip here.
11574        assert_eq!(
11575            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11576            "-WRONGTYPE wrong type of path value - expected string\r\n"
11577        );
11578        assert_eq!(
11579            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11580            "*1\r\n$-1\r\n"
11581        );
11582        assert_eq!(
11583            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11584            "-ERR Path does not exist or not a string\r\n"
11585        );
11586        assert_eq!(
11587            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11588            "*0\r\n"
11589        );
11590        assert_eq!(
11591            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11592            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11593        );
11594    }
11595
11596    /// A legacy path can match more than one value, and which of them the one
11597    /// answer comes from is not the same choice twice.
11598    #[test]
11599    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11600        let mut f = Fixture::new();
11601        // Three arrays of one, two and three elements, which tells the first
11602        // match and the last match apart in a single command.
11603        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11604
11605        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11606        assert_eq!(
11607            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11608            ":4\r\n"
11609        );
11610        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11611        assert_eq!(
11612            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11613            ":2\r\n"
11614        );
11615        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11616        assert_eq!(
11617            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11618            ":1\r\n"
11619        );
11620        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11621        assert_eq!(
11622            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11623            bulk("1").as_str()
11624        );
11625        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11626        assert_eq!(
11627            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11628            bulk("13").as_str()
11629        );
11630        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11631        assert_eq!(
11632            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11633            ":4\r\n"
11634        );
11635        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11636        assert_eq!(
11637            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11638            bulk("false").as_str()
11639        );
11640        // Every one of them wrote to all three matches, whichever one it chose
11641        // to answer about.
11642        assert_eq!(
11643            f.run(&[b"JSON.GET", b"doc", b".a"]),
11644            bulk("[false,true,false]").as_str()
11645        );
11646
11647        // A match of the wrong kind is skipped rather than being the answer, so
11648        // a path that found a string and then two arrays still answers.
11649        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11650        assert_eq!(
11651            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11652            ":3\r\n"
11653        );
11654        assert_eq!(
11655            f.run(&[b"JSON.GET", b"doc", b".a"]),
11656            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11657        );
11658        // Nothing of the right kind anywhere is the error, and that is the only
11659        // case that is.
11660        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11661        assert_eq!(
11662            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11663            "-ERR Path does not exist or not an array\r\n"
11664        );
11665        assert_eq!(
11666            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11667            "-ERR Path does not exist or not a bool\r\n"
11668        );
11669        // The one array that was there and had nothing in it is an answer and
11670        // not a skip, so the pop answers about it rather than about the array
11671        // after it.
11672        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11673        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11674        assert_eq!(
11675            f.run(&[b"JSON.GET", b"doc", b".a"]),
11676            bulk("[[],[2]]").as_str()
11677        );
11678    }
11679
11680    /// A path that matched a value and something inside that value writes to
11681    /// both, which is what `$..` and a nested wildcard are for.
11682    #[test]
11683    fn a_write_reaches_a_match_that_sits_inside_another_match() {
11684        let mut f = Fixture::new();
11685        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11686
11687        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11688        assert_eq!(
11689            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11690            "*3\r\n:3\r\n:2\r\n:3\r\n"
11691        );
11692        assert_eq!(
11693            f.run(&[b"JSON.GET", b"doc", b"$"]),
11694            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11695        );
11696
11697        // The same for a trim, where the outer array keeps the two elements the
11698        // inner writes landed in.
11699        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11700        assert_eq!(
11701            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11702            "*3\r\n:1\r\n:1\r\n:1\r\n"
11703        );
11704        assert_eq!(
11705            f.run(&[b"JSON.GET", b"doc", b"$"]),
11706            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11707        );
11708
11709        // And for a number, where the first match is the object the outer array
11710        // holds and only the two inside it are numbers.
11711        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11712        assert_eq!(
11713            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11714            bulk("[null,8,8]").as_str()
11715        );
11716    }
11717
11718    /// The value a write is given is looked at only once the path has found
11719    /// something of the right kind to use it on.
11720    #[test]
11721    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11722        let mut f = Fixture::new();
11723        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11724
11725        // A string is not a number, so the path answers first and the `"x"` is
11726        // never looked at. Same for the value that is not JSON at all.
11727        assert_eq!(
11728            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11729            bulk("[null]").as_str()
11730        );
11731        assert_eq!(
11732            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11733            bulk("[null]").as_str()
11734        );
11735        assert_eq!(
11736            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11737            bulk("[]").as_str()
11738        );
11739        assert_eq!(
11740            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11741            "-ERR Path does not exist or does not contains a number\r\n"
11742        );
11743        // A number match anywhere and the value is looked at after all.
11744        assert_eq!(
11745            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11746            "-bad input number\r\n"
11747        );
11748
11749        // JSON.STRAPPEND follows the same order with its own two answers.
11750        assert_eq!(
11751            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11752            "*1\r\n$-1\r\n"
11753        );
11754        assert_eq!(
11755            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11756            "-ERR Path does not exist or not a string\r\n"
11757        );
11758        assert_eq!(
11759            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11760            "-WRONGTYPE wrong type of path value - expected string\r\n"
11761        );
11762
11763        // A key that is not there still comes before either of them.
11764        assert_eq!(
11765            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11766            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11767        );
11768        assert_eq!(
11769            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11770            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11771        );
11772    }
11773
11774    /// RFC 7386 in one test: a null deletes, everything else merges, and a
11775    /// patch that is not an object replaces what it lands on.
11776    #[test]
11777    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11778        let mut f = Fixture::new();
11779
11780        // A key that is not there is created at the root, nulls and all,
11781        // because a deletion with nothing to delete is still what the client
11782        // sent.
11783        assert_eq!(
11784            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11785            "+OK\r\n"
11786        );
11787        assert_eq!(
11788            f.run(&[b"JSON.GET", b"doc", b"$"]),
11789            bulk(r#"[{"x":null,"y":1}]"#).as_str()
11790        );
11791
11792        // Onto something that is there, a null deletes the member of that name
11793        // and the rest is merged one level at a time.
11794        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11795        assert_eq!(
11796            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11797            "+OK\r\n"
11798        );
11799        assert_eq!(
11800            f.run(&[b"JSON.GET", b"doc", b"$"]),
11801            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11802        );
11803
11804        // A patch that is not an object replaces what it is merged onto.
11805        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11806        assert_eq!(
11807            f.run(&[b"JSON.GET", b"doc", b"$"]),
11808            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11809        );
11810
11811        // A patch object onto a value that is not an object starts from an
11812        // empty object, so this time the null has nothing to delete and is
11813        // dropped rather than stored.
11814        assert_eq!(
11815            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11816            "+OK\r\n"
11817        );
11818        assert_eq!(
11819            f.run(&[b"JSON.GET", b"doc", b"$"]),
11820            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11821        );
11822
11823        // A member one level past the end of the document is created and keeps
11824        // its nulls, two levels past it is a write that did not happen, and a
11825        // path that would have to invent where it goes is the unprefixed line.
11826        assert_eq!(
11827            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11828            "+OK\r\n"
11829        );
11830        assert_eq!(
11831            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11832            bulk(r#"[{"z":null}]"#).as_str()
11833        );
11834        assert_eq!(
11835            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11836            "$-1\r\n"
11837        );
11838        assert_eq!(
11839            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11840            "-Err wrong static path\r\n"
11841        );
11842
11843        // A wildcard merges every match.
11844        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11845        assert_eq!(
11846            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11847            "+OK\r\n"
11848        );
11849        assert_eq!(
11850            f.run(&[b"JSON.GET", b"doc", b"$"]),
11851            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11852        );
11853
11854        // The three ways to get it wrong.
11855        assert_eq!(
11856            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11857            "-ERR syntax error\r\n"
11858        );
11859        assert_eq!(
11860            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11861            "-ERR new objects must be created at the root\r\n"
11862        );
11863        f.run(&[b"SET", b"str", b"x"]);
11864        assert_eq!(
11865            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11866            "-Existing key has wrong Redis type\r\n"
11867        );
11868    }
11869
11870    /// A descent is the one path that matches a value and something inside that
11871    /// same value, and the inner merge has to survive the outer one.
11872    #[test]
11873    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11874        let mut f = Fixture::new();
11875        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11876        assert_eq!(
11877            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11878            "+OK\r\n"
11879        );
11880        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
11881        // merged onto the result, so the `{"m":1}` written into `a.b` is still
11882        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
11883        assert_eq!(
11884            f.run(&[b"JSON.GET", b"doc", b"$"]),
11885            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11886        );
11887
11888        // A deletion down the same path, which is the case where the inner
11889        // merge empties the object the outer one then copies.
11890        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11891        assert_eq!(
11892            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11893            "+OK\r\n"
11894        );
11895        assert_eq!(
11896            f.run(&[b"JSON.GET", b"doc", b"$"]),
11897            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11898        );
11899    }
11900
11901    /// A filter is a selector like any other, so every command that takes a path
11902    /// takes one, reads and writes alike.
11903    #[test]
11904    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11905        let mut f = Fixture::new();
11906        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11907        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11908
11909        assert_eq!(
11910            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11911            bulk(r#"["a","c"]"#).as_str()
11912        );
11913        // `$` inside the expression is the document, so a member can be measured
11914        // against something that is not inside it.
11915        assert_eq!(
11916            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11917            bulk(r#"["a","c"]"#).as_str()
11918        );
11919        // The legacy syntax takes one too, and answers the first match.
11920        assert_eq!(
11921            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11922            bulk(r#""a""#).as_str()
11923        );
11924        assert_eq!(
11925            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11926            "*1\r\n$6\r\nobject\r\n"
11927        );
11928
11929        // A write goes through it as far as a value that is already there. A
11930        // field that is not there yet has nowhere definite to go, which is the
11931        // same refusal a wildcard gets.
11932        assert_eq!(
11933            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11934            bulk("[9,10]").as_str()
11935        );
11936        assert_eq!(
11937            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11938            "+OK\r\n"
11939        );
11940        assert_eq!(
11941            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11942            "-Err wrong static path\r\n"
11943        );
11944        assert_eq!(
11945            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11946            ":2\r\n"
11947        );
11948        assert_eq!(
11949            f.run(&[b"JSON.GET", b"doc", b"$"]),
11950            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11951        );
11952
11953        // A path that does not parse is refused before the document is read, so
11954        // a key that is not there answers the same way.
11955        assert!(
11956            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11957                .starts_with("-ERR")
11958        );
11959        assert!(
11960            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11961                .starts_with("-ERR")
11962        );
11963    }
11964
11965    /// The operators past the comparisons, over the wire rather than in the
11966    /// parser's own tests, so that a client can reach all of them.
11967    #[test]
11968    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11969        let mut f = Fixture::new();
11970        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11971        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11972
11973        for (path, want) in [
11974            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11975            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11976            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11977            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11978            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11979            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11980            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11981            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11982            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11983            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11984            (b"$.box[?(@.n~)].t", "[]"),
11985            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11986            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11987            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11988            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11989        ] {
11990            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11991        }
11992
11993        // A write goes through one of these the same way it goes through a
11994        // comparison.
11995        assert_eq!(
11996            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11997            "+OK\r\n"
11998        );
11999        assert_eq!(
12000            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
12001            bulk(r#"["b"]"#).as_str()
12002        );
12003    }
12004
12005    /// D-41. RedisJSON refuses this one, and which document it refuses is
12006    /// decided by how it happens to hold an array of numbers.
12007    #[test]
12008    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
12009        let mut f = Fixture::new();
12010        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
12011        assert_eq!(
12012            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12013            "+OK\r\n"
12014        );
12015        assert_eq!(
12016            f.run(&[b"JSON.GET", b"doc", b"$"]),
12017            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
12018        );
12019        // The same document with one element that is not an integer is the one
12020        // RedisJSON is happy with, and it goes the same way here.
12021        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
12022        assert_eq!(
12023            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12024            "+OK\r\n"
12025        );
12026        assert_eq!(
12027            f.run(&[b"JSON.GET", b"doc", b"$"]),
12028            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
12029        );
12030    }
12031
12032    /// `JSON.MSET` checks what it can before it writes anything and skips the
12033    /// one thing it cannot, which is a path with nowhere to put its value.
12034    #[test]
12035    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
12036        let mut f = Fixture::new();
12037        assert_eq!(
12038            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
12039            "+OK\r\n"
12040        );
12041        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
12042        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
12043
12044        // A repeated key takes the last write.
12045        assert_eq!(
12046            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
12047            "+OK\r\n"
12048        );
12049        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
12050
12051        // A triple whose path names nowhere is skipped, the others are still
12052        // written and the reply turns into a nil. Both ways round, because a
12053        // loop that gave up at the first skip would agree with this on one
12054        // order and not on the other.
12055        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
12056        assert_eq!(
12057            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
12058            "$-1\r\n"
12059        );
12060        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
12061        assert_eq!(
12062            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
12063            "$-1\r\n"
12064        );
12065        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12066
12067        // A value that is not JSON, a key holding something else and a path
12068        // that would have to create a document below its own root are all
12069        // checked before anything is written, so the good triple next to them
12070        // does not happen either.
12071        f.run(&[b"SET", b"str", b"x"]);
12072        assert_eq!(
12073            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
12074            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
12075        );
12076        assert_eq!(
12077            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
12078            "-Existing key has wrong Redis type\r\n"
12079        );
12080        assert_eq!(
12081            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
12082            "-ERR new objects must be created at the root\r\n"
12083        );
12084        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
12085
12086        // The two errors a path can be are checked up front as well, so the
12087        // triple before them is not written either. A wildcard that matched
12088        // nothing has nowhere to invent, and an index that is not in the array
12089        // is out of range, and both of them stop the whole command.
12090        assert_eq!(
12091            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
12092            "-Err wrong static path\r\n"
12093        );
12094        assert_eq!(
12095            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
12096            "-ERR array index out of range\r\n"
12097        );
12098        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12099
12100        // Every triple is worked out against the keyspace as the command found
12101        // it, so a second triple on the same key does not see the first one and
12102        // the last write is the one that stays.
12103        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
12104        assert_eq!(
12105            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
12106            "+OK\r\n"
12107        );
12108        assert_eq!(
12109            f.run(&[b"JSON.GET", b"c", b"$"]),
12110            bulk(r#"[{"n":3}]"#).as_str()
12111        );
12112
12113        // An argument count that is not a run of key, path and value is the
12114        // arity error rather than a syntax one.
12115        assert_eq!(
12116            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
12117            "-ERR wrong number of arguments for 'json.mset' command\r\n"
12118        );
12119    }
12120
12121    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
12122    /// an empty array and an empty object apart.
12123    #[test]
12124    fn json_resp_answers_the_document_as_resp_types() {
12125        let mut f = Fixture::new();
12126        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
12127        assert_eq!(
12128            f.run(&[b"JSON.RESP", b"doc"]),
12129            "*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"
12130        );
12131        // A JSONPath wraps the same answer in one more array.
12132        assert_eq!(
12133            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
12134            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
12135        );
12136
12137        f.run(&[
12138            b"JSON.SET",
12139            b"doc",
12140            b"$",
12141            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
12142        ]);
12143        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
12144        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
12145        // A double goes out as its text, so a client reads the same digits
12146        // `JSON.GET` would have given it.
12147        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
12148        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
12149        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
12150
12151        // A missing legacy path is an error, a missing JSONPath is an empty
12152        // array, and a key that is not there is a nil on either.
12153        assert_eq!(
12154            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
12155            "-ERR Path does not exist\r\n"
12156        );
12157        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
12158        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
12159        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
12160    }
12161
12162    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
12163    /// pins the shapes and that the two syntaxes agree rather than a number
12164    /// read off another server. That is D-42.
12165    #[test]
12166    fn json_debug_answers_a_byte_count_and_its_own_help() {
12167        let mut f = Fixture::new();
12168        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
12169        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
12170        assert!(one.starts_with(':'), "{one}");
12171        assert_eq!(
12172            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
12173            format!("*1\r\n{one}")
12174        );
12175        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
12176        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
12177
12178        // A key that is not there is a zero on a legacy path and an empty set
12179        // on a JSONPath, which is the one reader here that does not answer nil
12180        // for it.
12181        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
12182        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
12183        assert_eq!(
12184            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
12185            "-ERR Path does not exist\r\n"
12186        );
12187        assert_eq!(
12188            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
12189            "*0\r\n"
12190        );
12191
12192        assert_eq!(
12193            f.run(&[b"JSON.DEBUG", b"HELP"]),
12194            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
12195             $34\r\nHELP                - this message\r\n"
12196        );
12197        assert_eq!(
12198            f.run(&[b"JSON.DEBUG", b"NOPE"]),
12199            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
12200        );
12201        assert_eq!(
12202            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
12203            "-ERR wrong number of arguments for 'json.debug' command\r\n"
12204        );
12205    }
12206
12207    // ---------------------------------------------------------------- vector
12208
12209    /// The first `VADD` fixes the dimension and every one after it has to
12210    /// agree, because there is no create command to say it earlier.
12211    #[test]
12212    fn the_first_vadd_decides_how_wide_the_set_is() {
12213        let mut f = Fixture::new();
12214        assert_eq!(
12215            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
12216            ":1\r\n"
12217        );
12218        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12219        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12220        // A second vector under the same name replaces it and says so with a
12221        // zero, so an ingest can count what it created.
12222        assert_eq!(
12223            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
12224            ":0\r\n"
12225        );
12226        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12227        // Three dimensions into a two dimensional set names both numbers, since
12228        // a client that gets this wrong needs to know which end is which.
12229        assert_eq!(
12230            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
12231            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
12232        );
12233        // A vector of zeros has no direction, and it is taken anyway and comes
12234        // back as the origin, because that is what a real server does with it.
12235        assert_eq!(
12236            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
12237            ":1\r\n"
12238        );
12239        assert_eq!(
12240            f.run(&[b"VEMB", b"v", b"nowhere"]),
12241            "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
12242        );
12243        // A set is made with one quantisation and keeps it, and a `VADD` that
12244        // names another is refused. Naming none names `Q8`, which is why this
12245        // set is a `Q8` one.
12246        assert_eq!(
12247            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
12248            "-ERR asked quantization mismatch with existing vector set\r\n"
12249        );
12250        // Nothing above created a key, and a set that never took a vector has
12251        // no dimension to report.
12252        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
12253        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
12254        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
12255    }
12256
12257    /// What a client sent comes back out, and what a client asked for is a
12258    /// similarity and not the distance underneath it.
12259    #[test]
12260    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
12261        let mut f = Fixture::new();
12262        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
12263        // The set stored the direction and the length is multiplied back on the
12264        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
12265        // either, because nobody named a quantisation and that means `Q8`: the
12266        // wider coordinate lands on a code exactly and the other one does not.
12267        // Both numbers are a real server's answers for the same input.
12268        assert_eq!(
12269            f.run(&[b"VEMB", b"v", b"a"]),
12270            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12271        );
12272        // NOQUANT is the way to ask for what went in to come back out.
12273        f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
12274        assert_eq!(
12275            f.run(&[b"VEMB", b"n", b"a"]),
12276            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12277        );
12278        // BIN keeps the signs and nothing else, and does not multiply the
12279        // length back on, since a sign has no length in it to scale.
12280        f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
12281        assert_eq!(
12282            f.run(&[b"VEMB", b"b", b"a"]),
12283            "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
12284        );
12285        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
12286        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
12287
12288        // On the axes, where the unit vector is exact and so is the dot
12289        // product, both ends of the scale come out exact: the same direction is
12290        // 1 and the opposite one is 0, with a right angle at a half.
12291        let mut f = Fixture::new();
12292        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
12293        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
12294        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
12295        assert_eq!(
12296            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
12297            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
12298             $8\r\nopposite\r\n$1\r\n0\r\n"
12299        );
12300        // A search from an element leaves that element out, since it is always
12301        // its own nearest neighbour.
12302        assert_eq!(
12303            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
12304            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12305        );
12306        // An element that is not there is an empty answer and not an error,
12307        // which is what a missing key gives too.
12308        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
12309        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
12310        // COUNT bounds it and TRUTH reads every vector rather than the codes,
12311        // which has to agree with the index on a set this small.
12312        assert_eq!(
12313            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
12314            "*1\r\n$6\r\nacross\r\n"
12315        );
12316        assert_eq!(
12317            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
12318            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12319        );
12320        // EF widens how much of the index is read and does not change how many
12321        // answers come back, so a wide search still returns what COUNT asked
12322        // for.
12323        assert_eq!(
12324            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
12325            "*1\r\n$6\r\nacross\r\n"
12326        );
12327
12328        // On RESP3 a scored search is a map, which is what the vector set
12329        // module replies and is not what ZRANGE does here.
12330        let mut g = Fixture::new();
12331        g.run(&[b"HELLO", b"3"]);
12332        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12333        assert_eq!(
12334            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
12335            "%1\r\n$4\r\neast\r\n,1\r\n"
12336        );
12337    }
12338
12339    /// The attribute pair, and the one reply that means two things.
12340    #[test]
12341    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
12342        let mut f = Fixture::new();
12343        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12344        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12345        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
12346        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
12347        // Not parsed as JSON, because nothing reads into it yet and refusing a
12348        // write for a rule nothing enforces would be the wrong trade.
12349        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
12350        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
12351        // An empty string clears it, which is Redis's spelling of the removal.
12352        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
12353        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12354        // An element that is not there answers zero rather than being created,
12355        // since an attribute with no vector under it is not a thing this holds.
12356        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
12357        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
12358        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
12359        // A null for an element with no attribute and a null for one that is
12360        // not there. VISMEMBER is how a client tells the two apart.
12361        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
12362        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
12363        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
12364        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
12365
12366        // WITHATTRIBS carries it alongside the answers.
12367        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12368        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12369        assert_eq!(
12370            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
12371            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
12372        );
12373    }
12374
12375    /// The slot a removed element had is reused, and nothing that was beside it
12376    /// comes back with the next element to get it.
12377    #[test]
12378    fn vrem_takes_the_attribute_with_it() {
12379        let mut f = Fixture::new();
12380        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12381        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12382        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
12383        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
12384        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
12385        // The key went with the last element, the way every other collection
12386        // here works.
12387        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12388
12389        // The next element is given the slot the removed one had, and it comes
12390        // with no attribute on it.
12391        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12392        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12393        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12394        f.run(&[b"VREM", b"v", b"east"]);
12395        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
12396        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
12397    }
12398
12399    /// `VINFO` says what the index is before it says anything a client could
12400    /// mistake for a graph.
12401    #[test]
12402    fn vinfo_says_partition_first() {
12403        let mut f = Fixture::new();
12404        f.run(&[
12405            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12406        ]);
12407        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12408        let info = f.run(&[b"VINFO", b"v"]);
12409        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12410        // What the client asked for and not what happened to the tuning, which
12411        // is `10` section 7: M is recorded and changes nothing.
12412        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12413        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12414        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12415        // Nobody named a quantisation, so this set is a `Q8` one and every
12416        // element in it is stored that way.
12417        assert!(
12418            info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12419            "{info}"
12420        );
12421        let mut f = Fixture::new();
12422        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12423        assert!(
12424            f.run(&[b"VINFO", b"v"])
12425                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12426        );
12427        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12428    }
12429
12430    /// A set to read ranges of names out of.
12431    fn named() -> Fixture {
12432        let mut f = Fixture::new();
12433        for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12434            .iter()
12435            .enumerate()
12436        {
12437            let x = (i + 1).to_string();
12438            f.run(&[
12439                b"VADD",
12440                b"r",
12441                b"VALUES",
12442                b"2",
12443                x.as_bytes(),
12444                b"1",
12445                name.as_bytes(),
12446            ]);
12447        }
12448        f
12449    }
12450
12451    /// `VRANGE` reads the names in the order bytes come in and pays no
12452    /// attention to where the vectors point.
12453    #[test]
12454    fn vrange_walks_the_names_and_not_the_vectors() {
12455        let mut f = named();
12456        assert_eq!(
12457            f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12458            "*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"
12459        );
12460        assert_eq!(
12461            f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12462            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12463            "the high end is a name and not a prefix, so delta is past it"
12464        );
12465        assert_eq!(
12466            f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12467            "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12468        );
12469        assert_eq!(
12470            f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12471            "*1\r\n$4\r\nbeta\r\n"
12472        );
12473        assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12474        // Bytes and not letters, so an upper case name sorts before every lower
12475        // case one rather than beside its own spelling.
12476        f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12477        assert_eq!(
12478            f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12479            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12480        );
12481        assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12482    }
12483
12484    /// The count cuts the answer after the range is decided, and zero is not
12485    /// the same as leaving it out.
12486    #[test]
12487    fn a_vrange_count_of_zero_asks_for_nothing() {
12488        let mut f = named();
12489        assert_eq!(
12490            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12491            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12492        );
12493        assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12494        assert!(
12495            f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12496                .starts_with("*5\r\n"),
12497            "a negative count is no limit at all"
12498        );
12499    }
12500
12501    /// Both ends are read before either is placed, and the count is read before
12502    /// either end.
12503    #[test]
12504    fn vrange_says_which_end_it_could_not_read() {
12505        let mut f = named();
12506        assert_eq!(
12507            f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12508            "-ERR invalid start range format\r\n"
12509        );
12510        assert_eq!(
12511            f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12512            "-ERR invalid end range format\r\n",
12513            "the high end is spelled wrong, which is worth saying before the \
12514             low end being on the wrong side"
12515        );
12516        assert_eq!(
12517            f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12518            "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12519        );
12520        // A bracket with nothing after it is not the empty name here, though an
12521        // element really can be called that.
12522        assert_eq!(
12523            f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12524            "-ERR invalid start range format\r\n"
12525        );
12526        assert_eq!(
12527            f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12528            "-ERR invalid COUNT value\r\n"
12529        );
12530        assert_eq!(
12531            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12532            "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12533        );
12534        f.run(&[b"SET", b"s", b"x"]);
12535        assert!(
12536            f.run(&[b"VRANGE", b"s", b"-", b"+"])
12537                .starts_with("-WRONGTYPE")
12538        );
12539    }
12540
12541    /// The option that asks for something this index does not have says so
12542    /// rather than doing something else quietly.
12543    #[test]
12544    fn reduce_is_refused_and_not_ignored() {
12545        let mut f = Fixture::new();
12546        let reduce = f.run(&[
12547            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12548        ]);
12549        assert!(
12550            reduce.starts_with("-ERR REDUCE is not supported."),
12551            "{reduce}"
12552        );
12553        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12554    }
12555
12556    /// A filtered search answers with the nearest elements that match, and an
12557    /// expression that is not one is an error before the key is looked at.
12558    #[test]
12559    fn vsim_filter_reads_the_attributes() {
12560        let mut f = Fixture::new();
12561        for (name, x, y, attr) in [
12562            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12563            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12564            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12565            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12566        ] {
12567            f.run(&[
12568                b"VADD",
12569                b"v",
12570                b"VALUES",
12571                b"2",
12572                x.as_bytes(),
12573                y.as_bytes(),
12574                name.as_bytes(),
12575                b"SETATTR",
12576                attr.as_bytes(),
12577            ]);
12578        }
12579        // `b` is the nearest to the query and is the one the filter drops, so
12580        // this is the answer a filter applied afterwards would have got wrong.
12581        assert_eq!(
12582            f.run(&[
12583                b"VSIM",
12584                b"v",
12585                b"VALUES",
12586                b"2",
12587                b"9",
12588                b"1",
12589                b"COUNT",
12590                b"2",
12591                b"FILTER",
12592                b".lang == \"en\"",
12593            ]),
12594            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12595        );
12596        // A number is compared as a number, and the two halves of an `and` both
12597        // have to hold.
12598        assert_eq!(
12599            f.run(&[
12600                b"VSIM",
12601                b"v",
12602                b"VALUES",
12603                b"2",
12604                b"9",
12605                b"1",
12606                b"FILTER",
12607                b".lang == 'en' and .year > 1980",
12608            ]),
12609            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12610        );
12611        // A list, and a field an element does not have.
12612        assert_eq!(
12613            f.run(&[
12614                b"VSIM",
12615                b"v",
12616                b"VALUES",
12617                b"2",
12618                b"9",
12619                b"1",
12620                b"FILTER",
12621                b".lang in ['fr', 'de']",
12622            ]),
12623            "*1\r\n$1\r\nb\r\n"
12624        );
12625        assert_eq!(
12626            f.run(&[
12627                b"VSIM",
12628                b"v",
12629                b"VALUES",
12630                b"2",
12631                b"9",
12632                b"1",
12633                b"FILTER",
12634                b".rating > 3"
12635            ]),
12636            "*0\r\n"
12637        );
12638        // TRUTH measures every vector, and the filter still decides which ones
12639        // are measured.
12640        assert_eq!(
12641            f.run(&[
12642                b"VSIM",
12643                b"v",
12644                b"VALUES",
12645                b"2",
12646                b"9",
12647                b"1",
12648                b"TRUTH",
12649                b"FILTER",
12650                b".year < 1980",
12651            ]),
12652            "*1\r\n$1\r\nc\r\n"
12653        );
12654        // VSETATTR moves an element in and out of a filter, which means the tag
12655        // beside its code was rewritten and not just the string.
12656        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12657        assert_eq!(
12658            f.run(&[
12659                b"VSIM",
12660                b"v",
12661                b"VALUES",
12662                b"2",
12663                b"9",
12664                b"1",
12665                b"COUNT",
12666                b"1",
12667                b"FILTER",
12668                b".lang == \"en\"",
12669            ]),
12670            "*1\r\n$1\r\nb\r\n"
12671        );
12672        // And a VADD that replaces the vector keeps the attribute and the tag,
12673        // which is the same rewrite from the other end.
12674        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12675        assert_eq!(
12676            f.run(&[
12677                b"VSIM",
12678                b"v",
12679                b"VALUES",
12680                b"2",
12681                b"9",
12682                b"1",
12683                b"COUNT",
12684                b"1",
12685                b"FILTER",
12686                b".lang == \"en\"",
12687            ]),
12688            "*1\r\n$1\r\nb\r\n"
12689        );
12690
12691        // The expression is parsed before the key is read, so a bad one is an
12692        // error whether or not the key is there.
12693        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12694        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12695        assert_eq!(
12696            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12697            "-ERR invalid FILTER expression\r\n"
12698        );
12699        // FILTER-EF raises the effort rather than capping it, and zero is
12700        // Redis's word for no limit, so neither is an error.
12701        assert_eq!(
12702            f.run(&[
12703                b"VSIM",
12704                b"v",
12705                b"VALUES",
12706                b"2",
12707                b"9",
12708                b"1",
12709                b"COUNT",
12710                b"1",
12711                b"FILTER-EF",
12712                b"500",
12713                b"FILTER",
12714                b".lang == 'en'",
12715            ]),
12716            "*1\r\n$1\r\nb\r\n"
12717        );
12718        assert_eq!(
12719            f.run(&[
12720                b"VSIM",
12721                b"v",
12722                b"VALUES",
12723                b"2",
12724                b"9",
12725                b"1",
12726                b"COUNT",
12727                b"1",
12728                b"FILTER-EF",
12729                b"0"
12730            ]),
12731            "*1\r\n$1\r\nb\r\n"
12732        );
12733        assert_eq!(
12734            f.run(&[
12735                b"VSIM",
12736                b"v",
12737                b"VALUES",
12738                b"2",
12739                b"9",
12740                b"1",
12741                b"FILTER-EF",
12742                b"lots"
12743            ]),
12744            "-ERR EF must be a positive integer\r\n"
12745        );
12746    }
12747
12748    /// A vector set key is a key, so the keyspace owns it the way it owns every
12749    /// other one and none of those commands know what is inside it.
12750    #[test]
12751    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12752        let mut f = Fixture::new();
12753        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12754        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12755        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12756        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12757        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12758        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12759        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12760        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12761        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12762        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12763        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12764
12765        // And the wrong type is the wrong type in both directions.
12766        f.run(&[b"SET", b"s", b"1"]);
12767        assert_eq!(
12768            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12769            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12770        );
12771        assert_eq!(
12772            f.run(&[b"VCARD", b"s"]),
12773            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12774        );
12775        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12776        assert_eq!(
12777            f.run(&[b"GET", b"v"]),
12778            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12779        );
12780        // A graph and a vector set share the escape in the record tag and are
12781        // still two different types, which is the case the tag alone cannot
12782        // decide.
12783        f.run(&[b"G.NADD", b"social", b"ada"]);
12784        assert_eq!(
12785            f.run(&[b"VCARD", b"social"]),
12786            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12787        );
12788        assert_eq!(
12789            f.run(&[b"G.NGET", b"v", b"ada"]),
12790            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12791        );
12792    }
12793
12794    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
12795    /// shapes, off the database's own generator.
12796    #[test]
12797    fn vrandmember_has_the_two_shapes_srandmember_has() {
12798        let mut f = Fixture::new();
12799        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12800            let x = (i + 1).to_string();
12801            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12802        }
12803        // One element is a bulk string and not an array of one.
12804        let one = f.run(&[b"VRANDMEMBER", b"v"]);
12805        assert!(one.starts_with("$1\r\n"), "{one}");
12806        // A positive count is distinct and stops at the size of the set.
12807        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12808        assert!(all.starts_with("*3\r\n"), "{all}");
12809        for name in ["a", "b", "c"] {
12810            assert!(all.contains(name), "{all} is missing {name}");
12811        }
12812        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12813        assert!(all.starts_with("*2\r\n"), "{all}");
12814        // A negative one draws that many and allows repeats.
12815        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12816        assert!(many.starts_with("*5\r\n"), "{many}");
12817        // A key that is not there answers the shape that was asked for.
12818        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12819        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12820    }
12821
12822    /// `VLINKS` answers about the index that is here rather than the graph that
12823    /// is not, which is D-2.
12824    #[test]
12825    fn vlinks_reports_one_layer_of_partition_neighbours() {
12826        let mut f = Fixture::new();
12827        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12828        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12829        // One layer deep, because the index is one layer deep, so a client
12830        // walking layers gets a short list and not a shape it cannot parse.
12831        assert_eq!(
12832            f.run(&[b"VLINKS", b"v", b"east"]),
12833            "*1\r\n*1\r\n$5\r\nnorth\r\n"
12834        );
12835        assert_eq!(
12836            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12837            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12838        );
12839        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12840        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12841    }
12842
12843    /// A vector arrives either as digits or as bytes, and the two have to mean
12844    /// the same thing.
12845    #[test]
12846    fn fp32_and_values_are_the_same_vector() {
12847        let mut f = Fixture::new();
12848        let mut blob = Vec::new();
12849        for x in [3.0f32, 4.0] {
12850            blob.extend_from_slice(&x.to_le_bytes());
12851        }
12852        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12853        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12854        assert_eq!(
12855            f.run(&[b"VEMB", b"v", b"a"]),
12856            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12857        );
12858        // RAW is the stored bytes and the numbers that turn them back into the
12859        // client's vector, which for `Q8` is a code a coordinate, the length the
12860        // vector arrived with and the scale the codes are measured against. The
12861        // name of the form is a simple string, which is a real server's shape,
12862        // and all four of these are a real server's answers.
12863        assert_eq!(
12864            f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12865            "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12866        );
12867        // A blob that is not a whole number of floats is not a vector.
12868        assert_eq!(
12869            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12870            "-ERR invalid vector specification\r\n"
12871        );
12872        // Neither is a count that promises more than arrived.
12873        assert_eq!(
12874            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12875            "-ERR syntax error\r\n"
12876        );
12877        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12878    }
12879
12880    // ----------------------------------------------------------------- bloom
12881
12882    /// The filter a client gets when it does not describe one, and the two
12883    /// answers an add can give.
12884    #[test]
12885    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12886        let mut f = Fixture::new();
12887        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12888        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12889        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12890        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12891        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12892        // The defaults are the module's configs and not anything the command
12893        // said, which is 100 entries at a hundredth and a growth of 2.
12894        assert_eq!(
12895            f.run(&[b"BF.INFO", b"b"]),
12896            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12897             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12898             +Expansion rate\r\n:2\r\n"
12899        );
12900        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12901        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12902        // A key that is not there has no filter to report on, and answers two
12903        // different ways about it depending on which command asked.
12904        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12905        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12906    }
12907
12908    /// `BF.EXISTS` on a key holding something else answers a miss, and
12909    /// everything else in the family answers `WRONGTYPE`.
12910    ///
12911    /// The two halves of a check and set disagree about what that key is, which
12912    /// is the module's behaviour and not a decision taken here.
12913    #[test]
12914    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12915        let mut f = Fixture::new();
12916        f.run(&[b"SET", b"s", b"text"]);
12917        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12918        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12919        for cmd in [
12920            vec![&b"BF.ADD"[..], b"s", b"x"],
12921            vec![&b"BF.MADD"[..], b"s", b"x"],
12922            vec![&b"BF.CARD"[..], b"s"],
12923            vec![&b"BF.INFO"[..], b"s"],
12924            vec![&b"BF.DEBUG"[..], b"s"],
12925            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12926        ] {
12927            let name = String::from_utf8_lossy(cmd[0]).into_owned();
12928            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12929        }
12930        // The arguments are read before the key is, so a reserve with a bad
12931        // error rate complains about the rate and never learns about the string.
12932        assert_eq!(
12933            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12934            "-ERR bad error rate\r\n"
12935        );
12936        assert!(
12937            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12938                .starts_with("-WRONGTYPE")
12939        );
12940    }
12941
12942    /// A chain grows by its expansion factor and each link is half as wrong as
12943    /// the one before, which is what makes the whole filter hold its rate.
12944    #[test]
12945    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12946        let mut f = Fixture::new();
12947        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12948        for i in 0..10u32 {
12949            assert_eq!(
12950                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12951                ":1\r\n"
12952            );
12953        }
12954        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12955        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12956        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12957        // Capacity is the sum of every link and not the number that was asked
12958        // for, so it is 10 and then 10 plus 20.
12959        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12960        assert_eq!(
12961            f.run(&[b"BF.DEBUG", b"g"]),
12962            "*3\r\n$7\r\nsize:11\r\n\
12963             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12964             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12965        );
12966
12967        // The same filter told not to grow fills instead.
12968        assert_eq!(
12969            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12970            "+OK\r\n"
12971        );
12972        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12973        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12974        assert_eq!(
12975            f.run(&[b"BF.ADD", b"n", b"c"]),
12976            "-ERR non scaling filter is full\r\n"
12977        );
12978        // And an item that is already in it still answers, because membership
12979        // is checked before fullness.
12980        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12981        // A filter that will not grow has no expansion rate to report, in
12982        // either of the two spellings that make one.
12983        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12984        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12985        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12986        // Asking for both at once is refused, which is one of the module's
12987        // errors that carries no prefix at all.
12988        assert_eq!(
12989            f.run(&[
12990                b"BF.RESERVE",
12991                b"q",
12992                b"0.01",
12993                b"2",
12994                b"NONSCALING",
12995                b"EXPANSION",
12996                b"2"
12997            ]),
12998            "-Nonscaling filters cannot expand\r\n"
12999        );
13000    }
13001
13002    /// A multi add stops where the filter did, so the reply can be shorter than
13003    /// the argument list.
13004    #[test]
13005    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
13006        let mut f = Fixture::new();
13007        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
13008        assert_eq!(
13009            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
13010            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
13011        );
13012        assert_eq!(
13013            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
13014            "*2\r\n:1\r\n:0\r\n"
13015        );
13016    }
13017
13018    /// `BF.INSERT` describes a filter and fills it in one command, with its own
13019    /// spelling of every complaint.
13020    #[test]
13021    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
13022        let mut f = Fixture::new();
13023        assert_eq!(
13024            f.run(&[
13025                b"BF.INSERT",
13026                b"i",
13027                b"CAPACITY",
13028                b"50",
13029                b"ERROR",
13030                b"0.001",
13031                b"ITEMS",
13032                b"a",
13033                b"b"
13034            ]),
13035            "*2\r\n:1\r\n:1\r\n"
13036        );
13037        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
13038        // NOCREATE is the only way to add without making the key.
13039        assert_eq!(
13040            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13041            "-ERR not found\r\n"
13042        );
13043        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13044        // The same mistakes as BF.RESERVE, in the sentences this command uses
13045        // for them, and one sentence where BF.RESERVE has two.
13046        assert_eq!(
13047            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13048            "-Bad capacity\r\n"
13049        );
13050        assert_eq!(
13051            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
13052            "-Bad error rate\r\n"
13053        );
13054        assert_eq!(
13055            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
13056            "-Bad expansion\r\n"
13057        );
13058        // An option is matched on its first letter and not on the word, so a
13059        // token nobody meant as an option is one anyway if it starts with the
13060        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
13061        // builds says so.
13062        assert_eq!(
13063            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
13064            "*1\r\n:1\r\n"
13065        );
13066        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
13067        // Only E and N need a second look, one for ERROR against EXPANSION and
13068        // the other for NOCREATE against NONSCALING, and both stop as soon as
13069        // they can tell the two apart.
13070        assert_eq!(
13071            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
13072            "*1\r\n:1\r\n"
13073        );
13074        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
13075        assert_eq!(
13076            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
13077            "*1\r\n:1\r\n"
13078        );
13079        assert_eq!(
13080            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
13081            "-ERR not found\r\n"
13082        );
13083        // A letter that starts nothing is the one case that is refused.
13084        assert_eq!(
13085            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13086            "-Unknown argument received\r\n"
13087        );
13088        // Everything after ITEMS is an item, even when it spells an option.
13089        assert_eq!(
13090            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13091            "*1\r\n:1\r\n"
13092        );
13093        // And ITEMS with nothing after it is the same as leaving it out.
13094        assert!(
13095            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
13096                .contains("wrong number of arguments")
13097        );
13098    }
13099
13100    /// A filter dumped a chunk at a time and put back into another key is the
13101    /// same filter.
13102    #[test]
13103    fn a_dump_replays_into_a_filter_that_answers_the_same() {
13104        let mut f = Fixture::new();
13105        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
13106        for i in 0..25u32 {
13107            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
13108        }
13109        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
13110
13111        // Iterator zero asks for the header and every one after it is a running
13112        // byte offset, and a chunk never spans two links.
13113        let mut iter = b"0".to_vec();
13114        let mut chunks = 0;
13115        loop {
13116            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
13117            let text = String::from_utf8_lossy(&raw).into_owned();
13118            let next = text
13119                .split("\r\n")
13120                .nth(1)
13121                .and_then(|n| n.strip_prefix(':'))
13122                .expect("a two element reply of an iterator and a chunk")
13123                .to_owned();
13124            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13125            let data = &body[body
13126                .windows(2)
13127                .position(|w| w == b"\r\n")
13128                .expect("a length line")
13129                + 2..body.len() - 2];
13130            if next == "0" {
13131                assert!(data.is_empty(), "the last chunk is empty");
13132                break;
13133            }
13134            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
13135            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
13136            iter = next.into_bytes();
13137            chunks += 1;
13138        }
13139        assert_eq!(chunks, 3, "a header and one chunk per link");
13140
13141        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
13142        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
13143        for i in 0..25u32 {
13144            assert_eq!(
13145                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
13146                ":1\r\n"
13147            );
13148        }
13149
13150        // A header on top of a filter is refused rather than merged, and so is
13151        // one that no filter wrote.
13152        assert_eq!(
13153            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
13154            "-ERR received bad data\r\n"
13155        );
13156        assert_eq!(
13157            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
13158            "-ERR received bad data\r\n"
13159        );
13160        // An offset past the end of the filter names itself.
13161        assert_eq!(
13162            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
13163            "-ERR invalid offset - no link found\r\n"
13164        );
13165        assert_eq!(
13166            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
13167            "-ERR Second argument must be numeric\r\n"
13168        );
13169        // The same complaint without the prefix on the way out, which is the
13170        // module's inconsistency and not a slip here.
13171        assert_eq!(
13172            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
13173            "-Second argument must be numeric\r\n"
13174        );
13175    }
13176
13177    /// The argument checks, which have a sentence each and read numbers the way
13178    /// Redis reads them everywhere else.
13179    #[test]
13180    fn reserve_reads_its_numbers_the_way_string2ll_does() {
13181        let mut f = Fixture::new();
13182        for (args, want) in [
13183            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
13184            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
13185            (
13186                vec![&b"0"[..], b"10"],
13187                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13188            ),
13189            (
13190                vec![&b"1"[..], b"10"],
13191                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13192            ),
13193            (
13194                vec![&b"inf"[..], b"10"],
13195                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13196            ),
13197            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
13198            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
13199            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
13200            (
13201                vec![&b"0.01"[..], b"0"],
13202                "-ERR capacity must be in the range [1, 1073741824]\r\n",
13203            ),
13204            (
13205                vec![&b"0.01"[..], b"1073741825"],
13206                "-ERR capacity must be in the range [1, 1073741824]\r\n",
13207            ),
13208        ] {
13209            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
13210            cmd.extend(args.iter().copied());
13211            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
13212        }
13213        assert_eq!(
13214            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
13215            "-ERR no expansion\r\n"
13216        );
13217        assert_eq!(
13218            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
13219            "-ERR bad expansion\r\n"
13220        );
13221        assert_eq!(
13222            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
13223            "-ERR expansion must be in the range [0, 32768]\r\n"
13224        );
13225        // Trailing rubbish after the capacity is ignored rather than refused.
13226        assert_eq!(
13227            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
13228            "+OK\r\n"
13229        );
13230        assert_eq!(
13231            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
13232            "-ERR item exists\r\n"
13233        );
13234        assert_eq!(
13235            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
13236            "-Invalid information value\r\n"
13237        );
13238        assert!(
13239            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
13240                .contains("wrong number of arguments")
13241        );
13242    }
13243
13244    /// The RESP3 shapes, which are where this family differs most from RESP2.
13245    #[test]
13246    fn the_bloom_family_answers_in_resp3_spelling_too() {
13247        let mut f = Fixture::new();
13248        f.out.set_proto(Proto::Resp3);
13249        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
13250        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
13251        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
13252        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
13253        assert_eq!(
13254            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
13255            "*2\r\n#t\r\n#f\r\n"
13256        );
13257        // The count stays an integer, because it counts rather than answers.
13258        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
13259        assert_eq!(
13260            f.run(&[b"BF.INFO", b"b"]),
13261            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
13262             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
13263             +Expansion rate\r\n:2\r\n"
13264        );
13265        // One field is a map of one here and a bare array of one on RESP2, so
13266        // this is the reply where the two protocols carry different facts.
13267        assert_eq!(
13268            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
13269            "%1\r\n+Capacity\r\n:100\r\n"
13270        );
13271    }
13272
13273    // ---------------------------------------------------------------- cuckoo
13274
13275    /// A dump header, which is the four counts and the three widths a filter
13276    /// writes in front of its fingerprints.
13277    ///
13278    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
13279    /// tests below want out of it is the states a filter cannot be put into
13280    /// from the wire.
13281    fn cf_header(
13282        items: u64,
13283        buckets: u64,
13284        deletes: u64,
13285        filters: u64,
13286        geometry: [u16; 3],
13287    ) -> Vec<u8> {
13288        let mut out = Vec::with_capacity(38);
13289        for n in [items, buckets, deletes, filters] {
13290            out.extend_from_slice(&n.to_le_bytes());
13291        }
13292        for n in geometry {
13293            out.extend_from_slice(&n.to_le_bytes());
13294        }
13295        out
13296    }
13297
13298    /// The filter a client gets when it does not describe one, and the thing a
13299    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
13300    /// take them out again.
13301    #[test]
13302    fn cf_add_makes_the_filter_and_counts_the_copies() {
13303        let mut f = Fixture::new();
13304        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13305        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13306        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
13307        // The NX form is the one that looks first, which is why it is a command
13308        // of its own rather than an option.
13309        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
13310        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
13311        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
13312        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
13313        assert_eq!(
13314            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
13315            "*2\r\n:1\r\n:0\r\n"
13316        );
13317        // The defaults are the module's configs: 1024 entries over buckets of
13318        // two, twenty kicks and a chain that grows by one.
13319        assert_eq!(
13320            f.run(&[b"CF.INFO", b"d"]),
13321            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13322             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
13323             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
13324             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13325        );
13326        assert_eq!(
13327            f.run(&[b"CF.DEBUG", b"d"]),
13328            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
13329             max_iterations:20 expansion:1\r\n"
13330        );
13331        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
13332        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13333
13334        // A delete takes one copy, so the same item goes twice and then stops.
13335        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13336        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
13337        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13338        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
13339        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
13340
13341        // A key with no filter under it gets three different sentences and one
13342        // plain miss, depending on which command asked.
13343        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
13344        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
13345        assert_eq!(
13346            f.run(&[b"CF.COMPACT", b"gone"]),
13347            "-Cuckoo filter was not found\r\n"
13348        );
13349        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
13350        // And `CF.COMPACT` is declared as taking any number of keys and takes
13351        // exactly one, which is the module's own arity being wrong rather than
13352        // this table's.
13353        assert!(
13354            f.run(&[b"CF.COMPACT", b"a", b"b"])
13355                .contains("wrong number of arguments")
13356        );
13357    }
13358
13359    /// The four that only read fingerprints treat a key holding something else
13360    /// as a key with no filter, and everything else answers `WRONGTYPE`.
13361    #[test]
13362    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
13363        let mut f = Fixture::new();
13364        f.run(&[b"SET", b"s", b"text"]);
13365        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
13366        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13367        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
13368        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
13369        // and is declared read only, so neither of the two halves of the family
13370        // is the same set as the flags say.
13371        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
13372        assert_eq!(
13373            f.run(&[b"CF.COMPACT", b"s"]),
13374            "-Cuckoo filter was not found\r\n"
13375        );
13376        for cmd in [
13377            vec![&b"CF.ADD"[..], b"s", b"x"],
13378            vec![&b"CF.ADDNX"[..], b"s", b"x"],
13379            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
13380            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
13381            vec![&b"CF.INFO"[..], b"s"],
13382            vec![&b"CF.DEBUG"[..], b"s"],
13383            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
13384            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
13385            vec![&b"CF.RESERVE"[..], b"s", b"64"],
13386        ] {
13387            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13388            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13389        }
13390    }
13391
13392    /// `CF.RESERVE` reads its options by name in an order of its own, and the
13393    /// first pair with a given name is the only one it looks at.
13394    #[test]
13395    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
13396        let mut f = Fixture::new();
13397        assert_eq!(
13398            f.run(&[
13399                b"CF.RESERVE",
13400                b"r",
13401                b"64",
13402                b"BUCKETSIZE",
13403                b"1",
13404                b"MAXITERATIONS",
13405                b"7",
13406                b"EXPANSION",
13407                b"4"
13408            ]),
13409            "+OK\r\n"
13410        );
13411        assert_eq!(
13412            f.run(&[b"CF.DEBUG", b"r"]),
13413            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13414             max_iterations:7 expansion:4\r\n"
13415        );
13416        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13417
13418        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13419        assert_eq!(
13420            f.run(&[b"CF.RESERVE", b"q", b"1"]),
13421            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13422        );
13423        // The range is the bucket size's and not a constant, so a capacity that
13424        // was fine at two slots a bucket is not at four.
13425        assert_eq!(
13426            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13427            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13428        );
13429        assert_eq!(
13430            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13431            "+OK\r\n"
13432        );
13433
13434        // The capacity is checked last, so a command that is wrong twice
13435        // answers about the option. Which option it answers about is the order
13436        // the module looks for them in and not the order they were written, so
13437        // a bad kick budget wins over a bad bucket size wherever the two sit.
13438        assert_eq!(
13439            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13440            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13441        );
13442        assert_eq!(
13443            f.run(&[
13444                b"CF.RESERVE",
13445                b"q2",
13446                b"64",
13447                b"EXPANSION",
13448                b"xx",
13449                b"BUCKETSIZE",
13450                b"0"
13451            ]),
13452            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13453        );
13454        assert_eq!(
13455            f.run(&[
13456                b"CF.RESERVE",
13457                b"q2",
13458                b"64",
13459                b"MAXITERATIONS",
13460                b"0",
13461                b"BUCKETSIZE",
13462                b"0"
13463            ]),
13464            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13465        );
13466        // A second pair with a name that has already been read is not looked at
13467        // at all, so this one is a filter with buckets of one rather than an
13468        // error about a bucket size of zero.
13469        assert_eq!(
13470            f.run(&[
13471                b"CF.RESERVE",
13472                b"q3",
13473                b"64",
13474                b"BUCKETSIZE",
13475                b"1",
13476                b"BUCKETSIZE",
13477                b"0"
13478            ]),
13479            "+OK\r\n"
13480        );
13481        // A pair nobody knows is dropped, which is the opposite of what
13482        // `CF.INSERT` does with the same mistake.
13483        assert_eq!(
13484            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13485            "+OK\r\n"
13486        );
13487        assert_eq!(
13488            f.run(&[b"CF.DEBUG", b"q4"]),
13489            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13490             max_iterations:20 expansion:1\r\n"
13491        );
13492        // And an option with nothing after it leaves an odd number of them,
13493        // which is an arity error rather than a complaint about the option.
13494        assert!(
13495            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13496                .contains("wrong number of arguments")
13497        );
13498    }
13499
13500    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
13501    /// with `CF.RESERVE` about nothing.
13502    #[test]
13503    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13504        let mut f = Fixture::new();
13505        assert_eq!(
13506            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13507            "*2\r\n:1\r\n:1\r\n"
13508        );
13509        assert_eq!(
13510            f.run(&[b"CF.DEBUG", b"i"]),
13511            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13512             max_iterations:20 expansion:1\r\n"
13513        );
13514        // The NX form has three answers rather than two, which is why it stays
13515        // integers on both protocols.
13516        assert_eq!(
13517            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13518            "*2\r\n:0\r\n:1\r\n"
13519        );
13520        assert_eq!(
13521            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13522            "-ERR not found\r\n"
13523        );
13524        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13525
13526        assert_eq!(
13527            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13528            "-Bad capacity\r\n"
13529        );
13530        // The bucket size cannot be given here, so the range names the config
13531        // that holds it instead of the option `CF.RESERVE` names.
13532        assert_eq!(
13533            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13534            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13535        );
13536        // Every occurrence is checked, which is where this differs from
13537        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
13538        // one is the one that would have been used.
13539        assert_eq!(
13540            f.run(&[
13541                b"CF.INSERT",
13542                b"i",
13543                b"CAPACITY",
13544                b"8",
13545                b"CAPACITY",
13546                b"2",
13547                b"ITEMS",
13548                b"a"
13549            ]),
13550            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13551        );
13552        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
13553        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
13554        // refused.
13555        assert_eq!(
13556            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13557            "*1\r\n:1\r\n"
13558        );
13559        assert_eq!(
13560            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13561            "*1\r\n:1\r\n"
13562        );
13563        assert_eq!(
13564            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13565            "-Unknown argument received\r\n"
13566        );
13567        // Everything after ITEMS is an item, even when it spells an option.
13568        assert_eq!(
13569            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13570            "*1\r\n:1\r\n"
13571        );
13572        // And the two ways of sending no items at all are the same complaint.
13573        assert!(
13574            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13575                .contains("wrong number of arguments")
13576        );
13577        assert!(
13578            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13579                .contains("wrong number of arguments")
13580        );
13581    }
13582
13583    /// The two walls a filter can hit, which say different things and are not
13584    /// the same wall.
13585    #[test]
13586    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13587        let mut f = Fixture::new();
13588        f.run(&[
13589            b"CF.RESERVE",
13590            b"s",
13591            b"4",
13592            b"BUCKETSIZE",
13593            b"1",
13594            b"EXPANSION",
13595            b"0",
13596        ]);
13597        for i in 0..4u32 {
13598            assert_eq!(
13599                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13600                ":1\r\n"
13601            );
13602        }
13603        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13604        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13605        // The add commands say it in a sentence and the insert commands say it
13606        // in the array, one value per item, and the array is never short.
13607        assert_eq!(
13608            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13609            "*2\r\n:-1\r\n:-1\r\n"
13610        );
13611        assert_eq!(
13612            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13613            "*2\r\n:0\r\n:-1\r\n"
13614        );
13615
13616        // A chain that is allowed to grow stops for a different reason, and the
13617        // count it stops at is the filter limit rather than the room: this one
13618        // gives up with three slots free. Loading a chain that already has
13619        // every filter it is allowed shows why, since it refuses an item
13620        // straight into an empty one.
13621        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13622        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13623        assert_eq!(
13624            f.run(&[b"CF.ADD", b"g", b"q"]),
13625            "-Maximum expansions reached\r\n"
13626        );
13627        assert_eq!(
13628            f.run(&[b"CF.INFO", b"g"]),
13629            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13630             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13631             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13632             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13633        );
13634    }
13635
13636    /// A filter dumped a chunk at a time and put back under another key is the
13637    /// same filter, and the headers that describe one nobody could build are
13638    /// refused on the way in.
13639    #[test]
13640    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13641        let mut f = Fixture::new();
13642        f.run(&[
13643            b"CF.RESERVE",
13644            b"src",
13645            b"8",
13646            b"BUCKETSIZE",
13647            b"2",
13648            b"EXPANSION",
13649            b"2",
13650        ]);
13651        for i in 0..40u32 {
13652            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13653        }
13654        // Position zero asks for the header and every one after it is a byte
13655        // offset across every filter laid end to end, and the walk ends on a
13656        // zero and a nil rather than an empty chunk.
13657        let mut pos = b"0".to_vec();
13658        let mut chunks = 0;
13659        loop {
13660            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13661            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13662            let next = head
13663                .split("\r\n")
13664                .nth(1)
13665                .and_then(|n| n.strip_prefix(':'))
13666                .expect("a two element reply of a position and a chunk")
13667                .to_owned();
13668            if next == "0" {
13669                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13670                break;
13671            }
13672            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13673            let at = body
13674                .windows(2)
13675                .position(|w| w == b"\r\n")
13676                .expect("a length line")
13677                + 2;
13678            let data = &body[at..body.len() - 2];
13679            assert_eq!(
13680                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13681                "+OK\r\n",
13682                "loading chunk {chunks}"
13683            );
13684            pos = next.into_bytes();
13685            chunks += 1;
13686        }
13687        assert!(chunks >= 2, "a header and at least one chunk");
13688
13689        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13690        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13691        for i in 0..40u32 {
13692            assert_eq!(
13693                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13694                ":1\r\n"
13695            );
13696        }
13697
13698        // A filter with nothing in it hands out no header at all, so a client
13699        // that dumps one has nothing to load back.
13700        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13701        assert_eq!(
13702            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13703            "*2\r\n:0\r\n$-1\r\n"
13704        );
13705
13706        // The positions this end will not take, which are not the same set at
13707        // both ends: a dump refuses a negative one and a load takes it as an
13708        // offset and fails to find anything there.
13709        assert_eq!(
13710            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13711            "-Invalid position\r\n"
13712        );
13713        assert_eq!(
13714            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13715            "-Invalid position\r\n"
13716        );
13717        assert_eq!(
13718            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13719            "-Invalid position\r\n"
13720        );
13721        assert_eq!(
13722            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13723            "-Couldn't load chunk!\r\n"
13724        );
13725        // A header on top of a filter is refused rather than merged.
13726        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13727        assert_eq!(
13728            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13729            "-ERR item exists\r\n"
13730        );
13731        // A chunk that is not the size of a header where a header should have
13732        // been is one sentence, and one that is the size of a header and
13733        // describes a filter nobody could build is another.
13734        assert_eq!(
13735            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13736            "-Invalid header\r\n"
13737        );
13738        for (why, bad) in [
13739            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13740            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13741            (
13742                "a bucket count that is not a power of two",
13743                cf_header(0, 3, 0, 1, [2, 20, 1]),
13744            ),
13745            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13746            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13747            (
13748                "a growth nobody could reach",
13749                cf_header(0, 8, 0, 1, [2, 20, 32769]),
13750            ),
13751            (
13752                "a chain that cannot grow and did",
13753                cf_header(0, 8, 0, 2, [2, 20, 0]),
13754            ),
13755            // The count is written in eight bytes and read into two, so a
13756            // number that is a multiple of the second arrives as none.
13757            (
13758                "a filter count that wraps",
13759                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13760            ),
13761        ] {
13762            assert_eq!(
13763                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13764                "-Couldn't create filter!\r\n",
13765                "{why}"
13766            );
13767        }
13768    }
13769
13770    /// The RESP3 shapes, which are where this family differs most from RESP2
13771    /// and where one of its answers stops being readable.
13772    #[test]
13773    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13774        let mut f = Fixture::new();
13775        f.out.set_proto(Proto::Resp3);
13776        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13777        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13778        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13779        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13780        assert_eq!(
13781            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13782            "*2\r\n#t\r\n#f\r\n"
13783        );
13784        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13785        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13786        // The count stays an integer, because it counts rather than answers.
13787        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13788        assert_eq!(
13789            f.run(&[b"CF.INFO", b"c"]),
13790            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13791             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13792             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13793             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13794        );
13795
13796        // `CF.INSERT` writes a boolean per item here and an integer per item on
13797        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
13798        // client cannot tell an item that did not fit from one that is already
13799        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
13800        f.run(&[
13801            b"CF.RESERVE",
13802            b"s",
13803            b"4",
13804            b"BUCKETSIZE",
13805            b"1",
13806            b"EXPANSION",
13807            b"0",
13808        ]);
13809        assert_eq!(
13810            f.run(&[
13811                b"CF.INSERT",
13812                b"s",
13813                b"ITEMS",
13814                b"a",
13815                b"b",
13816                b"c",
13817                b"d",
13818                b"e",
13819                b"f"
13820            ]),
13821            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13822        );
13823        assert_eq!(
13824            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13825            "*2\r\n:0\r\n:-1\r\n"
13826        );
13827        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13828        // The end of a dump is a nil and not an empty chunk, which is one
13829        // underscore here and a negative length on RESP2.
13830        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13831    }
13832
13833    // ------------------------------------------------------------------- cms
13834
13835    /// A sketch is made from either end, and both constructors look at the key
13836    /// before they look at their arguments.
13837    #[test]
13838    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13839        let mut f = Fixture::new();
13840        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13841        assert_eq!(
13842            f.run(&[b"CMS.INFO", b"d"]),
13843            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13844        );
13845        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13846        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13847        // Two over the error rounded up, and the log of the probability over the
13848        // log of a half rounded up, which for these two is 200 by 6.
13849        assert_eq!(
13850            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13851            "+OK\r\n"
13852        );
13853        assert_eq!(
13854            f.run(&[b"CMS.INFO", b"p"]),
13855            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13856        );
13857        // The key is checked first, so a width of zero at a key that is already
13858        // there is about the key and not about the width.
13859        assert_eq!(
13860            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13861            "-CMS: key already exists\r\n"
13862        );
13863        assert_eq!(
13864            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13865            "-CMS: invalid width\r\n"
13866        );
13867        assert_eq!(
13868            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13869            "-CMS: invalid depth\r\n"
13870        );
13871        assert_eq!(
13872            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13873            "-CMS: invalid overestimation value\r\n"
13874        );
13875        assert_eq!(
13876            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13877            "-CMS: invalid prob value\r\n"
13878        );
13879        // A probability whose float conversion is zero has no depth, and a width
13880        // past a signed sixty four bit integer has no width, and both are the
13881        // same sentence.
13882        assert_eq!(
13883            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13884            "-CMS: invalid init arguments\r\n"
13885        );
13886        // And a sketch bigger than a gibibyte of counters is refused here where
13887        // the reference reserves address space nobody has touched, which is
13888        // D-47.
13889        assert_eq!(
13890            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13891            "-CMS: Insufficient memory to create the key\r\n"
13892        );
13893        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13894    }
13895
13896    /// Every pair is parsed before any of them lands, the counters saturate,
13897    /// and the count is a signed total of what was asked for.
13898    #[test]
13899    fn increments_are_parsed_whole_and_the_counters_saturate() {
13900        let mut f = Fixture::new();
13901        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13902        assert_eq!(
13903            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13904            "*2\r\n:3\r\n:4\r\n"
13905        );
13906        // An item that is incremented twice in one call sees its own first
13907        // increment in the reply to the second.
13908        assert_eq!(
13909            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13910            "*2\r\n:4\r\n:5\r\n"
13911        );
13912        // A bad number anywhere means nothing at all is applied.
13913        assert_eq!(
13914            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13915            "-CMS: Cannot parse number\r\n"
13916        );
13917        assert_eq!(
13918            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13919            "-CMS: Number cannot be negative\r\n"
13920        );
13921        assert_eq!(
13922            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13923            "*2\r\n:5\r\n:4\r\n"
13924        );
13925        // The counters stop at four billion and the item that stopped says so in
13926        // its own slot while the one beside it answers a number.
13927        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13928        assert_eq!(
13929            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13930            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13931        );
13932        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13933        // The count is what was asked for rather than what landed, and it is
13934        // signed, so a big enough total comes back negative.
13935        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13936        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13937        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13938        assert_eq!(
13939            f.run(&[b"CMS.INFO", b"w"]),
13940            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13941        );
13942        // An odd number of arguments after the key is an arity error and not a
13943        // syntax one.
13944        assert!(
13945            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13946                .contains("wrong number of arguments")
13947        );
13948        assert_eq!(
13949            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13950            "-CMS: key does not exist\r\n"
13951        );
13952        assert_eq!(
13953            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13954            "-CMS: key does not exist\r\n"
13955        );
13956    }
13957
13958    /// A merge overwrites its destination, and it is worked out in full before
13959    /// any of it is written.
13960    #[test]
13961    fn a_merge_lands_whole_or_not_at_all() {
13962        let mut f = Fixture::new();
13963        for name in [&b"m1"[..], b"m2", b"dst"] {
13964            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13965        }
13966        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13967        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13968        assert_eq!(
13969            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13970            "+OK\r\n"
13971        );
13972        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13973        // Overwritten and not added to, so the same merge twice is the same
13974        // answer twice.
13975        assert_eq!(
13976            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13977            "+OK\r\n"
13978        );
13979        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13980        assert_eq!(
13981            f.run(&[
13982                b"CMS.MERGE",
13983                b"dst",
13984                b"2",
13985                b"m1",
13986                b"m2",
13987                b"WEIGHTS",
13988                b"2",
13989                b"3"
13990            ]),
13991            "+OK\r\n"
13992        );
13993        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13994        // A cell times a weight is checked wide rather than wrapped, so this is
13995        // a refusal and the destination is left exactly as it was.
13996        assert_eq!(
13997            f.run(&[
13998                b"CMS.MERGE",
13999                b"dst",
14000                b"1",
14001                b"m1",
14002                b"WEIGHTS",
14003                b"4611686018427387904"
14004            ]),
14005            "-CMS: MERGE overflow\r\n"
14006        );
14007        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14008        // The destination comes first, then the count, then the layout, then the
14009        // weights, then the sources one at a time.
14010        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
14011        assert_eq!(
14012            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
14013            "-CMS: key does not exist\r\n"
14014        );
14015        assert_eq!(
14016            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
14017            "-CMS: Number of keys must be positive\r\n"
14018        );
14019        assert_eq!(
14020            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
14021            "-CMS: wrong number of keys\r\n"
14022        );
14023        assert_eq!(
14024            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
14025            "-CMS: wrong number of keys/weights\r\n"
14026        );
14027        assert_eq!(
14028            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
14029            "-CMS: width/depth is not equal\r\n"
14030        );
14031        assert_eq!(
14032            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
14033            "-CMS: key does not exist\r\n"
14034        );
14035    }
14036
14037    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
14038    /// a sketch is refused by the two commands that would have to serialise it.
14039    #[test]
14040    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14041        let mut f = Fixture::new();
14042        f.run(&[b"SET", b"s", b"text"]);
14043        for cmd in [
14044            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
14045            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
14046            vec![&b"CMS.QUERY"[..], b"s", b"a"],
14047            vec![&b"CMS.INFO"[..], b"s"],
14048            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
14049        ] {
14050            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14051            let reply = f.run(&cmd);
14052            // The two constructors see the key before anything else and say so
14053            // in the module's own words, and the rest are `WRONGTYPE`.
14054            assert!(
14055                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
14056                "{name}: {reply}"
14057            );
14058        }
14059        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
14060        // Redis refuses to copy a module key that has no copy callback, and
14061        // these are its words rather than ours. `DUMP` is the other half of
14062        // D-48: the reference has a payload for one of these and we do not.
14063        assert_eq!(
14064            f.run(&[b"COPY", b"c", b"c2"]),
14065            "-ERR not supported for this module key\r\n"
14066        );
14067        assert_eq!(
14068            f.run(&[b"DUMP", b"c"]),
14069            "-ERR DUMP is not supported for this module key\r\n"
14070        );
14071        // A graph is nobody's module and keeps its own sentence.
14072        f.run(&[b"G.NADD", b"g", b"a"]);
14073        assert_eq!(
14074            f.run(&[b"COPY", b"g", b"g2"]),
14075            "-ERR COPY is not supported for a graph\r\n"
14076        );
14077        assert_eq!(
14078            f.run(&[b"DUMP", b"g"]),
14079            "-ERR DUMP is not supported for a graph\r\n"
14080        );
14081        // Everything that does not need a byte shape works on a sketch key the
14082        // way it works on any other.
14083        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
14084        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
14085        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
14086        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
14087        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
14088    }
14089
14090    // ------------------------------------------------------------------ topk
14091
14092    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
14093    /// it looks at any of them.
14094    #[test]
14095    fn a_reserve_takes_three_arguments_or_six() {
14096        let mut f = Fixture::new();
14097        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
14098        assert_eq!(
14099            f.run(&[b"TOPK.INFO", b"t"]),
14100            "*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"
14101        );
14102        // Four arguments and five are an arity error rather than a defaulted
14103        // depth or decay.
14104        for cmd in [
14105            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
14106            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
14107        ] {
14108            assert!(f.run(&cmd).contains("wrong number of arguments"));
14109        }
14110        assert_eq!(
14111            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
14112            "+OK\r\n"
14113        );
14114        // The key is checked first, so a reserve with nothing else right at a
14115        // key that is taken still says the key is taken.
14116        assert_eq!(
14117            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
14118            "-TopK: key already exists\r\n"
14119        );
14120        assert_eq!(
14121            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
14122            "-TopK: invalid k\r\n"
14123        );
14124        assert_eq!(
14125            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
14126            "-TopK: invalid width\r\n"
14127        );
14128        assert_eq!(
14129            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
14130            "-TopK: invalid depth\r\n"
14131        );
14132        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
14133        assert_eq!(
14134            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
14135            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
14136        );
14137        assert_eq!(
14138            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
14139            "+OK\r\n"
14140        );
14141        // Past the cap, with the one sentence in the family that has a prefix.
14142        assert_eq!(
14143            f.run(&[
14144                b"TOPK.RESERVE",
14145                b"w",
14146                b"1",
14147                b"4294967295",
14148                b"4294967295",
14149                b"0.9"
14150            ]),
14151            "-ERR Insufficient memory to create topk data structure\r\n"
14152        );
14153    }
14154
14155    /// What the sketch keeps, and the three ways of asking about it.
14156    #[test]
14157    fn the_kept_set_is_what_query_and_list_answer_from() {
14158        let mut f = Fixture::new();
14159        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
14160        // A null an item while there is room, then the name of whatever was
14161        // pushed out.
14162        assert_eq!(
14163            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
14164            "*2\r\n$-1\r\n$-1\r\n"
14165        );
14166        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
14167        // Two slots are full and `c` arrives with a count of one, which is not
14168        // under the smallest kept count, so it takes that slot straight away.
14169        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
14170        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
14171        assert_eq!(
14172            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
14173            "*3\r\n:1\r\n:0\r\n:1\r\n"
14174        );
14175        // The table still counts what the kept set let go of.
14176        assert_eq!(
14177            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14178            "*3\r\n:11\r\n:1\r\n:6\r\n"
14179        );
14180        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
14181        assert_eq!(
14182            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
14183            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
14184        );
14185        // Any prefix of the keyword turns the counts on, the empty string
14186        // included, and only a longer word or a different one is refused.
14187        assert_eq!(
14188            f.run(&[b"TOPK.LIST", b"t", b"w"]),
14189            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14190        );
14191        assert_eq!(
14192            f.run(&[b"TOPK.LIST", b"t", b""]),
14193            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14194        );
14195        assert_eq!(
14196            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
14197            "-WITHCOUNT keyword expected\r\n"
14198        );
14199        // And the keyword is looked at before the key, so a missing key with a
14200        // bad keyword complains about the keyword.
14201        assert_eq!(
14202            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
14203            "-WITHCOUNT keyword expected\r\n"
14204        );
14205        assert_eq!(
14206            f.run(&[b"TOPK.LIST", b"missing"]),
14207            "-TopK: key does not exist\r\n"
14208        );
14209        // An item counted zero times is kept and not listed.
14210        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
14211        assert_eq!(
14212            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
14213            "*1\r\n$-1\r\n"
14214        );
14215        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
14216        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
14217    }
14218
14219    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
14220    /// before it counted, and the reply counts what it wrote.
14221    #[test]
14222    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
14223        let mut f = Fixture::new();
14224        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
14225        // Three pairs, the middle one bad: two elements come back, one of them
14226        // the error, and the array header says two rather than three. That last
14227        // part is D-51 and it is why a client here stays in step.
14228        assert_eq!(
14229            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
14230            format!(
14231                "*2\r\n$-1\r\n-{}\r\n",
14232                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
14233            )
14234        );
14235        assert_eq!(
14236            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14237            "*3\r\n:3\r\n:0\r\n:0\r\n"
14238        );
14239        // A hundred thousand is in and one more is out.
14240        assert_eq!(
14241            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
14242            "*1\r\n$-1\r\n"
14243        );
14244        assert!(
14245            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
14246                .contains("smaller or equal to 100,000")
14247        );
14248        // Pairs have to be pairs.
14249        assert!(
14250            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
14251                .contains("wrong number of arguments")
14252        );
14253        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
14254    }
14255
14256    /// The RESP3 shapes, which are the two the protocols disagree about.
14257    #[test]
14258    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
14259        let mut f = Fixture::new();
14260        f.run(&[b"HELLO", b"3"]);
14261        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
14262        f.run(&[b"TOPK.ADD", b"t", b"a"]);
14263        assert_eq!(
14264            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
14265            "*2\r\n#t\r\n#f\r\n"
14266        );
14267        // The count stays an integer on both protocols.
14268        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
14269        assert_eq!(
14270            f.run(&[b"TOPK.INFO", b"t"]),
14271            "%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"
14272        );
14273        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
14274    }
14275
14276    /// A top k key answers the module sentences the other sketch families
14277    /// answer, and its own word for its type.
14278    #[test]
14279    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14280        let mut f = Fixture::new();
14281        f.run(&[b"SET", b"s", b"text"]);
14282        for cmd in [
14283            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
14284            vec![&b"TOPK.ADD"[..], b"s", b"a"],
14285            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
14286            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
14287            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
14288            vec![&b"TOPK.LIST"[..], b"s"],
14289            vec![&b"TOPK.INFO"[..], b"s"],
14290        ] {
14291            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14292            let reply = f.run(&cmd);
14293            assert!(
14294                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
14295                "{name}: {reply}"
14296            );
14297        }
14298        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
14299        assert_eq!(
14300            f.run(&[b"COPY", b"t", b"t2"]),
14301            "-ERR not supported for this module key\r\n"
14302        );
14303        assert_eq!(
14304            f.run(&[b"DUMP", b"t"]),
14305            "-ERR DUMP is not supported for this module key\r\n"
14306        );
14307        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14308        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14309        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14310        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
14311        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14312        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14313        // Every one of the six that is not the constructor says the same thing
14314        // about a key that is not there.
14315        assert_eq!(
14316            f.run(&[b"TOPK.INFO", b"t3"]),
14317            "-TopK: key does not exist\r\n"
14318        );
14319    }
14320
14321    // --------------------------------------------------------------- tdigest
14322
14323    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
14324    /// search rather than a lookup.
14325    #[test]
14326    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
14327        let mut f = Fixture::new();
14328        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
14329        // A hundred is the default and the capacity is six times it plus ten.
14330        assert_eq!(
14331            f.run(&[b"TDIGEST.INFO", b"t"]),
14332            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
14333             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
14334             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
14335        );
14336        assert_eq!(
14337            f.run(&[b"TDIGEST.CREATE", b"t"]),
14338            "-ERR T-Digest: key already exists\r\n"
14339        );
14340        // Three arguments is an arity error and not a missing keyword.
14341        assert!(
14342            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
14343                .contains("wrong number of arguments")
14344        );
14345        assert_eq!(
14346            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
14347            "+OK\r\n"
14348        );
14349        assert_eq!(
14350            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
14351            "+OK\r\n"
14352        );
14353        // The word is looked for across both trailing arguments and the number
14354        // is then read out of the last one whatever was found, so this looks for
14355        // a number inside the word `COMPRESSION` and does not find one.
14356        assert_eq!(
14357            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
14358            "-ERR T-Digest: error parsing compression parameter\r\n"
14359        );
14360        assert_eq!(
14361            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
14362            "-ERR T-Digest: wrong keyword\r\n"
14363        );
14364        assert_eq!(
14365            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
14366            "-ERR T-Digest: error parsing compression parameter\r\n"
14367        );
14368        assert_eq!(
14369            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
14370            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
14371        );
14372        // The reference's own ceiling, which is where the capacity stops fitting
14373        // in an int, and one past it.
14374        assert_eq!(
14375            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
14376            "-ERR T-Digest: allocation failed\r\n"
14377        );
14378        // And ours, which is a gibibyte of centroids and is D-52.
14379        assert_eq!(
14380            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
14381            "-ERR T-Digest: allocation failed\r\n"
14382        );
14383        // The key is checked before the arguments, so a bad compression at a key
14384        // that is already a digest still says the key is taken.
14385        assert_eq!(
14386            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
14387            "-ERR T-Digest: key already exists\r\n"
14388        );
14389    }
14390
14391    /// The four samples every note about this family is written against, and the
14392    /// answers a real 8.10.1 gives for them.
14393    #[test]
14394    fn the_quantile_family_answers_what_the_module_answers() {
14395        let mut f = Fixture::new();
14396        f.run(&[b"TDIGEST.CREATE", b"s"]);
14397        assert_eq!(
14398            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
14399            "+OK\r\n"
14400        );
14401        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14402        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14403        // The cdf of a sample is the weight below it plus half its own.
14404        assert_eq!(
14405            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14406            "*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"
14407        );
14408        assert_eq!(
14409            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14410            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14411        );
14412        // Out of order, the walk restarts, and 0.5 answers 3 either way while
14413        // the two after it are read from the front again.
14414        assert_eq!(
14415            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14416            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14417        );
14418        assert_eq!(
14419            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14420            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14421        );
14422        assert_eq!(
14423            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14424            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14425        );
14426        assert_eq!(
14427            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14428            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14429        );
14430        assert_eq!(
14431            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14432            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14433        );
14434        assert_eq!(
14435            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14436            "$3\r\n2.5\r\n"
14437        );
14438        assert_eq!(
14439            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14440            "$3\r\n2.5\r\n"
14441        );
14442        // The ranges, which are separate sentences from the parse failures.
14443        assert_eq!(
14444            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14445            "-ERR T-Digest: quantile should be in [0,1]\r\n"
14446        );
14447        assert_eq!(
14448            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14449            "-ERR T-Digest: error parsing quantile\r\n"
14450        );
14451        assert_eq!(
14452            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14453            "-ERR T-Digest: error parsing cdf\r\n"
14454        );
14455        assert_eq!(
14456            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14457            "-ERR T-Digest: error parsing value\r\n"
14458        );
14459        assert_eq!(
14460            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14461            "-ERR T-Digest: rank needs to be non negative\r\n"
14462        );
14463        assert_eq!(
14464            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14465            "-ERR T-Digest: error parsing rank\r\n"
14466        );
14467        // Both cuts have their own parse sentence and share the range one, and
14468        // equal cuts are refused rather than answering nothing.
14469        assert_eq!(
14470            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14471            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14472        );
14473        assert_eq!(
14474            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14475            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14476        );
14477        assert_eq!(
14478            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14479            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14480        );
14481        assert_eq!(
14482            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14483            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14484        );
14485    }
14486
14487    /// An empty digest answers every question, and answers most of them with
14488    /// something that is not a number.
14489    #[test]
14490    fn an_empty_digest_has_an_answer_for_everything() {
14491        let mut f = Fixture::new();
14492        f.run(&[b"TDIGEST.CREATE", b"e"]);
14493        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14494        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14495        assert_eq!(
14496            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14497            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14498        );
14499        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14500        assert_eq!(
14501            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14502            "$3\r\nnan\r\n"
14503        );
14504        // Minus two, which is a number no rank on a digest with samples in it
14505        // can ever be.
14506        assert_eq!(
14507            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14508            "*2\r\n:-2\r\n:-2\r\n"
14509        );
14510        assert_eq!(
14511            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14512            "*2\r\n:-2\r\n:-2\r\n"
14513        );
14514        assert_eq!(
14515            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14516            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14517        );
14518        // A reset puts a digest with samples back into exactly this state.
14519        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14520        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14521        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14522        // Down to the compression count, so a reset digest and a fresh one of
14523        // the same compression report the same nine numbers.
14524        f.run(&[b"TDIGEST.CREATE", b"e2"]);
14525        assert_eq!(
14526            f.run(&[b"TDIGEST.INFO", b"e"]),
14527            f.run(&[b"TDIGEST.INFO", b"e2"])
14528        );
14529    }
14530
14531    /// The double parser is Redis's and not this engine's, and the two disagree
14532    /// at both ends of the range.
14533    #[test]
14534    fn a_sample_is_read_the_way_redis_reads_a_double() {
14535        let mut f = Fixture::new();
14536        f.run(&[b"TDIGEST.CREATE", b"a"]);
14537        // Overflow and underflow are parse failures rather than an infinity and
14538        // a zero, which is where this parts company with the rest of the engine.
14539        for bad in [
14540            &b"nan"[..],
14541            b"1e400",
14542            b"-1e400",
14543            b"1e309",
14544            b"1e-400",
14545            b"",
14546            b" 1",
14547            b"1 ",
14548            b"1e",
14549            b"--1",
14550        ] {
14551            assert_eq!(
14552                f.run(&[b"TDIGEST.ADD", b"a", bad]),
14553                "-ERR T-Digest: error parsing val parameter\r\n",
14554                "{}",
14555                String::from_utf8_lossy(bad)
14556            );
14557        }
14558        // An infinity spelled out parses and is then refused for being one, with
14559        // a different sentence.
14560        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14561            assert_eq!(
14562                f.run(&[b"TDIGEST.ADD", b"a", word]),
14563                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14564                "{}",
14565                String::from_utf8_lossy(word)
14566            );
14567        }
14568        // These all parse: hex, a bare point either side, and the smallest
14569        // subnormal the reference will take.
14570        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14571            assert_eq!(
14572                f.run(&[b"TDIGEST.ADD", b"a", good]),
14573                "+OK\r\n",
14574                "{}",
14575                String::from_utf8_lossy(good)
14576            );
14577        }
14578        // Nothing landed from the failures, so six samples is what there is.
14579        assert!(
14580            f.run(&[b"TDIGEST.INFO", b"a"])
14581                .contains("Observations\r\n:6\r\n")
14582        );
14583        // Every value is parsed before any is added, so this whole command is a
14584        // no op.
14585        assert_eq!(
14586            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14587            "-ERR T-Digest: error parsing val parameter\r\n"
14588        );
14589        assert!(
14590            f.run(&[b"TDIGEST.INFO", b"a"])
14591                .contains("Observations\r\n:6\r\n")
14592        );
14593    }
14594
14595    /// What a merge does to its destination, to its inputs and to the buffer
14596    /// split `TDIGEST.INFO` reports.
14597    #[test]
14598    fn a_merge_sweeps_the_destination_between_its_inputs() {
14599        let mut f = Fixture::new();
14600        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14601        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14602        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14603        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14604        assert_eq!(
14605            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14606            "+OK\r\n"
14607        );
14608        // The destination did not exist, so the compression is the largest of
14609        // the inputs. The three from the first input were swept in before the
14610        // three from the second arrived, which is the one visible effect of the
14611        // reference folding one input at a time.
14612        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14613        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14614        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14615        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14616        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14617        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14618        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14619        // Reading a source sweeps it too, so a merge writes to keys it only
14620        // reads from.
14621        assert!(
14622            f.run(&[b"TDIGEST.INFO", b"m1"])
14623                .contains("Merged nodes\r\n:3\r\n")
14624        );
14625        // Without OVERRIDE the destination joins its own inputs, so this takes
14626        // it to nine observations and keeps its own compression.
14627        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14628        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14629        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14630        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14631        // With OVERRIDE the old destination is dropped and the compression goes
14632        // back to the largest of the inputs.
14633        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14634        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14635        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14636        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14637        // And COMPRESSION beats both.
14638        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14639        assert!(
14640            f.run(&[b"TDIGEST.INFO", b"d"])
14641                .contains("Compression\r\n:500\r\n")
14642        );
14643        // Naming the destination as a source folds it in twice.
14644        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14645        assert!(
14646            f.run(&[b"TDIGEST.INFO", b"d"])
14647                .contains("Observations\r\n:12\r\n")
14648        );
14649        // The arguments, in the order the reference checks them.
14650        assert_eq!(
14651            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14652            "-ERR T-Digest: error parsing numkeys\r\n"
14653        );
14654        assert_eq!(
14655            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14656            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14657        );
14658        assert!(
14659            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14660                .contains("wrong number of arguments")
14661        );
14662        assert!(
14663            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14664                .contains("wrong number of arguments")
14665        );
14666        assert_eq!(
14667            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14668            "-ERR T-Digest: wrong keyword\r\n"
14669        );
14670        // A source that is not there stops the whole thing, and the destination
14671        // is left as it was.
14672        assert_eq!(
14673            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14674            "-ERR T-Digest: key does not exist\r\n"
14675        );
14676        assert!(
14677            f.run(&[b"TDIGEST.INFO", b"d"])
14678                .contains("Observations\r\n:12\r\n")
14679        );
14680        // A destination that is not there and is also named as a source is the
14681        // same sentence rather than an empty merge.
14682        assert_eq!(
14683            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14684            "-ERR T-Digest: key does not exist\r\n"
14685        );
14686    }
14687
14688    /// The RESP3 shapes, which are the two the protocols disagree about.
14689    #[test]
14690    fn a_digest_answers_doubles_and_a_map_on_resp3() {
14691        let mut f = Fixture::new();
14692        f.run(&[b"HELLO", b"3"]);
14693        f.run(&[b"TDIGEST.CREATE", b"s"]);
14694        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14695        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14696        assert_eq!(
14697            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14698            "*2\r\n,1\r\n,4\r\n"
14699        );
14700        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14701        // The two infinities and the NaN go out as the bare words.
14702        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14703        assert_eq!(
14704            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14705            "*1\r\n,-inf\r\n"
14706        );
14707        f.run(&[b"TDIGEST.CREATE", b"e"]);
14708        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14709        // The ranks stay integers on both protocols.
14710        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14711        // Every question above swept the buffer in, so the four samples are all
14712        // merged by now and the compression count says it happened once.
14713        assert_eq!(
14714            f.run(&[b"TDIGEST.INFO", b"s"]),
14715            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14716             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14717             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14718        );
14719    }
14720
14721    /// A t digest key answers the module sentences the other sketch families
14722    /// answer, and its own word for its type.
14723    #[test]
14724    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14725        let mut f = Fixture::new();
14726        f.run(&[b"SET", b"s", b"text"]);
14727        for cmd in [
14728            vec![&b"TDIGEST.CREATE"[..], b"s"],
14729            vec![&b"TDIGEST.RESET"[..], b"s"],
14730            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14731            vec![&b"TDIGEST.MIN"[..], b"s"],
14732            vec![&b"TDIGEST.MAX"[..], b"s"],
14733            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14734            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14735            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14736            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14737            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14738            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14739            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14740            vec![&b"TDIGEST.INFO"[..], b"s"],
14741        ] {
14742            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14743            let reply = f.run(&cmd);
14744            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14745        }
14746        // The merge checks its destination the same way, and its sources too.
14747        f.run(&[b"TDIGEST.CREATE", b"t"]);
14748        assert!(
14749            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14750                .starts_with("-WRONGTYPE")
14751        );
14752        assert!(
14753            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14754                .starts_with("-WRONGTYPE")
14755        );
14756        assert_eq!(
14757            f.run(&[b"COPY", b"t", b"t2"]),
14758            "-ERR not supported for this module key\r\n"
14759        );
14760        assert_eq!(
14761            f.run(&[b"DUMP", b"t"]),
14762            "-ERR DUMP is not supported for this module key\r\n"
14763        );
14764        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14765        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14766        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14767        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14768        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14769        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14770        // An empty digest is still a key, so the twelve that are not the
14771        // constructor all say the same thing once it is gone.
14772        assert_eq!(
14773            f.run(&[b"TDIGEST.INFO", b"t3"]),
14774            "-ERR T-Digest: key does not exist\r\n"
14775        );
14776        // The key is looked at before the arguments, so a bad argument at a key
14777        // that is not there still says the key is not there.
14778        assert_eq!(
14779            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14780            "-ERR T-Digest: key does not exist\r\n"
14781        );
14782    }
14783
14784    // -------------------------------------------------------------------- ts
14785
14786    /// A `TS.INFO` reply with the memory usage taken out of it.
14787    ///
14788    /// That number is what a series costs here rather than what one costs in the
14789    /// module, which is D-53, and it moves whenever the layout of a chunk does.
14790    /// Everything either side of it is the wire contract and is worth pinning
14791    /// down exactly, so the tests below check the whole reply with the one
14792    /// number lifted out.
14793    fn without_memory(reply: &str) -> String {
14794        let head = "+memoryUsage\r\n:";
14795        let at = reply.find(head).expect("every TS.INFO reports memory");
14796        let rest = &reply[at + head.len()..];
14797        let end = rest.find("\r\n").expect("and it is a whole number");
14798        format!("{}{}", &reply[..at + head.len()], &rest[end..])
14799    }
14800
14801    /// A series is made empty and still says it has a chunk, and the options are
14802    /// read before the key is looked at.
14803    #[test]
14804    fn a_series_is_made_empty_and_reports_on_itself() {
14805        let mut f = Fixture::new();
14806        assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14807        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14808        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14809        // Fourteen fields, so twenty eight elements. An empty series reports one
14810        // chunk and zero at both ends, and neither the chunk type nor the
14811        // duplicate policy is ever a nil.
14812        assert_eq!(
14813            without_memory(&f.run(&[b"TS.INFO", b"t"])),
14814            "*28\r\n\
14815             +totalSamples\r\n:0\r\n\
14816             +memoryUsage\r\n:\r\n\
14817             +firstTimestamp\r\n:0\r\n\
14818             +lastTimestamp\r\n:0\r\n\
14819             +retentionTime\r\n:0\r\n\
14820             +chunkCount\r\n:1\r\n\
14821             +chunkSize\r\n:4096\r\n\
14822             +chunkType\r\n+compressed\r\n\
14823             +duplicatePolicy\r\n+block\r\n\
14824             +labels\r\n*0\r\n\
14825             +sourceKey\r\n$-1\r\n\
14826             +rules\r\n*0\r\n\
14827             +ignoreMaxTimeDiff\r\n:0\r\n\
14828             +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14829        );
14830        // A key that is already there is about the key whatever it holds, and
14831        // the existence is what is checked rather than the type.
14832        assert_eq!(
14833            f.run(&[b"TS.CREATE", b"t"]),
14834            "-ERR TSDB: key already exists\r\n"
14835        );
14836        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14837        assert_eq!(
14838            f.run(&[b"TS.CREATE", b"str"]),
14839            "-ERR TSDB: key already exists\r\n"
14840        );
14841        // But the arguments are read first, so a bad one at a key that is there
14842        // answers about the argument.
14843        assert_eq!(
14844            f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14845            "-ERR TSDB: Couldn't parse RETENTION\r\n"
14846        );
14847        // The seven that will not make a series say WRONGTYPE about a key
14848        // holding something else, where the two that would say a sentence.
14849        // The word is inside the sentence and not in front of it, because the
14850        // module writes its own error text and Redis puts ERR on the front of
14851        // anything a module writes.
14852        let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14853        assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14854        assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14855        assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14856        assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14857        assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14858        assert_eq!(
14859            f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14860            "-ERR TSDB: the key is not a TSDB key\r\n"
14861        );
14862        // And the ones that will not make one say so about a key that is gone.
14863        assert_eq!(
14864            f.run(&[b"TS.INFO", b"nope"]),
14865            "-ERR TSDB: the key does not exist\r\n"
14866        );
14867        assert_eq!(
14868            f.run(&[b"TS.GET", b"nope"]),
14869            "-ERR TSDB: the key does not exist\r\n"
14870        );
14871        assert_eq!(
14872            f.run(&[b"TS.ALTER", b"nope"]),
14873            "-ERR TSDB: the key does not exist\r\n"
14874        );
14875        assert_eq!(
14876            f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14877            "-ERR TSDB: the key does not exist\r\n"
14878        );
14879    }
14880
14881    /// Every option word, including the ones that are wrong, and the scan that
14882    /// finds them.
14883    #[test]
14884    fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14885        let mut f = Fixture::new();
14886        assert_eq!(
14887            f.run(&[
14888                b"TS.CREATE",
14889                b"t",
14890                b"RETENTION",
14891                b"5000",
14892                b"ENCODING",
14893                b"UNCOMPRESSED",
14894                b"CHUNK_SIZE",
14895                b"128",
14896                b"DUPLICATE_POLICY",
14897                b"LAST",
14898                b"IGNORE",
14899                b"10",
14900                b"0.5",
14901                b"LABELS",
14902                b"room",
14903                b"kitchen"
14904            ]),
14905            "+OK\r\n"
14906        );
14907        let info = f.run(&[b"TS.INFO", b"t"]);
14908        assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14909        assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14910        assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14911        assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14912        assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14913        // A plain double here, where a sample value out of TS.GET is the
14914        // shortest digits that read back as the same number.
14915        assert!(
14916            info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14917            "{info}"
14918        );
14919        assert!(
14920            info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
14921            "{info}"
14922        );
14923
14924        // A word that is not an option is read past rather than refused.
14925        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14926        // LABELS eats everything after it in pairs, and the later scans still
14927        // look inside what it ate, so this sets a retention and stores a label
14928        // called RETENTION at the same time.
14929        assert_eq!(
14930            f.run(&[
14931                b"TS.CREATE",
14932                b"g",
14933                b"LABELS",
14934                b"a",
14935                b"b",
14936                b"RETENTION",
14937                b"5"
14938            ]),
14939            "+OK\r\n"
14940        );
14941        let greedy = f.run(&[b"TS.INFO", b"g"]);
14942        assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
14943        assert!(
14944            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"),
14945            "{greedy}"
14946        );
14947
14948        // Every way an option can be wrong, in the order the module reads them.
14949        assert_eq!(
14950            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
14951            "-ERR TSDB: Couldn't parse LABELS\r\n"
14952        );
14953        assert_eq!(
14954            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
14955            "-ERR TSDB: Couldn't parse LABELS\r\n"
14956        );
14957        assert_eq!(
14958            f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
14959            "-ERR TSDB: Couldn't parse RETENTION\r\n"
14960        );
14961        // A retention below zero is one of the two the module writes with no
14962        // ERR in front of it, where one that is not a number gets one.
14963        assert_eq!(
14964            f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
14965            "-TSDB: Couldn't parse RETENTION\r\n"
14966        );
14967        assert_eq!(
14968            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
14969            "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
14970        );
14971        assert_eq!(
14972            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
14973            "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
14974        );
14975        assert_eq!(
14976            f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
14977            "-ERR TSDB: unknown ENCODING parameter\r\n"
14978        );
14979        // And an ENCODING with nothing behind it is an arity error where every
14980        // other keyword in the same spot is a sentence.
14981        assert!(
14982            f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
14983                .contains("wrong number of arguments for 'ts.create' command")
14984        );
14985        assert_eq!(
14986            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
14987            "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
14988        );
14989        assert_eq!(
14990            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
14991            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14992        );
14993        assert_eq!(
14994            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
14995            "-ERR TSDB: Couldn't parse IGNORE\r\n"
14996        );
14997        assert_eq!(
14998            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
14999            "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
15000        );
15001        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
15002
15003        // An alter changes what was named and leaves the rest alone, and reads
15004        // an encoding only far enough to refuse a bad one.
15005        assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
15006        let after = f.run(&[b"TS.INFO", b"t"]);
15007        assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
15008        assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
15009        assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
15010        assert_eq!(
15011            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
15012            "-ERR TSDB: unknown ENCODING parameter\r\n"
15013        );
15014        // An encoding it does take is still not applied.
15015        assert_eq!(
15016            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
15017            "+OK\r\n"
15018        );
15019        assert!(
15020            f.run(&[b"TS.INFO", b"t"])
15021                .contains("+chunkType\r\n+uncompressed\r\n")
15022        );
15023    }
15024
15025    /// Samples go in, come back out and are refused for the reasons the module
15026    /// refuses them.
15027    #[test]
15028    fn samples_land_where_they_are_put_and_the_newest_comes_back() {
15029        let mut f = Fixture::new();
15030        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
15031        // The series was made on the way in.
15032        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
15033        assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
15034        // A sample value goes out as a simple string of the shortest digits
15035        // that read back as the same number.
15036        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
15037        assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
15038        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
15039        // An empty series has no newest sample and answers an empty array
15040        // rather than a nil.
15041        assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
15042        assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
15043
15044        // The value is read before the key, so a bad one against a key holding
15045        // a string is about the value.
15046        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15047        assert_eq!(
15048            f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
15049            "-ERR TSDB: invalid value\r\n"
15050        );
15051        // The grammar is tighter than the one a number argument usually gets:
15052        // no leading plus, no bare fraction, no infinity and nothing that does
15053        // not fit.
15054        for bad in [
15055            &b".5"[..],
15056            b"1.",
15057            b"+1",
15058            b" 1",
15059            b"0x10",
15060            b"inf",
15061            b"1e400",
15062            b"--1",
15063            b"1e",
15064        ] {
15065            assert_eq!(
15066                f.run(&[b"TS.ADD", b"v", b"1", bad]),
15067                "-ERR TSDB: invalid value\r\n",
15068                "{}",
15069                String::from_utf8_lossy(bad)
15070            );
15071        }
15072        // And a reading that is not a number is one of three words.
15073        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
15074
15075        // A timestamp that is not a number, and one that is and is below zero,
15076        // are two different sentences.
15077        assert_eq!(
15078            f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
15079            "-ERR TSDB: invalid timestamp\r\n"
15080        );
15081        assert_eq!(
15082            f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
15083            "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
15084        );
15085
15086        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
15087        // command beats what the series was told.
15088        assert_eq!(
15089            f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
15090            "-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"
15091        );
15092        assert_eq!(
15093            f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
15094            ":300\r\n"
15095        );
15096        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
15097        // ON_DUPLICATE is only read when the key was already there, which is
15098        // why a policy word that is not a policy passes on a fresh key.
15099        assert_eq!(
15100            f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
15101            ":1\r\n"
15102        );
15103        assert_eq!(
15104            f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
15105            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15106        );
15107
15108        // Retention is exact and it is checked before anything else happens, so
15109        // a sample landing behind the window is refused rather than trimmed.
15110        assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
15111        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
15112        assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
15113        assert_eq!(
15114            f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
15115            "-ERR TSDB: Timestamp is older than retention\r\n"
15116        );
15117        // And the window trims as it moves.
15118        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
15119        assert!(
15120            f.run(&[b"TS.INFO", b"r"])
15121                .contains("+totalSamples\r\n:1\r\n")
15122        );
15123
15124        // An ignore window drops a sample close enough to the newest one to be
15125        // uninteresting, and answers the newest timestamp so a client can tell.
15126        assert_eq!(
15127            f.run(&[
15128                b"TS.CREATE",
15129                b"i",
15130                b"DUPLICATE_POLICY",
15131                b"LAST",
15132                b"IGNORE",
15133                b"10",
15134                b"0.5"
15135            ]),
15136            "+OK\r\n"
15137        );
15138        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
15139        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
15140        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
15141    }
15142
15143    /// Every triple in a `TS.MADD` is answered on its own, and none of them
15144    /// makes a series.
15145    #[test]
15146    fn a_madd_answers_each_triple_and_creates_nothing() {
15147        let mut f = Fixture::new();
15148        assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
15149        assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
15150        assert_eq!(
15151            f.run(&[
15152                b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
15153            ]),
15154            "*3\r\n:100\r\n:100\r\n:200\r\n"
15155        );
15156        // A key that is not a series is an error in its own slot and the ones
15157        // after it still land.
15158        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15159        assert_eq!(
15160            f.run(&[
15161                b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
15162            ]),
15163            "*3\r\n\
15164             -ERR TSDB: the key is not a TSDB key\r\n\
15165             -ERR TSDB: the key is not a TSDB key\r\n\
15166             :300\r\n"
15167        );
15168        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
15169        // A bad value and a bad timestamp are answered in their slots too.
15170        assert_eq!(
15171            f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
15172            "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
15173        );
15174        // And a list that is not made of triples is an arity error.
15175        assert!(
15176            f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
15177                .contains("wrong number of arguments for 'ts.madd' command")
15178        );
15179    }
15180
15181    /// The two increments, which only ever write forwards.
15182    #[test]
15183    fn an_increment_walks_the_newest_value_up_and_down() {
15184        let mut f = Fixture::new();
15185        assert_eq!(
15186            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15187            ":100\r\n"
15188        );
15189        assert_eq!(
15190            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15191            ":100\r\n"
15192        );
15193        // Two on one timestamp add up rather than collide, because the sample
15194        // goes in under the last policy whatever the series says.
15195        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
15196        assert_eq!(
15197            f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
15198            ":200\r\n"
15199        );
15200        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
15201        // A timestamp behind the newest sample is the other of the two errors
15202        // the module writes with no ERR in front of it.
15203        assert_eq!(
15204            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
15205            "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
15206        );
15207        // The increment goes through the ordinary number reader, so it takes
15208        // what a sample value will not and refuses a NaN that a sample value
15209        // takes.
15210        assert_eq!(
15211            f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
15212            ":1\r\n"
15213        );
15214        assert_eq!(
15215            f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
15216            ":1\r\n"
15217        );
15218        assert_eq!(
15219            f.run(&[b"TS.INCRBY", b"t", b"nan"]),
15220            "-ERR TSDB: invalid increase/decrease value\r\n"
15221        );
15222        assert_eq!(
15223            f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
15224            "-ERR TSDB: invalid increase/decrease value\r\n"
15225        );
15226        // A key holding something else is WRONGTYPE and is answered before the
15227        // number is looked at.
15228        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15229        assert_eq!(
15230            f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
15231            "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15232        );
15233        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
15234        // The reference reads one past the end of its own arguments here and
15235        // answers whatever was in that memory, so there is nothing to copy and
15236        // this answers the same thing every time.
15237        assert_eq!(
15238            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
15239            "-ERR TSDB: invalid timestamp\r\n"
15240        );
15241        // And one behind a LABELS is a label name rather than the keyword, so
15242        // this lands at the clock rather than at 5.
15243        assert_eq!(
15244            f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
15245            format!(":{}\r\n", f.server.now_ms())
15246        );
15247        // Adding to a series whose newest value is not a number has no answer.
15248        assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
15249        assert_eq!(
15250            f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
15251            "-ERR TSDB: cannot increment/decrement NaN value\r\n"
15252        );
15253    }
15254
15255    /// Deleting a span, both ends included.
15256    #[test]
15257    fn deleting_takes_out_a_span_and_answers_how_many_went() {
15258        let mut f = Fixture::new();
15259        for at in [b"100".as_slice(), b"200", b"300", b"400"] {
15260            f.run(&[b"TS.ADD", b"t", at, b"1"]);
15261        }
15262        assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
15263        assert!(
15264            f.run(&[b"TS.INFO", b"t"])
15265                .contains("+totalSamples\r\n:2\r\n")
15266        );
15267        // Ends the wrong way round take nothing out rather than being an error.
15268        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
15269        // The two open ends.
15270        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
15271        // A series everything has been deleted from keeps its chunk and reports
15272        // zero at both ends again.
15273        let empty = f.run(&[b"TS.INFO", b"t"]);
15274        assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
15275        assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
15276        assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
15277        assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
15278        assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
15279        // The two ends have their own sentences.
15280        assert_eq!(
15281            f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
15282            "-ERR TSDB: wrong fromTimestamp\r\n"
15283        );
15284        assert_eq!(
15285            f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
15286            "-ERR TSDB: wrong toTimestamp\r\n"
15287        );
15288        assert_eq!(
15289            f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
15290            "-ERR TSDB: wrong fromTimestamp\r\n"
15291        );
15292    }
15293
15294    /// What RESP3 changes, which is the two places a number is written and the
15295    /// shape of `TS.INFO`.
15296    #[test]
15297    fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
15298        let mut f = Fixture::new();
15299        f.out = Out::new(Proto::Resp3);
15300        assert_eq!(
15301            f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
15302            "+OK\r\n"
15303        );
15304        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
15305        // A double rather than the simple string RESP2 gets.
15306        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
15307        assert_eq!(
15308            without_memory(&f.run(&[b"TS.INFO", b"t"])),
15309            "%14\r\n\
15310             +totalSamples\r\n:1\r\n\
15311             +memoryUsage\r\n:\r\n\
15312             +firstTimestamp\r\n:100\r\n\
15313             +lastTimestamp\r\n:100\r\n\
15314             +retentionTime\r\n:0\r\n\
15315             +chunkCount\r\n:1\r\n\
15316             +chunkSize\r\n:4096\r\n\
15317             +chunkType\r\n+compressed\r\n\
15318             +duplicatePolicy\r\n+block\r\n\
15319             +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
15320             +sourceKey\r\n_\r\n\
15321             +rules\r\n%0\r\n\
15322             +ignoreMaxTimeDiff\r\n:0\r\n\
15323             +ignoreMaxValDiff\r\n,0\r\n"
15324        );
15325    }
15326
15327    /// Reading a span back, both ways round, with the two ends and the three
15328    /// things that trim what comes out.
15329    #[test]
15330    fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
15331        let mut f = Fixture::new();
15332        for (at, v) in [
15333            (b"100".as_slice(), b"1".as_slice()),
15334            (b"200", b"2"),
15335            (b"300", b"3"),
15336            (b"400", b"4"),
15337        ] {
15338            f.run(&[b"TS.ADD", b"t", at, v]);
15339        }
15340        assert_eq!(
15341            f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
15342            "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
15343             *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
15344        );
15345        // Both ends are included.
15346        assert_eq!(
15347            f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
15348            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15349        );
15350        // Backwards, and the count takes from the front of what comes out, so
15351        // backwards it takes the newest.
15352        assert_eq!(
15353            f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
15354            "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
15355        );
15356        // Ends the wrong way round are empty rather than an error.
15357        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
15358        // The two filters.
15359        assert_eq!(
15360            f.run(&[
15361                b"TS.RANGE",
15362                b"t",
15363                b"-",
15364                b"+",
15365                b"FILTER_BY_VALUE",
15366                b"2",
15367                b"3"
15368            ]),
15369            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15370        );
15371        assert_eq!(
15372            f.run(&[
15373                b"TS.RANGE",
15374                b"t",
15375                b"-",
15376                b"+",
15377                b"FILTER_BY_TS",
15378                b"100",
15379                b"400"
15380            ]),
15381            "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
15382        );
15383        // A word that is not an option is ignored wherever it sits.
15384        assert_eq!(
15385            f.run(&[
15386                b"TS.RANGE",
15387                b"t",
15388                b"-",
15389                b"+",
15390                b"ZZZ",
15391                b"FILTER_BY_TS",
15392                b"400"
15393            ]),
15394            "*1\r\n*2\r\n:400\r\n+4\r\n"
15395        );
15396        // `LATEST` means nothing until there is a compaction rule to follow.
15397        assert_eq!(
15398            f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
15399            "*1\r\n*2\r\n:100\r\n+1\r\n"
15400        );
15401    }
15402
15403    /// The bucketing, which is one column a reduction and a flat row.
15404    #[test]
15405    fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15406        let mut f = Fixture::new();
15407        for (at, v) in [
15408            (b"100".as_slice(), b"1".as_slice()),
15409            (b"200", b"2"),
15410            (b"300", b"3"),
15411            (b"400", b"4"),
15412        ] {
15413            f.run(&[b"TS.ADD", b"t", at, v]);
15414        }
15415        assert_eq!(
15416            f.run(&[
15417                b"TS.RANGE",
15418                b"t",
15419                b"-",
15420                b"+",
15421                b"AGGREGATION",
15422                b"avg",
15423                b"200"
15424            ]),
15425            "*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"
15426        );
15427        // Three reductions is a row of four and not a row of two with a nested
15428        // three in it.
15429        assert_eq!(
15430            f.run(&[
15431                b"TS.RANGE",
15432                b"t",
15433                b"-",
15434                b"+",
15435                b"AGGREGATION",
15436                b"min,max,count",
15437                b"200"
15438            ]),
15439            "*3\r\n\
15440             *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15441             *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15442             *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15443        );
15444        // The timestamp a bucket is reported under.
15445        assert_eq!(
15446            f.run(&[
15447                b"TS.RANGE",
15448                b"t",
15449                b"-",
15450                b"+",
15451                b"AGGREGATION",
15452                b"avg",
15453                b"200",
15454                b"BUCKETTIMESTAMP",
15455                b"+"
15456            ]),
15457            "*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"
15458        );
15459        // An alignment moves where the bucket edges land.
15460        assert_eq!(
15461            f.run(&[
15462                b"TS.RANGE",
15463                b"t",
15464                b"100",
15465                b"400",
15466                b"ALIGN",
15467                b"100",
15468                b"AGGREGATION",
15469                b"sum",
15470                b"200"
15471            ]),
15472            "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15473        );
15474        // A `COUNT` sitting where the reduction name belongs is that name, and
15475        // the scan for a real one starts again two words later.
15476        assert_eq!(
15477            f.run(&[
15478                b"TS.RANGE",
15479                b"t",
15480                b"-",
15481                b"+",
15482                b"AGGREGATION",
15483                b"count",
15484                b"200"
15485            ]),
15486            "*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"
15487        );
15488        assert_eq!(
15489            f.run(&[
15490                b"TS.RANGE",
15491                b"t",
15492                b"-",
15493                b"+",
15494                b"AGGREGATION",
15495                b"count",
15496                b"200",
15497                b"COUNT",
15498                b"1"
15499            ]),
15500            "*1\r\n*2\r\n:0\r\n+1\r\n"
15501        );
15502    }
15503
15504    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
15505    /// carries two different things depending on which kind of empty it is.
15506    #[test]
15507    fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15508        let mut f = Fixture::new();
15509        for (at, v) in [
15510            (b"0".as_slice(), b"1".as_slice()),
15511            (b"100", b"2"),
15512            (b"500", b"nan"),
15513            (b"600", b"3"),
15514        ] {
15515            f.run(&[b"TS.ADD", b"g", at, v]);
15516        }
15517        // Without `EMPTY` the buckets with nothing in them are not there at all,
15518        // and neither is the one holding only a reading that is not a number.
15519        assert_eq!(
15520            f.run(&[
15521                b"TS.RANGE",
15522                b"g",
15523                b"-",
15524                b"+",
15525                b"AGGREGATION",
15526                b"avg",
15527                b"100"
15528            ]),
15529            "*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"
15530        );
15531        // The sum of nothing is zero rather than not a number.
15532        assert_eq!(
15533            f.run(&[
15534                b"TS.RANGE",
15535                b"g",
15536                b"-",
15537                b"+",
15538                b"AGGREGATION",
15539                b"sum",
15540                b"100",
15541                b"EMPTY"
15542            ]),
15543            "*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\
15544             *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\
15545             *2\r\n:600\r\n+3\r\n"
15546        );
15547        // Buckets 200 through 400 have no readings at all and carry the reading
15548        // before the gap either way round. Bucket 500 has a reading that is not
15549        // a number, so it carries whatever the bucket before it in the reading
15550        // direction answered, which is 2 forwards and 3 backwards.
15551        assert_eq!(
15552            f.run(&[
15553                b"TS.RANGE",
15554                b"g",
15555                b"-",
15556                b"+",
15557                b"AGGREGATION",
15558                b"last",
15559                b"100",
15560                b"EMPTY"
15561            ]),
15562            "*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\
15563             *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\
15564             *2\r\n:600\r\n+3\r\n"
15565        );
15566        assert_eq!(
15567            f.run(&[
15568                b"TS.REVRANGE",
15569                b"g",
15570                b"-",
15571                b"+",
15572                b"AGGREGATION",
15573                b"last",
15574                b"100",
15575                b"EMPTY"
15576            ]),
15577            "*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\
15578             *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\
15579             *2\r\n:0\r\n+1\r\n"
15580        );
15581        // And a window that opens on that bucket has nothing in range before it
15582        // to carry, so it answers not a number.
15583        assert_eq!(
15584            f.run(&[
15585                b"TS.RANGE",
15586                b"g",
15587                b"500",
15588                b"600",
15589                b"AGGREGATION",
15590                b"last",
15591                b"100",
15592                b"EMPTY"
15593            ]),
15594            "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15595        );
15596    }
15597
15598    /// The sentences a read answers when its options do not add up, which are
15599    /// the module's own word for word.
15600    #[test]
15601    fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15602        let mut f = Fixture::new();
15603        f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15604        f.run(&[b"SET", b"str", b"x"]);
15605        let cases: &[(&[&[u8]], &str)] = &[
15606            (
15607                &[b"TS.RANGE", b"t"],
15608                "-ERR wrong number of arguments for 'ts.range' command\r\n",
15609            ),
15610            // The key is resolved before a single option is read.
15611            (
15612                &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15613                "-ERR TSDB: the key does not exist\r\n",
15614            ),
15615            (
15616                &[b"TS.RANGE", b"str", b"-", b"+"],
15617                "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15618            ),
15619            (
15620                &[b"TS.RANGE", b"t", b"abc", b"+"],
15621                "-ERR TSDB: wrong fromTimestamp\r\n",
15622            ),
15623            (
15624                &[b"TS.RANGE", b"t", b"-", b"abc"],
15625                "-ERR TSDB: wrong toTimestamp\r\n",
15626            ),
15627            (
15628                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15629                "-ERR TSDB: COUNT argument is missing\r\n",
15630            ),
15631            (
15632                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15633                "-ERR TSDB: Couldn't parse COUNT\r\n",
15634            ),
15635            (
15636                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15637                "-ERR TSDB: Invalid COUNT value\r\n",
15638            ),
15639            (
15640                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15641                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15642            ),
15643            (
15644                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15645                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15646            ),
15647            (
15648                &[
15649                    b"TS.RANGE",
15650                    b"t",
15651                    b"-",
15652                    b"+",
15653                    b"AGGREGATION",
15654                    b"nope",
15655                    b"100",
15656                ],
15657                "-ERR TSDB: Unknown aggregation type\r\n",
15658            ),
15659            (
15660                &[
15661                    b"TS.RANGE",
15662                    b"t",
15663                    b"-",
15664                    b"+",
15665                    b"AGGREGATION",
15666                    b"avg,,min",
15667                    b"100",
15668                ],
15669                "-ERR TSDB: Empty aggregation type in list\r\n",
15670            ),
15671            // The list of names is read before the width is looked at.
15672            (
15673                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15674                "-ERR TSDB: Unknown aggregation type\r\n",
15675            ),
15676            (
15677                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15678                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15679            ),
15680            (
15681                &[
15682                    b"TS.RANGE",
15683                    b"t",
15684                    b"-",
15685                    b"+",
15686                    b"AGGREGATION",
15687                    b"avg",
15688                    b"100",
15689                    b"X",
15690                    b"EMPTY",
15691                ],
15692                "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15693            ),
15694            (
15695                &[
15696                    b"TS.RANGE",
15697                    b"t",
15698                    b"-",
15699                    b"+",
15700                    b"AGGREGATION",
15701                    b"avg",
15702                    b"100",
15703                    b"BUCKETTIMESTAMP",
15704                    b"z",
15705                ],
15706                "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15707            ),
15708            (
15709                &[
15710                    b"TS.RANGE",
15711                    b"t",
15712                    b"-",
15713                    b"+",
15714                    b"AGGREGATION",
15715                    b"avg",
15716                    b"100",
15717                    b"X",
15718                    b"Y",
15719                    b"BUCKETTIMESTAMP",
15720                    b"-",
15721                ],
15722                "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15723                 AGGREGATION flag\r\n",
15724            ),
15725            (
15726                &[
15727                    b"TS.RANGE",
15728                    b"t",
15729                    b"-",
15730                    b"+",
15731                    b"ALIGN",
15732                    b"z",
15733                    b"AGGREGATION",
15734                    b"avg",
15735                    b"100",
15736                ],
15737                "-ERR TSDB: unknown ALIGN parameter\r\n",
15738            ),
15739            (
15740                &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15741                "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15742            ),
15743            (
15744                &[
15745                    b"TS.RANGE",
15746                    b"t",
15747                    b"-",
15748                    b"+",
15749                    b"ALIGN",
15750                    b"-",
15751                    b"AGGREGATION",
15752                    b"avg",
15753                    b"100",
15754                ],
15755                "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15756            ),
15757            (
15758                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15759                "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15760            ),
15761            (
15762                &[
15763                    b"TS.RANGE",
15764                    b"t",
15765                    b"-",
15766                    b"+",
15767                    b"FILTER_BY_VALUE",
15768                    b"x",
15769                    b"2",
15770                ],
15771                "-ERR TSDB: Couldn't parse MIN\r\n",
15772            ),
15773            (
15774                &[
15775                    b"TS.RANGE",
15776                    b"t",
15777                    b"-",
15778                    b"+",
15779                    b"FILTER_BY_VALUE",
15780                    b"1",
15781                    b"y",
15782                ],
15783                "-ERR TSDB: Couldn't parse MAX\r\n",
15784            ),
15785            (
15786                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15787                "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15788            ),
15789        ];
15790        for (argv, want) in cases {
15791            let got = f.run(argv);
15792            assert_eq!(&got, want, "{:?}", argv.last());
15793        }
15794        // The one sentence here that is yo's own rather than the module's, which
15795        // is D-54. A read that would build more rows than yo will build is
15796        // refused instead of attempted.
15797        f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15798        f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15799        assert_eq!(
15800            f.run(&[
15801                b"TS.RANGE",
15802                b"wide",
15803                b"-",
15804                b"+",
15805                b"AGGREGATION",
15806                b"avg",
15807                b"1",
15808                b"EMPTY"
15809            ]),
15810            "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15811        );
15812    }
15813
15814    /// What RESP3 changes on a read, which is only how a number is written.
15815    #[test]
15816    fn resp3_writes_a_read_value_as_a_double() {
15817        let mut f = Fixture::new();
15818        f.out = Out::new(Proto::Resp3);
15819        for (at, v) in [
15820            (b"0".as_slice(), b"1".as_slice()),
15821            (b"100", b"2"),
15822            (b"500", b"nan"),
15823            (b"600", b"3"),
15824        ] {
15825            f.run(&[b"TS.ADD", b"g", at, v]);
15826        }
15827        assert_eq!(
15828            f.run(&[
15829                b"TS.RANGE",
15830                b"g",
15831                b"0",
15832                b"100",
15833                b"AGGREGATION",
15834                b"avg,min",
15835                b"200"
15836            ]),
15837            "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15838        );
15839        assert_eq!(
15840            f.run(&[
15841                b"TS.RANGE",
15842                b"g",
15843                b"500",
15844                b"600",
15845                b"AGGREGATION",
15846                b"last",
15847                b"100",
15848                b"EMPTY"
15849            ]),
15850            "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15851        );
15852    }
15853
15854    /// Two series with an overlap and a gap each, plus a third holding nothing,
15855    /// which is what the joined reads are measured against.
15856    fn joined() -> Fixture {
15857        let mut f = Fixture::new();
15858        f.run(&[b"TS.CREATE", b"z"]);
15859        for (at, v) in [
15860            (b"10".as_slice(), b"1".as_slice()),
15861            (b"20", b"2"),
15862            (b"40", b"4"),
15863            (b"50", b"5"),
15864        ] {
15865            f.run(&[b"TS.ADD", b"x", at, v]);
15866        }
15867        for (at, v) in [
15868            (b"20".as_slice(), b"20".as_slice()),
15869            (b"30", b"30"),
15870            (b"50", b"50"),
15871            (b"60", b"60"),
15872        ] {
15873            f.run(&[b"TS.ADD", b"y", at, v]);
15874        }
15875        f
15876    }
15877
15878    /// The joined read lines its keys up on the timestamp and writes a row as
15879    /// the timestamp and then a nested array of the columns, which is the one
15880    /// shape in the family that is not the flat pair.
15881    #[test]
15882    fn an_nrange_joins_its_keys_on_the_timestamp() {
15883        let mut f = joined();
15884        // One key still nests, so the shape does not depend on the count.
15885        assert_eq!(
15886            f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15887            "*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\
15888             *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15889        );
15890        // A key with no reading where another key has one writes NaN there.
15891        assert_eq!(
15892            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15893            "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15894             *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15895             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15896             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15897             *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15898             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15899        );
15900        // A series holding nothing is a column of NaN and never a row of its
15901        // own, and the same key twice answers twice.
15902        assert_eq!(
15903            f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15904            "*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"
15905        );
15906        assert_eq!(
15907            f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15908            "*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"
15909        );
15910        // COUNT is applied to the joined rows and not to each key, so backwards
15911        // it gives the newest joined row rather than the newest of each.
15912        assert_eq!(
15913            f.run(&[
15914                b"TS.NREVRANGE",
15915                b"2",
15916                b"x",
15917                b"y",
15918                b"-",
15919                b"+",
15920                b"COUNT",
15921                b"1"
15922            ]),
15923            "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15924        );
15925        assert_eq!(
15926            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
15927            "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
15928        );
15929        // The two sample filters are settled a key at a time, before the join.
15930        assert_eq!(
15931            f.run(&[
15932                b"TS.NRANGE",
15933                b"2",
15934                b"x",
15935                b"y",
15936                b"-",
15937                b"+",
15938                b"FILTER_BY_VALUE",
15939                b"2",
15940                b"30"
15941            ]),
15942            "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15943             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15944             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15945             *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
15946        );
15947    }
15948
15949    /// The aggregation on a joined read names one reduction a key and then the
15950    /// one bucket width, and each name may be a comma list, so a row can be
15951    /// wider than the key count.
15952    #[test]
15953    fn an_nrange_aggregation_names_one_reduction_a_key() {
15954        let mut f = joined();
15955        assert_eq!(
15956            f.run(&[
15957                b"TS.NRANGE",
15958                b"2",
15959                b"x",
15960                b"y",
15961                b"-",
15962                b"+",
15963                b"AGGREGATION",
15964                b"sum",
15965                b"sum",
15966                b"20"
15967            ]),
15968            "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
15969             *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
15970             *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
15971             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15972        );
15973        // A comma list on the first key widens the row to three columns.
15974        assert_eq!(
15975            f.run(&[
15976                b"TS.NRANGE",
15977                b"2",
15978                b"x",
15979                b"y",
15980                b"-",
15981                b"+",
15982                b"AGGREGATION",
15983                b"sum,count",
15984                b"avg",
15985                b"20"
15986            ]),
15987            "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
15988             *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
15989             *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
15990             *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
15991        );
15992        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
15993        // sits one or two past the width whatever the key count is.
15994        assert_eq!(
15995            f.run(&[
15996                b"TS.NRANGE",
15997                b"2",
15998                b"x",
15999                b"y",
16000                b"-",
16001                b"+",
16002                b"AGGREGATION",
16003                b"avg",
16004                b"sum",
16005                b"100",
16006                b"EMPTY",
16007                b"BUCKETTIMESTAMP",
16008                b"end"
16009            ]),
16010            "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
16011        );
16012        // A COUNT landing in one of the name slots is a reduction name and not
16013        // the keyword, and the read then has no count at all.
16014        assert_eq!(
16015            f.run(&[
16016                b"TS.NRANGE",
16017                b"2",
16018                b"x",
16019                b"y",
16020                b"-",
16021                b"+",
16022                b"AGGREGATION",
16023                b"avg",
16024                b"COUNT",
16025                b"100"
16026            ]),
16027            "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
16028        );
16029    }
16030
16031    /// The sentences a joined read answers when it does not add up, which are
16032    /// the module's own and come out in the module's own order.
16033    #[test]
16034    fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
16035        let mut f = joined();
16036        f.run(&[b"SET", b"str", b"hi"]);
16037        let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
16038        let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
16039                       must be equal to numkeys\r\n";
16040        let cases: &[(&[&[u8]], &str)] = &[
16041            (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
16042            (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
16043            (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
16044            // Not enough words behind the count for the keys and both ends of
16045            // the span, which is an arity error however many keys were named.
16046            (
16047                &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
16048                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16049            ),
16050            (
16051                &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
16052                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16053            ),
16054            // The reduction names are read before the two ends of the span,
16055            // which no other option is.
16056            (
16057                &[
16058                    b"TS.NRANGE",
16059                    b"2",
16060                    b"x",
16061                    b"y",
16062                    b"abc",
16063                    b"+",
16064                    b"AGGREGATION",
16065                    b"nope",
16066                    b"sum",
16067                    b"100",
16068                ],
16069                "-ERR TSDB: Unknown aggregation type\r\n",
16070            ),
16071            (
16072                &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
16073                "-ERR TSDB: wrong fromTimestamp\r\n",
16074            ),
16075            (
16076                &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
16077                "-ERR TSDB: wrong toTimestamp\r\n",
16078            ),
16079            // A name slot that is missing or holds a number is the count
16080            // sentence, and a width slot that is itself a reduction name is
16081            // that sentence as well.
16082            (
16083                &[
16084                    b"TS.NRANGE",
16085                    b"2",
16086                    b"x",
16087                    b"y",
16088                    b"-",
16089                    b"+",
16090                    b"AGGREGATION",
16091                    b"avg",
16092                ],
16093                numkeys,
16094            ),
16095            (
16096                &[
16097                    b"TS.NRANGE",
16098                    b"2",
16099                    b"x",
16100                    b"y",
16101                    b"-",
16102                    b"+",
16103                    b"AGGREGATION",
16104                    b"100",
16105                    b"sum",
16106                    b"100",
16107                ],
16108                numkeys,
16109            ),
16110            (
16111                &[
16112                    b"TS.NRANGE",
16113                    b"2",
16114                    b"x",
16115                    b"y",
16116                    b"-",
16117                    b"+",
16118                    b"AGGREGATION",
16119                    b"avg",
16120                    b"sum",
16121                    b"sum",
16122                    b"100",
16123                ],
16124                numkeys,
16125            ),
16126            (
16127                &[
16128                    b"TS.NRANGE",
16129                    b"2",
16130                    b"x",
16131                    b"y",
16132                    b"-",
16133                    b"+",
16134                    b"AGGREGATION",
16135                    b"avg",
16136                    b"sum",
16137                    b"abc",
16138                ],
16139                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16140            ),
16141            (
16142                &[
16143                    b"TS.NRANGE",
16144                    b"2",
16145                    b"x",
16146                    b"y",
16147                    b"-",
16148                    b"+",
16149                    b"AGGREGATION",
16150                    b"avg",
16151                    b"sum",
16152                    b"0",
16153                ],
16154                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16155            ),
16156            // With one key none of that applies and the plain parser runs, so a
16157            // lone width is a missing width rather than a count mismatch.
16158            (
16159                &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
16160                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16161            ),
16162            (
16163                &[
16164                    b"TS.NRANGE",
16165                    b"1",
16166                    b"x",
16167                    b"-",
16168                    b"+",
16169                    b"AGGREGATION",
16170                    b"100",
16171                    b"200",
16172                ],
16173                "-ERR TSDB: Unknown aggregation type\r\n",
16174            ),
16175            // The keys come last and in the order they were named.
16176            (
16177                &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
16178                "-ERR TSDB: the key does not exist\r\n",
16179            ),
16180            (
16181                &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
16182                "-ERR WRONGTYPE Operation against a key \
16183                 holding the wrong kind of value\r\n",
16184            ),
16185        ];
16186        for (argv, want) in cases {
16187            let got = f.run(argv);
16188            assert_eq!(&got, want, "{argv:?}");
16189        }
16190    }
16191
16192    /// `TS.READ`, which is a key, one timestamp and everything from there on.
16193    #[test]
16194    fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
16195        let mut f = joined();
16196        assert_eq!(
16197            f.run(&[b"TS.READ", b"x", b"-"]),
16198            "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
16199             *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16200        );
16201        // A plus is the last sample on its own, and a timestamp between two
16202        // samples starts at the one behind it.
16203        assert_eq!(
16204            f.run(&[b"TS.READ", b"x", b"+"]),
16205            "*1\r\n*2\r\n:50\r\n+5\r\n"
16206        );
16207        assert_eq!(
16208            f.run(&[b"TS.READ", b"x", b"25"]),
16209            "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16210        );
16211        // Past the end, a series holding nothing and a key that is not there
16212        // are all the empty array rather than an error.
16213        assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
16214        assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
16215        assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
16216        assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
16217        // The timestamp refusal goes out with nothing in front of it, and a key
16218        // holding something else answers the bare WRONGTYPE rather than the
16219        // module's prefixed one, both unlike the rest of the family.
16220        assert_eq!(
16221            f.run(&[b"TS.READ", b"x", b"abc"]),
16222            "-TSDB: invalid timestamp\r\n"
16223        );
16224        assert_eq!(
16225            f.run(&[b"TS.READ", b"x", b"-1"]),
16226            "-TSDB: invalid timestamp\r\n"
16227        );
16228        f.run(&[b"SET", b"str", b"hi"]);
16229        assert_eq!(
16230            f.run(&[b"TS.READ", b"str", b"-"]),
16231            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
16232        );
16233        // Anything other than exactly three words is an arity error, so there
16234        // is nowhere to put an option even though the table says minus three.
16235        assert_eq!(
16236            f.run(&[b"TS.READ", b"x"]),
16237            "-ERR wrong number of arguments for 'ts.read' command\r\n"
16238        );
16239        assert_eq!(
16240            f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
16241            "-ERR wrong number of arguments for 'ts.read' command\r\n"
16242        );
16243    }
16244
16245    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
16246    /// to read the count to find them.
16247    #[test]
16248    fn getkeys_reads_the_count_of_a_joined_read() {
16249        let mut f = Fixture::new();
16250        assert_eq!(
16251            f.run(&[
16252                b"COMMAND",
16253                b"GETKEYS",
16254                b"TS.NRANGE",
16255                b"2",
16256                b"a",
16257                b"b",
16258                b"-",
16259                b"+"
16260            ]),
16261            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
16262        );
16263        assert_eq!(
16264            f.run(&[
16265                b"COMMAND",
16266                b"GETKEYS",
16267                b"TS.NREVRANGE",
16268                b"1",
16269                b"a",
16270                b"-",
16271                b"+"
16272            ]),
16273            "*1\r\n$1\r\na\r\n"
16274        );
16275        // A count of zero, or one too large for the words that follow it, is
16276        // the server's own refusal and not the module's.
16277        for n in [b"0".as_slice(), b"9", b"abc"] {
16278            assert_eq!(
16279                f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
16280                "-ERR Invalid arguments specified for command\r\n"
16281            );
16282        }
16283    }
16284
16285    /// The five series every test of the label surface works against.
16286    fn labelled() -> Fixture {
16287        let mut f = Fixture::new();
16288        f.run(&[
16289            b"TS.CREATE",
16290            b"a",
16291            b"LABELS",
16292            b"room",
16293            b"kitchen",
16294            b"x",
16295            b"1",
16296        ]);
16297        f.run(&[
16298            b"TS.CREATE",
16299            b"b",
16300            b"LABELS",
16301            b"room",
16302            b"bedroom",
16303            b"x",
16304            b"2",
16305        ]);
16306        f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
16307        f.run(&[b"TS.CREATE", b"d"]);
16308        f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
16309        f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
16310        f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
16311        f
16312    }
16313
16314    /// The filter grammar, which is four steps and a `strtok` rather than a
16315    /// grammar, and which every command that searches on labels shares.
16316    #[test]
16317    fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
16318        let mut f = labelled();
16319        let cases: &[(&[&[u8]], &str)] = &[
16320            // The plain forms, and the order the answer comes back in, which is
16321            // by key name and not by anything the series remembers.
16322            (
16323                &[b"TS.QUERYINDEX", b"room=kitchen"],
16324                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16325            ),
16326            (
16327                &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
16328                "*1\r\n$1\r\na\r\n",
16329            ),
16330            (
16331                &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
16332                "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
16333            ),
16334            // An empty list still counts as something that says which series to
16335            // take, it just never takes any.
16336            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
16337            // Absent and present, neither of which stands on its own.
16338            (
16339                &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
16340                "*1\r\n$1\r\nc\r\n",
16341            ),
16342            (
16343                &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
16344                "*1\r\n$1\r\na\r\n",
16345            ),
16346            (
16347                &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
16348                "-ERR TSDB: please provide at least one matcher\r\n",
16349            ),
16350            // A run of separators is one separator and everything past the
16351            // second field is dropped, so all three of these ask one question.
16352            (
16353                &[b"TS.QUERYINDEX", b"room==kitchen"],
16354                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16355            ),
16356            (
16357                &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
16358                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16359            ),
16360            (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
16361            // A bracket is only a list when it sits straight behind the
16362            // separator, and then the label in front of it has to be there.
16363            (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
16364            (
16365                &[b"TS.QUERYINDEX", b"=(1)"],
16366                "-ERR TSDB: failed parsing labels\r\n",
16367            ),
16368            (
16369                &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
16370                "-ERR TSDB: failed parsing labels\r\n",
16371            ),
16372            (
16373                &[b"TS.QUERYINDEX", b"room=(kitchen"],
16374                "-ERR TSDB: failed parsing labels\r\n",
16375            ),
16376            (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
16377            (
16378                &[b"TS.QUERYINDEX", b"nonsense"],
16379                "-ERR TSDB: failed parsing labels\r\n",
16380            ),
16381            // Nothing here says which series to take.
16382            (
16383                &[b"TS.QUERYINDEX", b"room!=kitchen"],
16384                "-ERR TSDB: please provide at least one matcher\r\n",
16385            ),
16386            // Names and values are both compared byte for byte.
16387            (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
16388            (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
16389            (
16390                &[b"TS.QUERYINDEX"],
16391                "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
16392            ),
16393        ];
16394        for (argv, want) in cases {
16395            let got = f.run(argv);
16396            assert_eq!(&got, want, "{:?}", argv.last());
16397        }
16398    }
16399
16400    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
16401    #[test]
16402    fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16403        let mut f = labelled();
16404        let cases: &[(&[&[u8]], &str)] = &[
16405            (
16406                &[b"TS.QUERYLABELS", b"LABELS"],
16407                "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16408            ),
16409            (
16410                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16411                "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16412            ),
16413            (
16414                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16415                "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16416            ),
16417            // The series wearing `r` twice contributes the smaller of the two
16418            // here, which is not the one it was written down as first.
16419            (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16420            (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16421            (
16422                &[b"TS.QUERYLABELS", b"VALUES"],
16423                "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16424            ),
16425            (
16426                &[b"TS.QUERYLABELS", b"ZZZ"],
16427                "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16428            ),
16429            (
16430                &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16431                "-ERR TSDB: unknown argument, expected FILTER\r\n",
16432            ),
16433            (
16434                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16435                "-ERR TSDB: FILTER given with no filter expressions\r\n",
16436            ),
16437            // With no filter at all every series is taken, which is why the
16438            // first case here answers about `r` as well. A filter that is there
16439            // still has to say which series to take.
16440            (
16441                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16442                "-ERR TSDB: please provide at least one matcher\r\n",
16443            ),
16444            (
16445                &[
16446                    b"TS.QUERYLABELS",
16447                    b"LABELS",
16448                    b"FILTER",
16449                    b"room=kitchen",
16450                    b"x=",
16451                ],
16452                "*1\r\n$4\r\nroom\r\n",
16453            ),
16454        ];
16455        for (argv, want) in cases {
16456            let got = f.run(argv);
16457            assert_eq!(&got, want, "{:?}", argv.last());
16458        }
16459    }
16460
16461    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
16462    /// ways of asking for the labels back alongside it.
16463    #[test]
16464    fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16465        let mut f = labelled();
16466        let cases: &[(&[&[u8]], &str)] = &[
16467            // A series with no samples writes an empty array where the sample
16468            // goes rather than dropping out of the reply.
16469            (
16470                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16471                "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16472                 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16473            ),
16474            (
16475                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16476                "*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\
16477                 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16478                 *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",
16479            ),
16480            // A selected label the series does not wear is a nil, not a gap.
16481            (
16482                &[
16483                    b"TS.MGET",
16484                    b"SELECTED_LABELS",
16485                    b"x",
16486                    b"FILTER",
16487                    b"room=kitchen",
16488                ],
16489                "*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\
16490                 *2\r\n:100\r\n+1.5\r\n\
16491                 *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",
16492            ),
16493            // The other half of the duplicated name rule. This one takes the
16494            // first written down where `TS.QUERYLABELS` takes the smallest.
16495            (
16496                &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16497                "*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",
16498            ),
16499            (
16500                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16501                "*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\
16502                 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16503            ),
16504            // A word that is not an option is ignored, but a missing `FILTER`
16505            // is an arity error whatever else was written.
16506            (
16507                &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16508                "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16509            ),
16510            (
16511                &[b"TS.MGET", b"a", b"b", b"c"],
16512                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16513            ),
16514            (
16515                &[b"TS.MGET", b"FILTER"],
16516                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16517            ),
16518            // Both keyword checks happen before the filter is read, and the two
16519            // sentences spell the second keyword without its `ED`.
16520            (
16521                &[
16522                    b"TS.MGET",
16523                    b"WITHLABELS",
16524                    b"SELECTED_LABELS",
16525                    b"x",
16526                    b"FILTER",
16527                    b"bad",
16528                ],
16529                "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16530            ),
16531            (
16532                &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16533                "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16534            ),
16535        ];
16536        for (argv, want) in cases {
16537            let got = f.run(argv);
16538            assert_eq!(&got, want, "{:?}", argv.last());
16539        }
16540    }
16541
16542    /// What RESP3 changes across the label surface, which is a set where there
16543    /// was an array and a map where there was a pair of them.
16544    #[test]
16545    fn resp3_writes_the_label_surface_as_sets_and_maps() {
16546        let mut f = labelled();
16547        f.out = Out::new(Proto::Resp3);
16548        let cases: &[(&[&[u8]], &str)] = &[
16549            (
16550                &[b"TS.QUERYINDEX", b"room=kitchen"],
16551                "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16552            ),
16553            (
16554                &[b"TS.QUERYLABELS", b"LABELS"],
16555                "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16556            ),
16557            (
16558                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16559                "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16560            ),
16561            // The key stops being the first of three and becomes the map key,
16562            // and the labels stop being pairs and become a map of their own.
16563            (
16564                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16565                "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16566                 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16567            ),
16568            (
16569                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16570                "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16571                 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16572                 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16573            ),
16574            (
16575                &[
16576                    b"TS.MGET",
16577                    b"SELECTED_LABELS",
16578                    b"x",
16579                    b"FILTER",
16580                    b"room=kitchen",
16581                ],
16582                "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16583                 *2\r\n:100\r\n,1.5\r\n\
16584                 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16585            ),
16586            // A map with a name in it twice, which is what a series wearing one
16587            // label name twice turns into.
16588            (
16589                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16590                "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16591                 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16592            ),
16593        ];
16594        for (argv, want) in cases {
16595            let got = f.run(argv);
16596            assert_eq!(&got, want, "{:?}", argv.last());
16597        }
16598    }
16599
16600    /// The same five series with enough samples in them for a group to have
16601    /// something to fold.
16602    fn spanned() -> Fixture {
16603        let mut f = labelled();
16604        f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16605        f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16606        f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16607        f
16608    }
16609
16610    /// A span read out of every series a filter takes, with and without a group
16611    /// over the top of it.
16612    #[test]
16613    fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16614        let mut f = spanned();
16615        let cases: &[(&[&[u8]], &str)] = &[
16616            (
16617                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16618                "*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\
16619                 *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",
16620            ),
16621            // Newest first is applied to each series before anything else sees
16622            // the rows.
16623            (
16624                &[
16625                    b"TS.MREVRANGE",
16626                    b"-",
16627                    b"+",
16628                    b"WITHLABELS",
16629                    b"FILTER",
16630                    b"room=kitchen",
16631                ],
16632                "*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\
16633                 *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\
16634                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16635                 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16636            ),
16637            // A label a series does not wear comes back against a nil rather
16638            // than being left out.
16639            (
16640                &[
16641                    b"TS.MRANGE",
16642                    b"-",
16643                    b"+",
16644                    b"SELECTED_LABELS",
16645                    b"x",
16646                    b"FILTER",
16647                    b"room=kitchen",
16648                ],
16649                "*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\
16650                 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16651                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16652                 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16653            ),
16654            // The fold: 100 is in both series and adds up, the other two are in
16655            // one each and are still rows.
16656            (
16657                &[
16658                    b"TS.MRANGE",
16659                    b"-",
16660                    b"+",
16661                    b"FILTER",
16662                    b"room=kitchen",
16663                    b"GROUPBY",
16664                    b"room",
16665                    b"REDUCE",
16666                    b"sum",
16667                ],
16668                "*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\
16669                 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16670            ),
16671            // RESP2 has nowhere to put the reducer and the member keys, so a
16672            // group wearing labels writes them as two more labels.
16673            (
16674                &[
16675                    b"TS.MRANGE",
16676                    b"-",
16677                    b"+",
16678                    b"WITHLABELS",
16679                    b"FILTER",
16680                    b"room=kitchen",
16681                    b"GROUPBY",
16682                    b"room",
16683                    b"REDUCE",
16684                    b"max",
16685                ],
16686                "*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\
16687                 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16688                 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16689                 *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",
16690            ),
16691            // A count is applied to each member and then again to the fold.
16692            (
16693                &[
16694                    b"TS.MREVRANGE",
16695                    b"-",
16696                    b"+",
16697                    b"COUNT",
16698                    b"1",
16699                    b"FILTER",
16700                    b"room=kitchen",
16701                    b"GROUPBY",
16702                    b"room",
16703                    b"REDUCE",
16704                    b"count",
16705                ],
16706                "*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",
16707            ),
16708            // Nothing wears the label, so nothing is in any group.
16709            (
16710                &[
16711                    b"TS.MRANGE",
16712                    b"-",
16713                    b"+",
16714                    b"FILTER",
16715                    b"room=kitchen",
16716                    b"GROUPBY",
16717                    b"nope",
16718                    b"REDUCE",
16719                    b"sum",
16720                ],
16721                "*0\r\n",
16722            ),
16723            (
16724                &[
16725                    b"TS.MRANGE",
16726                    b"-",
16727                    b"+",
16728                    b"AGGREGATION",
16729                    b"sum,avg",
16730                    b"100",
16731                    b"FILTER",
16732                    b"room=bedroom",
16733                ],
16734                "*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",
16735            ),
16736            // The errors, in the order they are looked for.
16737            (
16738                &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16739                "-ERR TSDB: missing FILTER argument\r\n",
16740            ),
16741            (
16742                &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16743                "-ERR TSDB: missing labels for filter argument\r\n",
16744            ),
16745            (
16746                &[
16747                    b"TS.MRANGE",
16748                    b"-",
16749                    b"+",
16750                    b"GROUPBY",
16751                    b"room",
16752                    b"REDUCE",
16753                    b"sum",
16754                    b"FILTER",
16755                    b"room=kitchen",
16756                ],
16757                "-ERR TSDB: GROUPBY should always come after filter\r\n",
16758            ),
16759            // The group is four words from the end here, so the length is what
16760            // is wrong with it.
16761            (
16762                &[
16763                    b"TS.MRANGE",
16764                    b"-",
16765                    b"+",
16766                    b"FILTER",
16767                    b"room=kitchen",
16768                    b"GROUPBY",
16769                    b"room",
16770                    b"REDUCE",
16771                    b"sum",
16772                    b"x",
16773                ],
16774                "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16775            ),
16776            // And here it is not, so its words are filters and answer first.
16777            (
16778                &[
16779                    b"TS.MRANGE",
16780                    b"-",
16781                    b"+",
16782                    b"FILTER",
16783                    b"nope",
16784                    b"GROUPBY",
16785                    b"room",
16786                    b"REDUCE",
16787                    b"sum",
16788                    b"x",
16789                ],
16790                "-ERR TSDB: failed parsing labels\r\n",
16791            ),
16792            (
16793                &[
16794                    b"TS.MRANGE",
16795                    b"-",
16796                    b"+",
16797                    b"FILTER",
16798                    b"room=kitchen",
16799                    b"GROUPBY",
16800                    b"room",
16801                    b"REDUCE",
16802                    b"twa",
16803                ],
16804                "-ERR TSDB: Invalid reducer type\r\n",
16805            ),
16806            (
16807                &[
16808                    b"TS.MRANGE",
16809                    b"-",
16810                    b"+",
16811                    b"AGGREGATION",
16812                    b"sum,avg",
16813                    b"100",
16814                    b"FILTER",
16815                    b"room=kitchen",
16816                    b"GROUPBY",
16817                    b"room",
16818                    b"REDUCE",
16819                    b"sum",
16820                ],
16821                "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16822            ),
16823            // The label list ends at a keyword, so this is a `COUNT` with a
16824            // `FILTER` where its number should be.
16825            (
16826                &[
16827                    b"TS.MRANGE",
16828                    b"-",
16829                    b"+",
16830                    b"SELECTED_LABELS",
16831                    b"COUNT",
16832                    b"FILTER",
16833                    b"room=kitchen",
16834                ],
16835                "-ERR TSDB: Couldn't parse COUNT\r\n",
16836            ),
16837        ];
16838        for (argv, want) in cases {
16839            let got = f.run(argv);
16840            assert_eq!(&got, want, "{argv:?}");
16841        }
16842    }
16843
16844    /// The multi key reads on RESP3, where the key becomes a map key and the
16845    /// reducer and the member keys become fields of their own.
16846    #[test]
16847    fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16848        let mut f = spanned();
16849        f.out = Out::new(Proto::Resp3);
16850        let cases: &[(&[&[u8]], &str)] = &[
16851            (
16852                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16853                "%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\
16854                 *1\r\n*2\r\n:200\r\n,2\r\n",
16855            ),
16856            // The reductions a read asked for, which RESP2 has no room for at
16857            // all and which is empty on a read that asked for none.
16858            (
16859                &[
16860                    b"TS.MRANGE",
16861                    b"-",
16862                    b"+",
16863                    b"AGGREGATION",
16864                    b"sum,avg",
16865                    b"100",
16866                    b"FILTER",
16867                    b"room=bedroom",
16868                ],
16869                "%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\
16870                 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16871            ),
16872            (
16873                &[
16874                    b"TS.MRANGE",
16875                    b"-",
16876                    b"+",
16877                    b"FILTER",
16878                    b"room=kitchen",
16879                    b"GROUPBY",
16880                    b"room",
16881                    b"REDUCE",
16882                    b"sum",
16883                ],
16884                "%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\
16885                 $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\
16886                 *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",
16887            ),
16888            // The labels hold only the pair the group was made on, because the
16889            // reducer and the sources have somewhere else to go.
16890            (
16891                &[
16892                    b"TS.MRANGE",
16893                    b"-",
16894                    b"+",
16895                    b"WITHLABELS",
16896                    b"FILTER",
16897                    b"room=kitchen",
16898                    b"GROUPBY",
16899                    b"room",
16900                    b"REDUCE",
16901                    b"max",
16902                ],
16903                "%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\
16904                 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16905                 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16906                 *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",
16907            ),
16908            (
16909                &[
16910                    b"TS.MRANGE",
16911                    b"-",
16912                    b"+",
16913                    b"FILTER",
16914                    b"room=kitchen",
16915                    b"GROUPBY",
16916                    b"nope",
16917                    b"REDUCE",
16918                    b"sum",
16919                ],
16920                "%0\r\n",
16921            ),
16922        ];
16923        for (argv, want) in cases {
16924            let got = f.run(argv);
16925            assert_eq!(&got, want, "{argv:?}");
16926        }
16927    }
16928
16929    /// `TS.CREATERULE`, whose refusals come in an order of their own.
16930    #[test]
16931    fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
16932        let mut f = Fixture::new();
16933        f.run(&[b"TS.CREATE", b"src"]);
16934        f.run(&[b"TS.CREATE", b"dst"]);
16935        f.run(&[b"SET", b"plain", b"v"]);
16936        let cases: &[(&[&[u8]], &str)] = &[
16937            // The width is read before the reduction, the reduction before the
16938            // width being above zero, and all three before either key is looked
16939            // at, so a command that is wrong twice complains about the first.
16940            (
16941                &[
16942                    b"TS.CREATERULE",
16943                    b"src",
16944                    b"dst",
16945                    b"AGGREGATION",
16946                    b"nope",
16947                    b"x",
16948                ],
16949                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16950            ),
16951            (
16952                &[
16953                    b"TS.CREATERULE",
16954                    b"src",
16955                    b"dst",
16956                    b"AGGREGATION",
16957                    b"nope",
16958                    b"10",
16959                ],
16960                "-ERR TSDB: Unknown aggregation type\r\n",
16961            ),
16962            (
16963                &[
16964                    b"TS.CREATERULE",
16965                    b"src",
16966                    b"dst",
16967                    b"AGGREGATION",
16968                    b"avg",
16969                    b"0",
16970                ],
16971                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16972            ),
16973            (
16974                &[
16975                    b"TS.CREATERULE",
16976                    b"src",
16977                    b"dst",
16978                    b"AGGREGATION",
16979                    b"avg",
16980                    b"10",
16981                    b"x",
16982                ],
16983                "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
16984            ),
16985            (
16986                &[
16987                    b"TS.CREATERULE",
16988                    b"src",
16989                    b"src",
16990                    b"AGGREGATION",
16991                    b"avg",
16992                    b"10",
16993                ],
16994                "-ERR TSDB: the source key and destination key should be different\r\n",
16995            ),
16996            // A key holding something else answers the same as a key that is not
16997            // there at all, because the source is looked up first and neither of
16998            // them is a series.
16999            (
17000                &[
17001                    b"TS.CREATERULE",
17002                    b"nope",
17003                    b"plain",
17004                    b"AGGREGATION",
17005                    b"avg",
17006                    b"10",
17007                ],
17008                "-ERR TSDB: the key does not exist\r\n",
17009            ),
17010            (
17011                &[
17012                    b"TS.CREATERULE",
17013                    b"src",
17014                    b"nope",
17015                    b"AGGREGATION",
17016                    b"avg",
17017                    b"10",
17018                ],
17019                "-ERR TSDB: the key does not exist\r\n",
17020            ),
17021            // A keyword other than AGGREGATION is an arity error rather than a
17022            // syntax one, because the arity is all that is checked.
17023            (
17024                &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
17025                "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
17026            ),
17027            (
17028                &[
17029                    b"TS.CREATERULE",
17030                    b"src",
17031                    b"dst",
17032                    b"AGGREGATION",
17033                    b"avg",
17034                    b"10",
17035                ],
17036                "+OK\r\n",
17037            ),
17038            // The link is now in place, so the same rule again is refused from
17039            // the destination's end.
17040            (
17041                &[
17042                    b"TS.CREATERULE",
17043                    b"src",
17044                    b"dst",
17045                    b"AGGREGATION",
17046                    b"avg",
17047                    b"10",
17048                ],
17049                "-ERR TSDB: the destination key already has a src rule\r\n",
17050            ),
17051            // A source that is already someone's destination, and a destination
17052            // that is already someone's source, are two different sentences.
17053            (
17054                &[
17055                    b"TS.CREATERULE",
17056                    b"dst",
17057                    b"src",
17058                    b"AGGREGATION",
17059                    b"avg",
17060                    b"10",
17061                ],
17062                "-ERR TSDB: the source key already has a source rule\r\n",
17063            ),
17064            (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
17065            (
17066                &[b"TS.DELETERULE", b"src", b"dst"],
17067                "-ERR TSDB: compaction rule does not exist\r\n",
17068            ),
17069            // The source is looked up and the destination is not, so a missing
17070            // destination is a missing rule and a missing source is a missing
17071            // key, which is the other way round from `TS.CREATERULE`.
17072            (
17073                &[b"TS.DELETERULE", b"src", b"nope"],
17074                "-ERR TSDB: compaction rule does not exist\r\n",
17075            ),
17076            (
17077                &[b"TS.DELETERULE", b"nope", b"dst"],
17078                "-ERR TSDB: the key does not exist\r\n",
17079            ),
17080        ];
17081        for (argv, want) in cases {
17082            let got = f.run(argv);
17083            assert_eq!(&got, want, "{argv:?}");
17084        }
17085    }
17086
17087    /// What a rule writes, which is every bucket but the one it is filling.
17088    #[test]
17089    fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
17090        let mut f = Fixture::new();
17091        f.run(&[b"TS.CREATE", b"src"]);
17092        f.run(&[b"TS.CREATE", b"dst"]);
17093        // The readings written before the rule was made are not folded, so the
17094        // destination is still empty after the first two.
17095        f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
17096        f.run(&[
17097            b"TS.CREATERULE",
17098            b"src",
17099            b"dst",
17100            b"AGGREGATION",
17101            b"sum",
17102            b"100",
17103        ]);
17104        f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
17105        assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
17106        // The bucket the rule is filling holds only what it was given, so it is
17107        // 2 rather than 3, and it is written when a reading lands past it.
17108        assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
17109        f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
17110        assert_eq!(
17111            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17112            "*1\r\n*2\r\n:0\r\n+2\r\n"
17113        );
17114        // A reading into a bucket that has already been written works that
17115        // bucket out again over everything the source now holds.
17116        f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
17117        assert_eq!(
17118            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17119            "*1\r\n*2\r\n:0\r\n+11\r\n"
17120        );
17121        // Deleting from the source works the buckets it touched out again and
17122        // reopens the newest one, so `LATEST` starts from the whole bucket.
17123        assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
17124        assert_eq!(
17125            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17126            "*1\r\n*2\r\n:0\r\n+8\r\n"
17127        );
17128        assert_eq!(
17129            f.run(&[b"TS.GET", b"dst", b"LATEST"]),
17130            "*2\r\n:100\r\n+4\r\n"
17131        );
17132        // The link shows on both ends, and dropping either key takes it down.
17133        assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
17134        f.run(&[b"DEL", b"dst"]);
17135        assert_eq!(
17136            f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
17137            "-ERR TSDB: compaction rule does not exist\r\n"
17138        );
17139    }
17140
17141    /// The three shapes an `XADD` id can take, and the one rule behind all of
17142    /// them.
17143    #[test]
17144    fn xadd_ids_only_ever_go_up() {
17145        let mut f = Fixture::new();
17146        // A bare millisecond is that millisecond and sequence zero.
17147        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
17148        // And `5-*` is the next free sequence inside it.
17149        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
17150        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
17151        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
17152        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17153
17154        assert!(
17155            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
17156                .contains("equal or smaller")
17157        );
17158        assert!(
17159            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
17160                .contains("must be greater than 0-0")
17161        );
17162        assert!(
17163            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
17164                .contains("Invalid stream ID")
17165        );
17166        // The pairs have to be pairs, and Redis calls an odd one an arity error
17167        // rather than a syntax error even though the table has already passed.
17168        assert!(
17169            f.run(&[b"XADD", b"s", b"*", b"a"])
17170                .contains("wrong number of arguments")
17171        );
17172
17173        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
17174        // producer can tell nobody is consuming this yet from the write landed.
17175        assert_eq!(
17176            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
17177            "$-1\r\n"
17178        );
17179        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
17180        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
17181        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
17182    }
17183
17184    /// The trim options, which are three keywords that disagree about how many
17185    /// arguments they take.
17186    #[test]
17187    fn trimming_reads_its_options_the_way_redis_does() {
17188        let mut f = Fixture::new();
17189        for i in 1..=10u32 {
17190            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17191        }
17192        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
17193        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17194        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
17195        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17196
17197        // One argument after the keyword and the `~` is read as the threshold,
17198        // which is what a real server does and is the reason this is a number
17199        // complaint and not a syntax one.
17200        assert!(
17201            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
17202                .contains("not an integer")
17203        );
17204        assert!(
17205            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
17206                .contains("MAXLEN argument must be >= 0")
17207        );
17208        // The strategy check runs before the approximation check, so a LIMIT
17209        // with neither is told about the missing strategy.
17210        assert!(
17211            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
17212                .contains("without specifying a trimming strategy")
17213        );
17214        assert!(
17215            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
17216                .contains("without the special ~ option")
17217        );
17218        assert!(
17219            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
17220                .contains("at the same time are not compatible")
17221        );
17222        // NOMKSTREAM is XADD's and XTRIM does not take it.
17223        assert!(
17224            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
17225                .contains("syntax error")
17226        );
17227        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
17228    }
17229
17230    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
17231    #[test]
17232    fn xrange_looks_the_key_up_before_it_reads_the_count() {
17233        let mut f = Fixture::new();
17234        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
17235        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
17236
17237        assert_eq!(
17238            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17239            "*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\
17240             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17241        );
17242        assert_eq!(
17243            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
17244            "*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"
17245        );
17246        // The exclusive bound is stepped after the missing sequence is filled
17247        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
17248        // `6-1` is still in the range.
17249        assert_eq!(
17250            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
17251            "*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\
17252             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17253        );
17254        assert_eq!(
17255            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
17256            "*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"
17257        );
17258        assert!(
17259            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
17260                .contains("Invalid stream ID")
17261        );
17262
17263        // The two kinds of nothing. A key that is not there is an empty array
17264        // and a key that is there with a count of zero is a null array, because
17265        // the lookup happens first.
17266        assert_eq!(
17267            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
17268            "*0\r\n"
17269        );
17270        assert_eq!(
17271            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
17272            "*-1\r\n"
17273        );
17274        f.run(&[b"SET", b"str", b"v"]);
17275        assert!(
17276            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
17277                .starts_with("-WRONGTYPE")
17278        );
17279        // The count is read in a loop, so the last one wins.
17280        assert_eq!(
17281            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
17282            "*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"
17283        );
17284    }
17285
17286    /// `XDEL` and `XACK` check every id before they touch any of them.
17287    #[test]
17288    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
17289        let mut f = Fixture::new();
17290        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17291        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17292        assert!(
17293            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
17294                .contains("Invalid stream ID")
17295        );
17296        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17297        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
17298        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
17299        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
17300        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
17301    }
17302
17303    /// `XGROUP`, and the two different complaints it makes about arguments.
17304    #[test]
17305    fn xgroup_has_an_arity_per_subcommand() {
17306        let mut f = Fixture::new();
17307        assert!(
17308            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17309                .contains("requires the key")
17310        );
17311        assert_eq!(
17312            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
17313            "+OK\r\n"
17314        );
17315        // A second CREATE is BUSYGROUP and not an ordinary error, because a
17316        // client racing another one to make a group branches on the prefix.
17317        assert!(
17318            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17319                .starts_with("-BUSYGROUP")
17320        );
17321        assert_eq!(
17322            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17323            ":1\r\n"
17324        );
17325        assert_eq!(
17326            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17327            ":0\r\n"
17328        );
17329        assert_eq!(
17330            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
17331            ":0\r\n"
17332        );
17333
17334        // Below the subcommand's own arity is an arity error naming the pair.
17335        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
17336        assert!(
17337            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
17338            "{short}"
17339        );
17340        // At or above it in a shape the handler will not take is the other one.
17341        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
17342        assert!(
17343            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
17344            "{odd}"
17345        );
17346        assert!(
17347            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
17348                .contains("Try XGROUP HELP")
17349        );
17350
17351        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
17352        assert!(
17353            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
17354                .starts_with("-NOGROUP")
17355        );
17356        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
17357        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
17358        assert!(
17359            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
17360                .contains("requires the key")
17361        );
17362    }
17363
17364    /// A group read, an acknowledgement, and what is left in between.
17365    #[test]
17366    fn xreadgroup_hands_out_and_xack_takes_back() {
17367        let mut f = Fixture::new();
17368        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17369        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17370        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17371
17372        let first = f.run(&[
17373            b"XREADGROUP",
17374            b"GROUP",
17375            b"g",
17376            b"c1",
17377            b"COUNT",
17378            b"1",
17379            b"STREAMS",
17380            b"s",
17381            b">",
17382        ]);
17383        assert_eq!(
17384            first,
17385            "*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"
17386        );
17387        // A history read names its stream even with nothing to show, which is
17388        // the difference between it and a `>` read that found nothing.
17389        assert_eq!(
17390            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
17391            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
17392        );
17393        assert_eq!(
17394            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17395            "*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"
17396        );
17397
17398        assert_eq!(
17399            f.run(&[b"XPENDING", b"s", b"g"]),
17400            "*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"
17401        );
17402        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17403        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17404        // Empty is four nulls and not a zero with three empty things.
17405        assert_eq!(
17406            f.run(&[b"XPENDING", b"s", b"g"]),
17407            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17408        );
17409
17410        // A history read of an entry that has since been deleted is the id with
17411        // a null beside it, so the consumer can still acknowledge it.
17412        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17413        f.run(&[b"XDEL", b"s", b"2-1"]);
17414        assert_eq!(
17415            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17416            "*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"
17417        );
17418
17419        // The group lookup runs before the id parse, so a `+` at a stream with
17420        // no such group is told about the group and not about the id.
17421        assert!(
17422            f.run(&[
17423                b"XREADGROUP",
17424                b"GROUP",
17425                b"nope",
17426                b"c",
17427                b"STREAMS",
17428                b"s",
17429                b"+"
17430            ])
17431            .starts_with("-NOGROUP")
17432        );
17433        assert!(
17434            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17435                .contains("meaningless in the context of XREADGROUP")
17436        );
17437        assert!(
17438            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17439                .contains("only supported by XREADGROUP")
17440        );
17441        assert!(
17442            f.run(&[
17443                b"XREADGROUP",
17444                b"GROUP",
17445                b"g",
17446                b"c",
17447                b"STREAMS",
17448                b"s",
17449                b"a",
17450                b"b"
17451            ])
17452            .contains("Unbalanced 'xreadgroup' list of streams")
17453        );
17454    }
17455
17456    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
17457    /// answer.
17458    #[test]
17459    fn xread_with_no_block_writes_the_null_itself() {
17460        let mut f = Fixture::new();
17461        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17462        assert_eq!(
17463            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17464            "*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"
17465        );
17466        // Nothing new is a null array and not an empty one, and a stream with
17467        // nothing new is left out rather than sent with an empty list.
17468        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17469        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17470        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17471        assert_eq!(
17472            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17473            "*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"
17474        );
17475        // `$` is the last id, so nothing that is already there comes back.
17476        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17477        // And `+` is the last entry, whatever COUNT says.
17478        assert_eq!(
17479            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17480            "*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"
17481        );
17482        // A count of zero means unlimited here, which is the opposite of what it
17483        // means to XRANGE.
17484        assert_eq!(
17485            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17486            "*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"
17487        );
17488        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
17489        assert!(
17490            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17491                .contains("not an integer")
17492        );
17493        assert!(
17494            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17495                .contains("timeout is negative")
17496        );
17497        assert!(
17498            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17499                .contains("Unbalanced 'xread' list of streams")
17500        );
17501    }
17502
17503    /// A blocked reader, and the two ways it stops being blocked.
17504    #[test]
17505    fn a_blocked_xread_wakes_on_the_next_entry() {
17506        let mut f = Fixture::new();
17507        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17508        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17509        assert_eq!(flow, Flow::Block);
17510        assert!(reply.is_empty());
17511
17512        // Everybody parked on the stream gets the entry, because a read takes
17513        // nothing away. That is the difference between this and BLPOP.
17514        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17515        assert_eq!(flow, Flow::Block);
17516        assert_eq!(f.server.parked(), 2);
17517
17518        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17519        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";
17520        for at in 0..2 {
17521            let mut out = Out::new(Proto::Resp2);
17522            assert!(f.server.serve_waiter(at, 0, &mut out));
17523            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17524        }
17525
17526        // And a deadline that runs out is a null array, the same as a plain
17527        // XREAD that found nothing.
17528        f.server.forget_waiters(7);
17529        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17530        assert_eq!(flow, Flow::Block);
17531        let mut out = Out::new(Proto::Resp2);
17532        assert!(!f.server.serve_waiter(0, 0, &mut out));
17533        assert!(out.as_slice().is_empty());
17534        assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17535        assert_eq!(
17536            core::str::from_utf8(out.as_slice()).expect("ascii"),
17537            "*-1\r\n"
17538        );
17539    }
17540
17541    /// A blocked group reader whose group is destroyed under it.
17542    #[test]
17543    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17544        let mut f = Fixture::new();
17545        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17546        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17547        let (flow, _) = f.flow(&[
17548            b"XREADGROUP",
17549            b"GROUP",
17550            b"g",
17551            b"c",
17552            b"BLOCK",
17553            b"0",
17554            b"STREAMS",
17555            b"s",
17556            b">",
17557        ]);
17558        assert_eq!(flow, Flow::Block);
17559
17560        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17561        let mut out = Out::new(Proto::Resp2);
17562        assert!(f.server.serve_waiter(0, 0, &mut out));
17563        // The ordinary sentence and not a special one about having been parked,
17564        // which is what a running 8.10 sends.
17565        assert_eq!(
17566            core::str::from_utf8(out.as_slice()).expect("ascii"),
17567            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17568        );
17569    }
17570
17571    /// `XCLAIM`, whose argument shape is the odd one in the group.
17572    #[test]
17573    fn xclaim_reads_ids_until_one_will_not_parse() {
17574        let mut f = Fixture::new();
17575        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17576        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17577        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17578        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17579
17580        // Everything after the first argument that is not an id is an option, so
17581        // a `-` is an unrecognised option and not a bad id.
17582        assert!(
17583            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17584                .contains("Unrecognized XCLAIM option '-'")
17585        );
17586        assert_eq!(
17587            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17588            "*1\r\n$3\r\n1-1\r\n"
17589        );
17590        // An id that is pending but whose entry has gone is an empty answer, and
17591        // it leaves the pending list on the way past.
17592        f.run(&[b"XDEL", b"s", b"2-1"]);
17593        assert_eq!(
17594            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17595            "*0\r\n"
17596        );
17597        assert!(
17598            f.run(&[b"XPENDING", b"s", b"g"])
17599                .starts_with("*4\r\n:1\r\n")
17600        );
17601        assert!(
17602            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17603                .starts_with("-NOGROUP")
17604        );
17605        assert!(
17606            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17607                .contains("Invalid min-idle-time argument for XCLAIM")
17608        );
17609    }
17610
17611    /// `XAUTOCLAIM`, and the third value nobody expects.
17612    #[test]
17613    fn xautoclaim_reports_what_it_dropped() {
17614        let mut f = Fixture::new();
17615        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17616        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17617        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17618        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17619        f.run(&[b"XDEL", b"s", b"1-1"]);
17620
17621        // The cursor, what was claimed, and what was dropped for no longer being
17622        // in the stream. The third one is what makes a sweep converge.
17623        assert_eq!(
17624            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17625            "*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"
17626        );
17627        assert!(
17628            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17629                .contains("COUNT must be > 0")
17630        );
17631        assert!(
17632            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17633                .starts_with("-NOGROUP")
17634        );
17635    }
17636
17637    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
17638    #[test]
17639    fn xdelex_answers_one_integer_an_id() {
17640        let mut f = Fixture::new();
17641        for i in 1..=4 {
17642            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17643        }
17644        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17645        f.run(&[
17646            b"XREADGROUP",
17647            b"GROUP",
17648            b"g",
17649            b"c",
17650            b"COUNT",
17651            b"2",
17652            b"STREAMS",
17653            b"s",
17654            b">",
17655        ]);
17656
17657        // One means gone and minus one means it was not there to start with.
17658        assert_eq!(
17659            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17660            "*2\r\n:1\r\n:-1\r\n"
17661        );
17662        // `KEEPREF` leaves the pending entry behind, so the group still counts
17663        // the one it was handed even though the entry has gone.
17664        assert!(
17665            f.run(&[b"XPENDING", b"s", b"g"])
17666                .starts_with("*4\r\n:2\r\n")
17667        );
17668        // `DELREF` takes it out of every pending list on the way past.
17669        assert_eq!(
17670            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17671            "*1\r\n:1\r\n"
17672        );
17673        // `1-1` is still in the list, because the delete before it said KEEPREF.
17674        assert_eq!(
17675            f.run(&[b"XPENDING", b"s", b"g"]),
17676            "*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"
17677        );
17678
17679        // Two means somebody still wants it, and the question is wider than the
17680        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
17681        // refused even though no consumer has ever been handed it.
17682        assert_eq!(
17683            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17684            "*2\r\n:2\r\n:2\r\n"
17685        );
17686
17687        // A key that is not there answers minus ones without reading the IDs.
17688        assert_eq!(
17689            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17690            "*2\r\n:-1\r\n:-1\r\n"
17691        );
17692        // A key that is there validates every ID before deleting any of them.
17693        assert!(
17694            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17695                .starts_with("-ERR Invalid stream ID")
17696        );
17697        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17698
17699        assert!(
17700            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17701                .contains("Number of IDs must be a positive integer")
17702        );
17703        assert!(
17704            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17705                .contains("The `numids` parameter must match the number of arguments")
17706        );
17707        // The condition is one word, so a second one is a syntax error, and so
17708        // is one ID more than the count promised.
17709        assert!(
17710            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17711                .starts_with("-ERR syntax error")
17712        );
17713        assert!(
17714            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17715                .starts_with("-ERR syntax error")
17716        );
17717        // The key is looked up first, so the wrong type beats the syntax.
17718        f.run(&[b"SET", b"str", b"v"]);
17719        assert!(
17720            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17721                .starts_with("-WRONGTYPE")
17722        );
17723    }
17724
17725    /// `XACKDEL`, whose reply is about the pending list and not about the log.
17726    #[test]
17727    fn xackdel_reports_what_the_group_was_holding() {
17728        let mut f = Fixture::new();
17729        for i in 1..=3 {
17730            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17731        }
17732        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17733        f.run(&[
17734            b"XREADGROUP",
17735            b"GROUP",
17736            b"g",
17737            b"c",
17738            b"COUNT",
17739            b"1",
17740            b"STREAMS",
17741            b"s",
17742            b">",
17743        ]);
17744
17745        // Minus one is not about the stream: `2-1` is sitting there unread and
17746        // still answers minus one, because the group was not holding it. It also
17747        // stays, since only an ID that was acknowledged is deleted.
17748        assert_eq!(
17749            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17750            "*2\r\n:1\r\n:-1\r\n"
17751        );
17752        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17753
17754        // A missing group is minus one an ID and not a NOGROUP.
17755        assert_eq!(
17756            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17757            "*1\r\n:-1\r\n"
17758        );
17759        assert_eq!(
17760            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17761            "*1\r\n:-1\r\n"
17762        );
17763
17764        // The acknowledgement happens whatever the condition says, so an ACKED
17765        // that answers two has still emptied the pending list.
17766        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17767        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17768        assert_eq!(
17769            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17770            "*1\r\n:2\r\n"
17771        );
17772        assert_eq!(
17773            f.run(&[b"XPENDING", b"s", b"g"]),
17774            "*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"
17775        );
17776        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17777    }
17778
17779    /// `XNACK`, which hands an entry back to nobody.
17780    #[test]
17781    fn xnack_releases_an_entry_for_the_next_claim() {
17782        let mut f = Fixture::new();
17783        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17784        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17785        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17786        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17787        // Twice, so the delivery count is two and the words have something to
17788        // do with it.
17789        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17790
17791        assert_eq!(
17792            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17793            ":1\r\n"
17794        );
17795        // No owner, no idle time, and the count left where it was. A released
17796        // entry reads as idle for longer than any min-idle-time, which is what
17797        // puts it at the front of the next claim.
17798        assert_eq!(
17799            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17800            "*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"
17801        );
17802        // The consumer no longer holds it, so a filtered XPENDING skips it.
17803        assert_eq!(
17804            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17805            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17806        );
17807        // The bookmark did not move, so a `>` read will not hand it out again.
17808        assert_eq!(
17809            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17810            "*-1\r\n"
17811        );
17812        // A claim at any min-idle-time takes it.
17813        assert_eq!(
17814            f.run(&[
17815                b"XAUTOCLAIM",
17816                b"s",
17817                b"g",
17818                b"c2",
17819                b"99999999",
17820                b"-",
17821                b"JUSTID"
17822            ]),
17823            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17824        );
17825
17826        // `SILENT` takes one off the count rather than putting it back to zero,
17827        // which only shows on an entry that has been handed out more than once.
17828        // It was delivered and then claimed, so it is on two and goes to one.
17829        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17830        assert!(
17831            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17832                .contains(":-1\r\n:1\r\n")
17833        );
17834        // And it stops at zero rather than wrapping.
17835        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17836        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17837        assert!(
17838            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17839                .contains(":-1\r\n:0\r\n")
17840        );
17841        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
17842        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17843        assert!(
17844            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17845                .contains(":9223372036854775807\r\n")
17846        );
17847        f.run(&[
17848            b"XNACK",
17849            b"s",
17850            b"g",
17851            b"FATAL",
17852            b"IDS",
17853            b"1",
17854            b"1-1",
17855            b"RETRYCOUNT",
17856            b"3",
17857        ]);
17858        assert!(
17859            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17860                .contains(":-1\r\n:3\r\n")
17861        );
17862
17863        // Releasing something the group is not holding is zero, and `FORCE`
17864        // makes the pending entry rather than answering zero. A forced entry
17865        // starts at zero, since there was no earlier count to keep.
17866        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17867        assert_eq!(
17868            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17869            ":0\r\n"
17870        );
17871        assert_eq!(
17872            f.run(&[
17873                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17874            ]),
17875            ":1\r\n"
17876        );
17877        assert!(
17878            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17879                .contains(":-1\r\n:0\r\n")
17880        );
17881        // `FORCE` on an ID the stream does not have is still zero.
17882        assert_eq!(
17883            f.run(&[
17884                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17885            ]),
17886            ":0\r\n"
17887        );
17888
17889        // The group is looked up before the mode word, and it raises rather
17890        // than answering per ID the way the two delete commands do.
17891        assert_eq!(
17892            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17893            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17894        );
17895        assert!(
17896            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17897                .starts_with("-ERR")
17898        );
17899        // Its own sentences, which are not the ones XDELEX uses.
17900        assert!(
17901            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17902                .contains("numids must be a positive integer")
17903        );
17904        assert!(
17905            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17906                .contains("number of IDs doesn't match numids")
17907        );
17908        // Everything past the counted IDs is an option, so one too many is an
17909        // option nobody recognises and not a count that does not add up.
17910        assert!(
17911            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17912                .contains("Unrecognized XNACK option '2-1'")
17913        );
17914    }
17915
17916    /// `XINFO`, which is where the shape of the storage shows through.
17917    #[test]
17918    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
17919        let mut f = Fixture::new();
17920        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17921        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17922        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17923        f.run(&[
17924            b"XREADGROUP",
17925            b"GROUP",
17926            b"g",
17927            b"c1",
17928            b"COUNT",
17929            b"1",
17930            b"STREAMS",
17931            b"s",
17932            b">",
17933        ]);
17934
17935        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17936        // Ten pairs, since the six idempotency fields have nothing behind them
17937        // here and a zero would claim they had. That is D-27.
17938        assert!(info.starts_with("*20\r\n"), "{info}");
17939        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
17940        assert!(
17941            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
17942            "{info}"
17943        );
17944        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
17945        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
17946
17947        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
17948        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
17949        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
17950        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
17951        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
17952
17953        // A consumer that has never been given anything reports minus one for
17954        // inactive rather than the moment it turned up, which is what tells a
17955        // worker that is stuck from one that has nothing to do.
17956        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
17957        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
17958        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
17959        assert!(
17960            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
17961            "{consumers}"
17962        );
17963        // And in name order, which the storage does not hold them in.
17964        let c1 = consumers.find("c1").unwrap();
17965        let c2 = consumers.find("c2").unwrap();
17966        assert!(c1 < c2, "{consumers}");
17967
17968        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
17969        assert!(full.starts_with("*18\r\n"), "{full}");
17970        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
17971        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
17972
17973        assert!(
17974            f.run(&[b"XINFO", b"STREAM", b"missing"])
17975                .contains("no such key")
17976        );
17977        assert!(
17978            f.run(&[b"XINFO", b"GROUPS", b"missing"])
17979                .contains("no such key")
17980        );
17981        assert!(
17982            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
17983                .starts_with("-NOGROUP")
17984        );
17985        assert!(
17986            f.run(&[b"XINFO", b"NOSUCH", b"s"])
17987                .contains("Try XINFO HELP")
17988        );
17989        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
17990        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
17991    }
17992
17993    /// `XPENDING`'s long form, which reads its arguments by counting them.
17994    #[test]
17995    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
17996        let mut f = Fixture::new();
17997        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17998        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17999        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
18000
18001        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
18002        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");
18003        assert_eq!(
18004            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
18005            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
18006        );
18007        // A consumer nobody has heard of holds nothing rather than erroring.
18008        assert_eq!(
18009            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
18010            "*0\r\n"
18011        );
18012        assert_eq!(
18013            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
18014            list
18015        );
18016        // IDLE is only read at position three.
18017        assert!(
18018            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
18019                .contains("syntax error")
18020        );
18021        assert!(
18022            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
18023                .contains("syntax error")
18024        );
18025        assert_eq!(
18026            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
18027            "*0\r\n"
18028        );
18029        assert!(
18030            f.run(&[b"XPENDING", b"missing", b"g"])
18031                .starts_with("-NOGROUP")
18032        );
18033    }
18034
18035    /// `XSETID`, which is three counters and two refusals.
18036    #[test]
18037    fn xsetid_will_not_go_below_what_is_there() {
18038        let mut f = Fixture::new();
18039        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
18040        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
18041        assert_eq!(
18042            f.run(&[
18043                b"XSETID",
18044                b"s",
18045                b"10-1",
18046                b"ENTRIESADDED",
18047                b"7",
18048                b"MAXDELETEDID",
18049                b"9-1"
18050            ]),
18051            "+OK\r\n"
18052        );
18053        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18054        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
18055        assert!(
18056            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
18057            "{info}"
18058        );
18059
18060        assert!(
18061            f.run(&[b"XSETID", b"s", b"1-1"])
18062                .contains("smaller than the target stream top item")
18063        );
18064        assert!(
18065            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
18066                .contains("entries_added must be positive")
18067        );
18068        assert!(
18069            f.run(&[b"XSETID", b"missing", b"1-1"])
18070                .contains("no such key")
18071        );
18072    }
18073
18074    /// RESP3, where the two reads answer a map and the entries stay an array.
18075    #[test]
18076    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
18077        let mut f = Fixture::new();
18078        f.run(&[b"HELLO", b"3"]);
18079        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18080        // A map header and then the key and the entries side by side, with no
18081        // two element array wrapping the pair.
18082        assert_eq!(
18083            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
18084            "%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"
18085        );
18086        // The fields are still one flat array and not a map, which is Redis's
18087        // shape and is what every consumer written before RESP3 expects.
18088        assert_eq!(
18089            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
18090            "*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"
18091        );
18092        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
18093    }
18094
18095    /// A store to migrate values into, so a test can watch the inversion.
18096    ///
18097    /// A vector rather than a file for the same reason the tier's own tests use
18098    /// one: the file work has not attached a real store yet, and what this is
18099    /// checking is the policy above the store rather than the store.
18100    struct Mem {
18101        blobs: Vec<Vec<u8>>,
18102    }
18103
18104    impl yo_kv::cold::Blocks for Mem {
18105        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
18106            self.blobs.push(bytes.to_vec());
18107            Ok(yo_common::Addr::new(
18108                yo_common::Space::Log,
18109                (self.blobs.len() - 1) as u64,
18110            ))
18111        }
18112
18113        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
18114            self.blobs
18115                .get(at.offset() as usize)
18116                .map(Vec::as_slice)
18117                .ok_or_else(|| {
18118                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
18119                })
18120        }
18121
18122        fn bytes(&self) -> u64 {
18123            self.blobs.iter().map(|b| b.len() as u64).sum()
18124        }
18125    }
18126
18127    /// A server holding several segments of strings, with somewhere to put them.
18128    ///
18129    /// Answers the fixture and what it was holding when it stopped filling.
18130    fn filled(attach: bool) -> (Fixture, usize) {
18131        let mut f = Fixture::new();
18132        if attach {
18133            f.server
18134                .striped(0)
18135                .hold_stripe(0)
18136                .attach(Box::new(Mem { blobs: Vec::new() }));
18137        }
18138        let val = vec![b'v'; 256];
18139        for i in 0..24000u32 {
18140            let k = format!("key:{i:08}");
18141            f.run(&[b"SET", k.as_bytes(), &val]);
18142        }
18143        let full = f.server.memory_bytes();
18144        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
18145        (f, full)
18146    }
18147
18148    /// Write until the server is under `limit` or the writes run out.
18149    ///
18150    /// The same shape the eviction test uses. A memory limit is enforced in
18151    /// front of a command, so nothing happens until something is written, and
18152    /// the budget means one command does not do the whole job.
18153    fn press(f: &mut Fixture, limit: usize) {
18154        let val = vec![b'v'; 256];
18155        for i in 0..3000u32 {
18156            let k = format!("new:{i:08}");
18157            assert_eq!(
18158                f.run(&[b"SET", k.as_bytes(), &val]),
18159                "+OK\r\n",
18160                "write {i} was refused"
18161            );
18162            f.server.refresh_memory();
18163            if f.server.memory_bytes() <= limit {
18164                return;
18165            }
18166        }
18167        panic!(
18168            "it never got under: {} against {limit}",
18169            f.server.memory_bytes()
18170        );
18171    }
18172
18173    #[test]
18174    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
18175        let mut f = Fixture::new();
18176        assert_eq!(
18177            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18178            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
18179            "no limit is the default"
18180        );
18181        // The same memory value parser `maxmemory` uses, and the same trap in
18182        // it, plus the one spelling that means no limit at all.
18183        for (typed, bytes) in [
18184            (&b"0"[..], "0"),
18185            (b"1024", "1024"),
18186            (b"1k", "1000"),
18187            (b"1gb", "1073741824"),
18188            (b"-1", "-1"),
18189        ] {
18190            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
18191            assert_eq!(
18192                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18193                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
18194                "set {}",
18195                String::from_utf8_lossy(typed)
18196            );
18197        }
18198        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
18199            assert_eq!(
18200                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
18201                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
18202                "refused {}",
18203                String::from_utf8_lossy(bad)
18204            );
18205        }
18206        // Nothing is attached, so the answer to a memory limit is still Redis's.
18207        let info = f.run(&[b"INFO", b"memory"]);
18208        assert!(info.contains("maxstore:-1"), "{info}");
18209        assert!(info.contains("yo_memory_regime:evict"), "{info}");
18210        assert!(info.contains("yo_store_bytes:0"), "{info}");
18211    }
18212
18213    #[test]
18214    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
18215        // The inversion. The same pressure that makes a Redis server throw keys
18216        // away makes this one move values to the file, and afterwards every key
18217        // is still there and still answers with what was stored in it.
18218        let (mut f, full) = filled(true);
18219        let keys = f.run(&[b"DBSIZE"]);
18220        assert!(
18221            f.run(&[b"INFO", b"memory"])
18222                .contains("yo_memory_regime:migrate"),
18223            "a database with somewhere to put values migrates"
18224        );
18225
18226        let limit = full - 2 * 1024 * 1024;
18227        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18228        f.run(&[
18229            b"CONFIG",
18230            b"SET",
18231            b"maxmemory",
18232            limit.to_string().as_bytes(),
18233        ]);
18234        press(&mut f, limit);
18235
18236        assert!(
18237            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18238            "nothing was thrown away"
18239        );
18240        let after: usize = f.run(&[b"DBSIZE"])[1..]
18241            .trim_end()
18242            .parse()
18243            .expect("a count");
18244        let before: usize = keys[1..].trim_end().parse().expect("a count");
18245        assert!(after > before, "the keys that came in are all still here");
18246        assert!(
18247            f.server.store_bytes() > 0,
18248            "and what came out of memory went to the file"
18249        );
18250        // And the values read back, which is the part that makes it a migration
18251        // rather than a loss.
18252        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
18253        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
18254        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
18255    }
18256
18257    #[test]
18258    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
18259        // The documented setting for a drop in cache. A file that may hold
18260        // nothing cannot be migrated to, so eviction is all that is left, and
18261        // the server behaves exactly as it did before any of this existed.
18262        let (mut f, full) = filled(true);
18263        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
18264        assert!(
18265            f.run(&[b"INFO", b"memory"])
18266                .contains("yo_memory_regime:evict"),
18267            "nothing may go to the file"
18268        );
18269
18270        let limit = full - 2 * 1024 * 1024;
18271        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18272        f.run(&[
18273            b"CONFIG",
18274            b"SET",
18275            b"maxmemory",
18276            limit.to_string().as_bytes(),
18277        ]);
18278        press(&mut f, limit);
18279
18280        assert!(
18281            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18282            "keys were thrown away, which is what was asked for"
18283        );
18284        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
18285    }
18286
18287    #[test]
18288    fn a_full_file_goes_back_to_evicting() {
18289        // A storage limit reached is a storage limit, and eviction is the right
18290        // answer to one. The budget here is a few kilobytes, so the first round
18291        // of migration fills it and everything after that is evicted.
18292        let (mut f, full) = filled(true);
18293        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
18294        let limit = full - 2 * 1024 * 1024;
18295        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18296        f.run(&[
18297            b"CONFIG",
18298            b"SET",
18299            b"maxmemory",
18300            limit.to_string().as_bytes(),
18301        ]);
18302        press(&mut f, limit);
18303
18304        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
18305        assert!(
18306            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18307            "and then it started evicting"
18308        );
18309        assert!(
18310            f.run(&[b"INFO", b"memory"])
18311                .contains("yo_memory_regime:evict"),
18312            "and it says so"
18313        );
18314    }
18315    // ------------------------------------------------------------- stripes
18316
18317    /// Every string command, run twice: once on a database that is one keyspace
18318    /// and once on a database that is eight, with the same commands in the same
18319    /// order and the replies compared byte for byte.
18320    ///
18321    /// This is the whole claim the striping rests on. A key belongs to one
18322    /// stripe and to no other, so the answer to a command cannot depend on how
18323    /// many stripes there are, and the way to check that is to ask the same
18324    /// question of two servers that differ in nothing else.
18325    ///
18326    /// The keys are chosen to land on different stripes rather than to look
18327    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
18328    /// those three keys are not all on the same one, and at eight stripes three
18329    /// keys land together about one time in fifty.
18330    #[test]
18331    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
18332        let script: &[&[&[u8]]] = &[
18333            // The single key commands, which are the ones that get handed one
18334            // stripe at the dispatch site.
18335            &[b"SET", b"k1", b"v1"],
18336            &[b"SET", b"k2", b"v2"],
18337            &[b"GET", b"k1"],
18338            &[b"GET", b"nothing"],
18339            &[b"GETSET", b"k1", b"v1b"],
18340            &[b"SETNX", b"k1", b"no"],
18341            &[b"SETNX", b"k3", b"yes"],
18342            &[b"APPEND", b"k3", b"!"],
18343            &[b"STRLEN", b"k3"],
18344            &[b"SETRANGE", b"k3", b"1", b"XY"],
18345            &[b"GETRANGE", b"k3", b"0", b"-1"],
18346            &[b"INCR", b"n1"],
18347            &[b"INCRBY", b"n1", b"41"],
18348            &[b"DECRBY", b"n1", b"2"],
18349            &[b"INCRBYFLOAT", b"f1", b"1.5"],
18350            &[b"SETEX", b"e1", b"100", b"v"],
18351            &[b"PSETEX", b"e2", b"100000", b"v"],
18352            &[b"GETEX", b"e1", b"PERSIST"],
18353            &[b"GETDEL", b"k2"],
18354            &[b"GET", b"k2"],
18355            &[b"DIGEST", b"k1"],
18356            &[b"DELEX", b"k3"],
18357            // The five that name more than one key, which are the ones that
18358            // cannot be handed one stripe at all.
18359            &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
18360            &[b"MGET", b"a", b"b", b"c", b"missing"],
18361            &[b"MSETNX", b"d", b"4", b"e", b"5"],
18362            &[b"MSETNX", b"e", b"6", b"f", b"7"],
18363            &[b"MGET", b"d", b"e", b"f"],
18364            &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
18365            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
18366            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
18367            &[b"MGET", b"g", b"h"],
18368            &[b"SET", b"s1", b"ohmytext"],
18369            &[b"SET", b"s2", b"mynewtext"],
18370            &[b"LCS", b"s1", b"s2"],
18371            &[b"LCS", b"s1", b"s2", b"LEN"],
18372            &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
18373            &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
18374            &[b"LCS", b"s1", b"gone"],
18375            // And the errors, which have to be the same errors.
18376            &[b"MSET", b"odd"],
18377            &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
18378            &[b"MGET"],
18379        ];
18380
18381        let mut one = Fixture::new();
18382        let mut many = Fixture::striped(8);
18383        for parts in script {
18384            let a = one.run(parts);
18385            let b = many.run(parts);
18386            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18387        }
18388    }
18389
18390    /// The keys of an `MSET` really do end up on different stripes.
18391    ///
18392    /// Without this the test above could pass on a server whose stripe number
18393    /// happened to be a constant, which is a striped database in name only.
18394    #[test]
18395    fn a_striped_database_spreads_the_keys_it_is_given() {
18396        let mut f = Fixture::striped(8);
18397        for i in 0..256 {
18398            let key = format!("key:{i}");
18399            f.run(&[b"SET", key.as_bytes(), b"v"]);
18400        }
18401        assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18402    }
18403
18404    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
18405    /// that is not a string comes back nil and the rest of the reply is intact.
18406    #[test]
18407    fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18408        let mut one = Fixture::new();
18409        let mut many = Fixture::striped(8);
18410        for f in [&mut one, &mut many] {
18411            f.run(&[b"SET", b"str", b"v"]);
18412            // Planted rather than pushed. `RPUSH` belongs to the list group,
18413            // which has not been taught about stripes yet and would refuse the
18414            // wide server. What is under test is what `MGET` does when it walks
18415            // onto a key that is not a string, and that does not care how the
18416            // key got there.
18417            f.server
18418                .striped(0)
18419                .hold(b"list")
18420                .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18421                .expect("a new list");
18422        }
18423        assert_eq!(
18424            one.run(&[b"MGET", b"str", b"list", b"gone"]),
18425            many.run(&[b"MGET", b"str", b"list", b"gone"])
18426        );
18427    }
18428
18429    /// The same claim for the keyspace group, and the same way of checking it.
18430    ///
18431    /// `SORT` is not in the script because it is the one command in that file
18432    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
18433    /// `RANDOMKEY` are not in it either, because those three do not promise an
18434    /// order and comparing two replies byte for byte would be asserting one.
18435    /// They get tests of their own below.
18436    #[test]
18437    fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18438        let script: &[&[&[u8]]] = &[
18439            &[b"SET", b"k1", b"v1"],
18440            &[b"SET", b"k2", b"v2"],
18441            &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18442            &[b"TYPE", b"k1"],
18443            &[b"TYPE", b"gone"],
18444            &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18445            &[b"EXPIRE", b"k1", b"100"],
18446            &[b"TTL", b"k1"],
18447            &[b"EXPIRE", b"k1", b"200", b"NX"],
18448            &[b"PERSIST", b"k1"],
18449            &[b"TTL", b"k1"],
18450            &[b"PEXPIREAT", b"k2", b"1900000000000"],
18451            &[b"EXPIRETIME", b"k2"],
18452            &[b"PEXPIRETIME", b"k2"],
18453            &[b"PERSIST", b"k2"],
18454            &[b"OBJECT", b"ENCODING", b"k1"],
18455            &[b"OBJECT", b"REFCOUNT", b"k1"],
18456            &[b"OBJECT", b"IDLETIME", b"k1"],
18457            &[b"OBJECT", b"FREQ", b"k1"],
18458            &[b"OBJECT", b"ENCODING", b"gone"],
18459            &[b"OBJECT", b"HELP"],
18460            &[b"RENAME", b"k1", b"k9"],
18461            &[b"GET", b"k9"],
18462            &[b"RENAME", b"gone", b"x"],
18463            &[b"RENAMENX", b"k9", b"k2"],
18464            &[b"RENAMENX", b"k9", b"k8"],
18465            &[b"GET", b"k8"],
18466            &[b"COPY", b"k8", b"c1"],
18467            &[b"COPY", b"k8", b"c1"],
18468            &[b"COPY", b"k8", b"c1", b"REPLACE"],
18469            &[b"COPY", b"k8", b"k8"],
18470            &[b"COPY", b"gone", b"c2"],
18471            &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18472            &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18473            &[b"MOVE", b"c1", b"1"],
18474            &[b"MOVE", b"c1", b"1"],
18475            &[b"MOVE", b"k8", b"0"],
18476            &[b"DEL", b"k2", b"gone"],
18477            &[b"UNLINK", b"k8", b"k8"],
18478            &[b"DBSIZE"],
18479        ];
18480
18481        let mut one = Fixture::new();
18482        let mut many = Fixture::striped(8);
18483        for parts in script {
18484            let a = one.run(parts);
18485            let b = many.run(parts);
18486            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18487        }
18488
18489        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
18490        // payload is taken from the store rather than parsed back out of a
18491        // reply that is not text. Both servers dump the same key and the bytes
18492        // are the same bytes, which is the first half of what is being checked
18493        // here.
18494        for f in [&mut one, &mut many] {
18495            f.run(&[b"SET", b"d1", b"payload"]);
18496            let payload = f
18497                .server
18498                .striped(0)
18499                .hold(b"d1")
18500                .dump(b"d1")
18501                .expect("a key that is there");
18502            assert!(
18503                f.run(&[b"DUMP", b"d1"])
18504                    .starts_with(&format!("${}", payload.len())),
18505                "a payload of the length the store gave"
18506            );
18507            assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18508            assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18509            assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18510            assert_eq!(
18511                f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18512                "-BUSYKEY Target key name already exists.\r\n"
18513            );
18514            assert_eq!(
18515                f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18516                "-ERR DUMP payload version or checksum are wrong\r\n"
18517            );
18518        }
18519    }
18520
18521    /// A `SCAN` of a database of eight stripes comes back with all of it.
18522    ///
18523    /// The cursor is the thing under test. It has to carry the stripe as well
18524    /// as the place in it, so a client that stops at one stripe and comes back
18525    /// carries on in that stripe and not at the top of the database, and the
18526    /// walk has to end once rather than eight times.
18527    #[test]
18528    fn a_scan_of_a_striped_database_walks_all_of_it() {
18529        let mut f = Fixture::striped(8);
18530        for i in 0..500 {
18531            let key = format!("key:{i}");
18532            f.run(&[b"SET", key.as_bytes(), b"v"]);
18533        }
18534
18535        let mut seen = Vec::new();
18536        let mut cursor = "0".to_owned();
18537        let mut calls = 0;
18538        loop {
18539            let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18540            let (next, keys) = scan_reply(&reply);
18541            seen.extend(keys);
18542            cursor = next;
18543            calls += 1;
18544            assert!(calls < 5_000, "a scan that will not finish");
18545            if cursor == "0" {
18546                break;
18547            }
18548        }
18549        seen.sort();
18550        assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18551        assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18552
18553        // And the options still work when the walk is over several stripes,
18554        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
18555        // applied by each stripe on the way.
18556        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18557        let (_, keys) = scan_reply(&reply);
18558        assert_eq!(keys.len(), 10, "key:40 through key:49");
18559        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18560        let (_, keys) = scan_reply(&reply);
18561        assert!(keys.is_empty(), "nothing here is a list");
18562    }
18563
18564    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
18565    ///
18566    /// The draw picks the stripe first, so the thing that can go wrong is that
18567    /// it always picks the same one, and two hundred draws over eight stripes
18568    /// would make that obvious.
18569    #[test]
18570    fn a_random_key_can_come_from_any_stripe() {
18571        let mut f = Fixture::striped(8);
18572        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18573        for i in 0..200 {
18574            let key = format!("key:{i}");
18575            f.run(&[b"SET", key.as_bytes(), b"v"]);
18576        }
18577        let mut homes = std::collections::HashSet::new();
18578        for _ in 0..200 {
18579            let got = f.run(&[b"RANDOMKEY"]);
18580            let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18581            assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18582            homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18583        }
18584        assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18585    }
18586
18587    /// Two keys that are not on the same stripe, which is what `RENAME` and
18588    /// `COPY` have to cope with and what a test has to arrange rather than
18589    /// hope for.
18590    fn apart(f: &mut Fixture, src: &str) -> String {
18591        let home = f.server.striped(0).stripe_of(src.as_bytes());
18592        for i in 0..1_000 {
18593            let dst = format!("dst:{i}");
18594            if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18595                return dst;
18596            }
18597        }
18598        panic!("eight stripes and a thousand keys all landed in one place");
18599    }
18600
18601    /// A rename whose two keys are on two stripes moves the value, the deadline
18602    /// and, for a collection, the body itself.
18603    #[test]
18604    fn a_rename_across_stripes_takes_everything_with_it() {
18605        let mut f = Fixture::striped(8);
18606        let dst = apart(&mut f, "src");
18607        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18608
18609        f.run(&[b"SET", src, b"v"]);
18610        f.run(&[b"EXPIRE", src, b"100"]);
18611        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18612        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18613        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18614        assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18615
18616        // A list, because a string lives in its record and a collection lives
18617        // in a slab, and the second of those is the one that can be left
18618        // behind. Planted through the store, since the list group has not been
18619        // taught about stripes yet.
18620        f.server
18621            .striped(0)
18622            .hold(src)
18623            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18624            .expect("a new list");
18625        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18626        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18627        assert_eq!(
18628            f.server.striped(0).hold(dst).llen(dst).expect("a list"),
18629            2,
18630            "the members are on the stripe the key moved to"
18631        );
18632
18633        // And `RENAMENX` still refuses a destination that is taken, which is
18634        // the one answer the cross stripe path has to work out for itself.
18635        f.run(&[b"SET", src, b"v"]);
18636        assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18637        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18638        assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18639    }
18640
18641    /// And a copy across two stripes leaves both keys behind it.
18642    #[test]
18643    fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18644        let mut f = Fixture::striped(8);
18645        let dst = apart(&mut f, "src");
18646        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18647
18648        f.run(&[b"SET", src, b"v"]);
18649        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18650        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18651        assert_eq!(
18652            f.run(&[b"COPY", src, dst]),
18653            ":0\r\n",
18654            "the destination is taken"
18655        );
18656        f.run(&[b"SET", src, b"w"]);
18657        assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18658        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18659
18660        // A collection is cloned rather than moved, so both keys have a body of
18661        // their own afterwards and writing to one does not show up in the
18662        // other.
18663        f.run(&[b"DEL", src, dst]);
18664        f.server
18665            .striped(0)
18666            .hold(src)
18667            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18668            .expect("a new list");
18669        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18670        f.server
18671            .striped(0)
18672            .hold(src)
18673            .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18674            .expect("a list that is there");
18675        assert_eq!(f.server.striped(0).hold(src).llen(src).expect("a list"), 3);
18676        assert_eq!(f.server.striped(0).hold(dst).llen(dst).expect("a list"), 2);
18677    }
18678
18679    /// Every bitmap command, on one stripe and on eight, replies compared byte
18680    /// for byte.
18681    ///
18682    /// `BITOP` is the one that names more than one key and it is where the work
18683    /// went. The rest are single key commands that now find their own stripe,
18684    /// and they are here because the cheapest way to be sure the routing is
18685    /// right is to ask.
18686    #[test]
18687    fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18688        let script: &[&[&[u8]]] = &[
18689            &[b"SET", b"k1", b"foobar"],
18690            &[b"SETBIT", b"b1", b"7", b"1"],
18691            &[b"SETBIT", b"b1", b"7", b"0"],
18692            &[b"GETBIT", b"k1", b"6"],
18693            &[b"GETBIT", b"k1", b"100"],
18694            &[b"BITCOUNT", b"k1"],
18695            &[b"BITCOUNT", b"k1", b"0", b"0"],
18696            &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18697            &[b"BITPOS", b"k1", b"1"],
18698            &[b"BITPOS", b"k1", b"0", b"2"],
18699            &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18700            &[
18701                b"BITFIELD",
18702                b"bf",
18703                b"SET",
18704                b"u8",
18705                b"0",
18706                b"255",
18707                b"GET",
18708                b"u8",
18709                b"0",
18710            ],
18711            &[
18712                b"BITFIELD",
18713                b"bf",
18714                b"OVERFLOW",
18715                b"SAT",
18716                b"INCRBY",
18717                b"u8",
18718                b"0",
18719                b"10",
18720            ],
18721            &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18722            // The multi key one, over sources that are not on one stripe unless
18723            // eight stripes have folded into one.
18724            &[b"SET", b"s1", b"abc"],
18725            &[b"SET", b"s2", b"abd"],
18726            &[b"SET", b"s3", b"a"],
18727            &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18728            &[b"GET", b"d1"],
18729            &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18730            &[b"GET", b"d2"],
18731            &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18732            &[b"STRLEN", b"d3"],
18733            &[b"BITOP", b"NOT", b"d4", b"s1"],
18734            &[b"STRLEN", b"d4"],
18735            &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18736            &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18737            &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18738            &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18739            // A source that is not there reads as empty, and a result with
18740            // nothing in it deletes the destination rather than writing one.
18741            &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18742            &[b"EXISTS", b"d1"],
18743            &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18744            &[b"GET", b"d9"],
18745            // And the errors, which have to be the same errors. The key that
18746            // is not a string is planted below rather than pushed here, since
18747            // the list group has not been taught about stripes yet.
18748            &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18749            &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18750            &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18751            &[b"BITOP", b"DIFF", b"d1", b"s1"],
18752            &[b"BITOP", b"NOPE", b"d1", b"s1"],
18753            &[b"BITCOUNT", b"list"],
18754            &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18755        ];
18756
18757        let mut one = Fixture::new();
18758        let mut many = Fixture::striped(8);
18759        for f in [&mut one, &mut many] {
18760            plant_list(f, b"list");
18761        }
18762        for parts in script {
18763            let a = one.run(parts);
18764            let b = many.run(parts);
18765            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18766        }
18767    }
18768
18769    /// A list under `key`, put there through the store.
18770    ///
18771    /// What a test does when it wants a key of the wrong type on a striped
18772    /// server, because the command that would make one is in a group that has
18773    /// not been taught about stripes yet.
18774    fn plant_list(f: &mut Fixture, key: &[u8]) {
18775        f.server
18776            .striped(0)
18777            .hold(key)
18778            .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18779            .expect("a new list");
18780    }
18781
18782    /// A `BITOP` whose keys are on two stripes reads both of them.
18783    ///
18784    /// The test above spreads its keys by hashing and would still pass if one
18785    /// stripe were doing all the work, since the answers would be the same. This
18786    /// one puts the destination and the two sources where they are known not to
18787    /// share a stripe.
18788    #[test]
18789    fn a_bitop_across_stripes_reads_every_source() {
18790        let mut f = Fixture::striped(8);
18791        let other = apart(&mut f, "src");
18792        let (src, far) = (b"src".as_slice(), other.as_bytes());
18793        assert_ne!(
18794            f.server.striped(0).stripe_of(src),
18795            f.server.striped(0).stripe_of(far),
18796            "the two keys are the point of the test"
18797        );
18798
18799        f.run(&[b"SET", src, b"abc"]);
18800        f.run(&[b"SET", far, b"abd"]);
18801        assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18802        assert_eq!(
18803            f.run(&[b"GET", far]),
18804            "$3\r\nab`\r\n",
18805            "a destination that is also a source"
18806        );
18807        f.run(&[b"SET", far, b"abd"]);
18808        assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18809        assert_eq!(
18810            f.run(&[b"GET", src]),
18811            "$3\r\n\0\0\x07\r\n",
18812            "and the other way round"
18813        );
18814
18815        // A result of nothing deletes a destination on whatever stripe it is
18816        // on, and a source of the wrong type is refused before anything is
18817        // written.
18818        f.run(&[b"SET", src, b"abc"]);
18819        f.run(&[b"DEL", far]);
18820        assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18821        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18822        f.run(&[b"SET", src, b"abc"]);
18823        f.run(&[b"DEL", far]);
18824        plant_list(&mut f, far);
18825        assert_eq!(
18826            f.run(&[b"BITOP", b"OR", b"out", src, far]),
18827            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18828        );
18829        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18830    }
18831
18832    /// Every HyperLogLog command, on one stripe and on eight.
18833    #[test]
18834    fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18835        let script: &[&[&[u8]]] = &[
18836            &[b"PFADD", b"h1", b"a", b"b", b"c"],
18837            &[b"PFADD", b"h1", b"a"],
18838            &[b"PFADD", b"h2"],
18839            &[b"PFADD", b"h2", b"c", b"d", b"e"],
18840            &[b"PFCOUNT", b"h1"],
18841            &[b"PFCOUNT", b"h2"],
18842            &[b"PFCOUNT", b"missing"],
18843            // The two that name more than one key.
18844            &[b"PFCOUNT", b"h1", b"h2"],
18845            &[b"PFCOUNT", b"h1", b"missing"],
18846            &[b"PFMERGE", b"m", b"h1", b"h2"],
18847            &[b"PFCOUNT", b"m"],
18848            &[b"STRLEN", b"m"],
18849            &[b"PFMERGE", b"m"],
18850            &[b"PFCOUNT", b"m"],
18851            &[b"PFMERGE", b"m2", b"missing"],
18852            &[b"PFCOUNT", b"m2"],
18853            // The debugging ones, which are single key and change what they
18854            // look at.
18855            &[b"PFDEBUG", b"ENCODING", b"h1"],
18856            &[b"PFDEBUG", b"DECODE", b"h1"],
18857            &[b"PFDEBUG", b"TODENSE", b"h1"],
18858            &[b"PFDEBUG", b"ENCODING", b"h1"],
18859            &[b"PFDEBUG", b"TODENSE", b"h1"],
18860            &[b"PFCOUNT", b"h1", b"h2"],
18861            &[b"PFSELFTEST"],
18862            // And the errors.
18863            &[b"SET", b"plain", b"not a sketch at all"],
18864            &[b"PFADD", b"plain", b"a"],
18865            &[b"PFCOUNT", b"plain"],
18866            &[b"PFCOUNT", b"h1", b"plain"],
18867            &[b"PFMERGE", b"plain", b"h1"],
18868            &[b"PFMERGE", b"m", b"plain"],
18869            &[b"PFDEBUG", b"ENCODING", b"gone"],
18870            &[b"PFDEBUG", b"NOPE", b"h1"],
18871        ];
18872
18873        let mut one = Fixture::new();
18874        let mut many = Fixture::striped(8);
18875        for parts in script {
18876            let a = one.run(parts);
18877            let b = many.run(parts);
18878            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18879        }
18880    }
18881
18882    /// Every set command, on one stripe and on eight.
18883    ///
18884    /// The commands that answer members answer them in whatever order the set
18885    /// or the table they were built in holds them, so those replies are
18886    /// compared as sets. Everything else is compared byte for byte. Two servers
18887    /// agreeing on the order would be a fact about the tables and not about the
18888    /// answer, and asserting it would make this test fail for a reason nobody
18889    /// cares about.
18890    #[test]
18891    fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18892        const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18893        let script: &[&[&[u8]]] = &[
18894            &[b"SADD", b"s1", b"a", b"b", b"c"],
18895            &[b"SADD", b"s1", b"a"],
18896            &[b"SADD", b"s2", b"b", b"c", b"d"],
18897            &[b"SADD", b"ints", b"1", b"2", b"3"],
18898            &[b"SCARD", b"s1"],
18899            &[b"SISMEMBER", b"s1", b"a"],
18900            &[b"SISMEMBER", b"s1", b"z"],
18901            &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18902            &[b"SMEMBERS", b"s1"],
18903            &[b"SREM", b"s1", b"c"],
18904            &[b"SADD", b"s1", b"c"],
18905            &[b"SSCAN", b"s1", b"0"],
18906            &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18907            // The two draws, on a set of one member, which is the only shape
18908            // whose answer two servers have to agree on.
18909            &[b"SADD", b"one", b"m"],
18910            &[b"SRANDMEMBER", b"one"],
18911            &[b"SRANDMEMBER", b"one", b"-3"],
18912            &[b"SRANDMEMBER", b"gone"],
18913            &[b"SPOP", b"one"],
18914            &[b"SPOP", b"one"],
18915            &[b"SPOP", b"gone", b"2"],
18916            // The one that names two keys.
18917            &[b"SMOVE", b"s1", b"s2", b"a"],
18918            &[b"SMOVE", b"s1", b"s2", b"zzz"],
18919            &[b"SMOVE", b"gone", b"s2", b"a"],
18920            &[b"SMEMBERS", b"s1"],
18921            &[b"SMEMBERS", b"s2"],
18922            // The algebra.
18923            &[b"SINTER", b"s1", b"s2"],
18924            &[b"SUNION", b"s1", b"s2"],
18925            &[b"SDIFF", b"s2", b"s1"],
18926            &[b"SINTER", b"s1", b"gone"],
18927            &[b"SUNION", b"s1", b"gone"],
18928            &[b"SDIFF", b"gone", b"s1"],
18929            &[b"SINTER", b"ints", b"s1"],
18930            &[b"SINTERCARD", b"2", b"s1", b"s2"],
18931            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
18932            &[b"SUNIONCARD", b"2", b"s1", b"s2"],
18933            &[b"SDIFFCARD", b"2", b"s2", b"s1"],
18934            &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
18935            &[b"SMEMBERS", b"d1"],
18936            &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
18937            &[b"SCARD", b"d2"],
18938            &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
18939            &[b"SCARD", b"d3"],
18940            // An empty result deletes the destination rather than storing a
18941            // set with nothing in it.
18942            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18943            &[b"EXISTS", b"d4"],
18944            // And a destination that is also a source.
18945            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18946            &[b"SCARD", b"s2"],
18947            // The errors, which have to be the same errors.
18948            &[b"SET", b"str", b"v"],
18949            &[b"SADD", b"str", b"a"],
18950            &[b"SINTER", b"s1", b"str"],
18951            &[b"SINTERSTORE", b"d5", b"s1", b"str"],
18952            &[b"EXISTS", b"d5"],
18953            &[b"SMOVE", b"str", b"s2", b"a"],
18954            &[b"SMOVE", b"s1", b"str", b"b"],
18955            &[b"SMOVE", b"gone", b"str", b"b"],
18956            &[b"SINTERCARD", b"0", b"s1"],
18957            &[b"SINTERCARD", b"3", b"s1", b"s2"],
18958            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
18959            &[b"SPOP", b"s1", b"-1"],
18960        ];
18961
18962        let mut one = Fixture::new();
18963        let mut many = Fixture::striped(8);
18964        for parts in script {
18965            let a = one.run(parts);
18966            let b = many.run(parts);
18967            let name = String::from_utf8_lossy(parts[0]).to_uppercase();
18968            if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
18969                assert_eq!(sorted(&a), sorted(&b), "{name}");
18970            } else {
18971                assert_eq!(a, b, "{name}");
18972            }
18973        }
18974    }
18975
18976    /// The algebra over sets that are known to be on different stripes.
18977    #[test]
18978    fn a_set_operation_across_stripes_reads_every_set() {
18979        let mut f = Fixture::striped(8);
18980        let second = apart(&mut f, "s1");
18981        let third = apart(&mut f, &second);
18982        let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
18983
18984        f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
18985        f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
18986        assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
18987        assert_eq!(
18988            sorted(&f.run(&[b"SUNION", s1, s2])),
18989            ["a", "b", "c", "d"],
18990            "a union of two stripes is both of them"
18991        );
18992        assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
18993        assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
18994        assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
18995        assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
18996
18997        // A destination on a third stripe, and then one that is also a source.
18998        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
18999        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
19000        assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
19001        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
19002        assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
19003        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
19004
19005        // An empty result deletes a destination wherever it is, and a key of
19006        // the wrong type stops the command before the destination is touched.
19007        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
19008        assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
19009        f.run(&[b"SET", s3, b"v"]);
19010        assert_eq!(
19011            f.run(&[b"SINTER", s1, s3]),
19012            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19013        );
19014        assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
19015    }
19016
19017    /// An `SMOVE` whose two keys are on two stripes.
19018    #[test]
19019    fn a_move_across_stripes_takes_the_member_with_it() {
19020        let mut f = Fixture::striped(8);
19021        let other = apart(&mut f, "src");
19022        let (src, dst) = (b"src".as_slice(), other.as_bytes());
19023
19024        f.run(&[b"SADD", src, b"a", b"b"]);
19025        f.run(&[b"SADD", dst, b"c"]);
19026        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
19027        assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
19028        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
19029        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
19030
19031        // A destination that is not there is created on its own stripe, and a
19032        // source that loses its last member is deleted from its own.
19033        f.run(&[b"DEL", dst]);
19034        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
19035        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
19036        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
19037
19038        // And a source that is not there answers zero without ever asking what
19039        // the destination holds, which is Redis's order and not the obvious
19040        // one.
19041        f.run(&[b"SET", dst, b"v"]);
19042        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
19043        f.run(&[b"SADD", src, b"b"]);
19044        assert_eq!(
19045            f.run(&[b"SMOVE", src, dst, b"b"]),
19046            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19047        );
19048    }
19049
19050    /// A count and a merge over sketches that are known to be on two stripes.
19051    #[test]
19052    fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
19053        let mut f = Fixture::striped(8);
19054        let other = apart(&mut f, "src");
19055        let (src, far) = (b"src".as_slice(), other.as_bytes());
19056
19057        for i in 0..150 {
19058            let ele = format!("e:{i}");
19059            f.run(&[b"PFADD", src, ele.as_bytes()]);
19060        }
19061        for i in 150..200 {
19062            let ele = format!("e:{i}");
19063            f.run(&[b"PFADD", far, ele.as_bytes()]);
19064        }
19065        // The three numbers a real server gives for these elements, which are
19066        // the numbers the single stripe tests in the keyspace crate check too.
19067        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
19068        assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
19069        assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
19070
19071        // A merge whose destination is on a third stripe, and then one that
19072        // writes into a source.
19073        let dest = apart(&mut f, &other);
19074        assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
19075        assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
19076        assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
19077        assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
19078        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
19079    }
19080
19081    /// Every sorted set command, on one stripe and on eight.
19082    ///
19083    /// Every reply here is compared byte for byte, unlike the set group, because
19084    /// a sorted set answers in rank order and members sharing a score come out
19085    /// in the order of their bytes. There is nothing left for the table the
19086    /// answer was built in to decide.
19087    #[test]
19088    fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
19089        let script: &[&[&[u8]]] = &[
19090            &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
19091            &[b"ZADD", b"z1", b"NX", b"9", b"a"],
19092            &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
19093            &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
19094            &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
19095            &[b"ZINCRBY", b"z1", b"1.5", b"b"],
19096            &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
19097            &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
19098            &[b"ZADD", b"one", b"1", b"m"],
19099            &[b"ZCARD", b"z1"],
19100            &[b"ZCARD", b"gone"],
19101            &[b"ZSCORE", b"z1", b"a"],
19102            &[b"ZSCORE", b"z1", b"zz"],
19103            &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
19104            &[b"ZRANK", b"z1", b"c"],
19105            &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
19106            &[b"ZREVRANK", b"z1", b"c"],
19107            &[b"ZRANK", b"z1", b"gone"],
19108            &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
19109            &[b"ZCOUNT", b"z1", b"(1", b"3"],
19110            &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
19111            // The range commands, which are one parse and one walk.
19112            &[b"ZRANGE", b"z1", b"0", b"-1"],
19113            &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
19114            &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
19115            &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
19116            &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
19117            &[b"ZREVRANGE", b"z1", b"0", b"-1"],
19118            &[
19119                b"ZRANGEBYSCORE",
19120                b"z1",
19121                b"-inf",
19122                b"+inf",
19123                b"LIMIT",
19124                b"1",
19125                b"1",
19126            ],
19127            &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
19128            &[b"ZSCAN", b"z1", b"0"],
19129            &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
19130            // The draw, on a sorted set of one member, which is the only shape
19131            // whose answer two servers have to agree on.
19132            &[b"ZRANDMEMBER", b"one"],
19133            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
19134            &[b"ZRANDMEMBER", b"gone"],
19135            // The one that copies a window into another key.
19136            &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
19137            &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
19138            &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
19139            &[b"EXISTS", b"d0"],
19140            // The algebra, in both its shapes.
19141            &[b"ZUNION", b"2", b"z1", b"z2"],
19142            &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
19143            &[
19144                b"ZUNION",
19145                b"2",
19146                b"z1",
19147                b"z2",
19148                b"WEIGHTS",
19149                b"2",
19150                b"3",
19151                b"AGGREGATE",
19152                b"MAX",
19153                b"WITHSCORES",
19154            ],
19155            &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
19156            &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
19157            &[b"ZDIFF", b"2", b"gone", b"z1"],
19158            &[b"ZINTERCARD", b"2", b"z1", b"z2"],
19159            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
19160            &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
19161            &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
19162            &[
19163                b"ZINTERSTORE",
19164                b"d2",
19165                b"2",
19166                b"z1",
19167                b"z2",
19168                b"AGGREGATE",
19169                b"MIN",
19170            ],
19171            &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
19172            &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
19173            &[b"ZCARD", b"d3"],
19174            // An empty result deletes the destination rather than storing a
19175            // sorted set with nothing in it.
19176            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
19177            &[b"EXISTS", b"d4"],
19178            // A plain set is a sorted set where every score is one, so it is a
19179            // legal input to all of these.
19180            &[b"SADD", b"plain", b"a", b"x"],
19181            &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
19182            &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
19183            // And a destination that is also a source.
19184            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
19185            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
19186            // The three removals and the two pops.
19187            &[b"ZREM", b"d5", b"x", b"nothere"],
19188            &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
19189            &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
19190            &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
19191            &[b"ZPOPMIN", b"z1"],
19192            &[b"ZPOPMAX", b"z1", b"2"],
19193            &[b"ZPOPMIN", b"gone"],
19194            &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
19195            &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
19196            // The errors, which have to be the same errors.
19197            &[b"SET", b"str", b"v"],
19198            &[b"ZADD", b"str", b"1", b"a"],
19199            &[b"ZSCORE", b"str", b"a"],
19200            &[b"ZADD", b"z1", b"nan", b"a"],
19201            &[b"ZUNION", b"2", b"z1", b"str"],
19202            &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
19203            &[b"EXISTS", b"d6"],
19204            &[b"ZINTERCARD", b"0", b"z1"],
19205            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
19206            &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
19207            &[b"ZMPOP", b"1", b"str", b"MIN"],
19208            &[b"ZPOPMIN", b"z1", b"-1"],
19209        ];
19210
19211        let mut one = Fixture::new();
19212        let mut many = Fixture::striped(8);
19213        for parts in script {
19214            let a = one.run(parts);
19215            let b = many.run(parts);
19216            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19217        }
19218    }
19219
19220    /// The algebra over sorted sets that are known to be on different stripes.
19221    #[test]
19222    fn a_sorted_set_operation_across_stripes_reads_every_input() {
19223        let mut f = Fixture::striped(8);
19224        let second = apart(&mut f, "z1");
19225        let third = apart(&mut f, &second);
19226        let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
19227
19228        f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
19229        f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
19230        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
19231        // come out in and the answer that says both stripes were read.
19232        assert_eq!(
19233            f.run(&[b"ZUNION", b"2", z1, z2]),
19234            "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
19235        );
19236        assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
19237        assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
19238        assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
19239        assert_eq!(
19240            f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
19241            ":1\r\n"
19242        );
19243
19244        // A destination on a third stripe, and the weights and the aggregate
19245        // reaching every input.
19246        assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
19247        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
19248        assert_eq!(
19249            f.run(&[
19250                b"ZUNIONSTORE",
19251                z3,
19252                b"2",
19253                z1,
19254                z2,
19255                b"WEIGHTS",
19256                b"2",
19257                b"3",
19258                b"AGGREGATE",
19259                b"MAX"
19260            ]),
19261            ":3\r\n"
19262        );
19263        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
19264        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
19265        assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
19266        assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
19267        assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
19268
19269        // A pop over keys on several stripes takes from the first one that has
19270        // anything, which is what makes the order of the keys matter.
19271        let popped = format!(
19272            "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
19273            second.len()
19274        );
19275        assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
19276        f.run(&[b"ZADD", z2, b"3", b"b"]);
19277
19278        // An empty result deletes a destination wherever it is, and an input of
19279        // the wrong type stops the command before the destination is touched.
19280        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
19281        assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
19282        f.run(&[b"SET", z3, b"v"]);
19283        assert_eq!(
19284            f.run(&[b"ZUNION", b"2", z1, z3]),
19285            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19286        );
19287        assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
19288
19289        // And a destination that is also a source works across stripes for the
19290        // reason it works on one: the whole result is built before anything is
19291        // written.
19292        assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
19293        assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
19294        assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
19295    }
19296
19297    /// A `ZRANGESTORE` whose two keys are on two stripes.
19298    #[test]
19299    fn a_range_store_across_stripes_copies_the_window() {
19300        let mut f = Fixture::striped(8);
19301        let other = apart(&mut f, "src");
19302        let third = apart(&mut f, &other);
19303        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19304
19305        f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
19306        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
19307        assert_eq!(
19308            f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
19309            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
19310        );
19311        assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
19312
19313        // A window walked backwards takes the other end of the sorted set and
19314        // still stores what it took in score order.
19315        assert_eq!(
19316            f.run(&[
19317                b"ZRANGESTORE",
19318                dst,
19319                src,
19320                b"+inf",
19321                b"-inf",
19322                b"BYSCORE",
19323                b"REV",
19324                b"LIMIT",
19325                b"0",
19326                b"2"
19327            ]),
19328            ":2\r\n"
19329        );
19330        assert_eq!(
19331            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19332            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19333        );
19334
19335        // An empty window deletes the destination on its own stripe, and a
19336        // source of the wrong type is refused before the destination is touched.
19337        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
19338        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19339        f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
19340        f.run(&[b"SET", plain, b"v"]);
19341        assert_eq!(
19342            f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
19343            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19344        );
19345        assert_eq!(
19346            f.run(&[b"ZCARD", dst]),
19347            ":3\r\n",
19348            "and left the destination"
19349        );
19350    }
19351
19352    /// Every list command, on one stripe and on eight.
19353    ///
19354    /// The blocking six are in here too, both when they can be answered on the
19355    /// spot and when they cannot, since a command that parks its client writes
19356    /// nothing at all and two servers have to agree about that as much as they
19357    /// agree about a reply.
19358    #[test]
19359    fn the_list_group_answers_the_same_however_many_stripes_there_are() {
19360        let script: &[&[&[u8]]] = &[
19361            &[b"RPUSH", b"l1", b"a", b"b", b"c"],
19362            &[b"LPUSH", b"l1", b"z"],
19363            &[b"RPUSHX", b"l1", b"d"],
19364            &[b"LPUSHX", b"gone", b"x"],
19365            &[b"RPUSHX", b"gone", b"x"],
19366            &[b"LLEN", b"l1"],
19367            &[b"LLEN", b"gone"],
19368            &[b"LRANGE", b"l1", b"0", b"-1"],
19369            &[b"LRANGE", b"l1", b"1", b"2"],
19370            &[b"LRANGE", b"l1", b"5", b"9"],
19371            &[b"LINDEX", b"l1", b"0"],
19372            &[b"LINDEX", b"l1", b"-1"],
19373            &[b"LINDEX", b"l1", b"99"],
19374            &[b"LSET", b"l1", b"0", b"y"],
19375            &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
19376            &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
19377            &[b"LPOS", b"l1", b"b"],
19378            &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
19379            &[b"LPOS", b"l1", b"nothere"],
19380            &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
19381            &[b"LREM", b"l1", b"1", b"aa"],
19382            &[b"LTRIM", b"l1", b"0", b"3"],
19383            &[b"LRANGE", b"l1", b"0", b"-1"],
19384            &[b"LPOP", b"l1"],
19385            &[b"RPOP", b"l1"],
19386            &[b"LPOP", b"l1", b"2"],
19387            &[b"LPOP", b"gone"],
19388            &[b"LPOP", b"gone", b"2"],
19389            &[b"EXISTS", b"l1"],
19390            // The ones that name two keys, and the one that takes a block of
19391            // elements rather than the one on the end.
19392            &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
19393            &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
19394            &[b"RPOPLPUSH", b"src", b"dst"],
19395            &[b"LRANGE", b"dst", b"0", b"-1"],
19396            &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
19397            &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
19398            &[
19399                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19400            ],
19401            &[
19402                b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19403            ],
19404            &[b"LRANGE", b"dst", b"0", b"-1"],
19405            &[
19406                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19407            ],
19408            &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19409            &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19410            &[b"LMPOP", b"1", b"gone", b"LEFT"],
19411            // The blocking ones, first with something there to answer them and
19412            // then with nothing, which parks the client and writes nothing.
19413            &[b"RPUSH", b"q", b"a", b"b", b"c"],
19414            &[b"BLPOP", b"gone", b"q", b"0"],
19415            &[b"BRPOP", b"q", b"0"],
19416            &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19417            &[b"RPUSH", b"q", b"x", b"y", b"z"],
19418            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19419            &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19420            &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19421            &[b"BLPOP", b"q", b"0"],
19422            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19423            // The errors, which have to be the same errors.
19424            &[b"SET", b"plain", b"v"],
19425            &[b"LPUSH", b"plain", b"a"],
19426            &[b"LLEN", b"plain"],
19427            &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19428            &[b"LRANGE", b"dst", b"0", b"-1"],
19429            &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19430            &[b"LSET", b"gone", b"0", b"v"],
19431            &[b"LSET", b"dst", b"99", b"v"],
19432            &[b"LPOP", b"dst", b"-1"],
19433            &[b"LMPOP", b"0", b"dst", b"LEFT"],
19434            &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19435        ];
19436
19437        let mut one = Fixture::new();
19438        let mut many = Fixture::striped(8);
19439        for parts in script {
19440            let a = one.run(parts);
19441            let b = many.run(parts);
19442            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19443        }
19444    }
19445
19446    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
19447    #[test]
19448    fn a_list_move_across_stripes_takes_the_elements_with_it() {
19449        let mut f = Fixture::striped(8);
19450        let other = apart(&mut f, "src");
19451        let third = apart(&mut f, &other);
19452        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19453
19454        f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19455        assert_eq!(
19456            f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19457            "$1\r\na\r\n"
19458        );
19459        assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19460        assert_eq!(
19461            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19462            "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19463            "one went on each end of the destination"
19464        );
19465        assert_eq!(
19466            f.run(&[b"LRANGE", src, b"0", b"-1"]),
19467            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19468        );
19469
19470        // A block of them, which under BULK arrives in the order it left.
19471        assert_eq!(
19472            f.run(&[
19473                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19474            ]),
19475            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19476        );
19477        assert_eq!(
19478            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19479            "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19480        );
19481        assert_eq!(
19482            f.run(&[b"EXISTS", src]),
19483            ":0\r\n",
19484            "and the source is gone with its last element"
19485        );
19486
19487        // An `EXACTLY` the source cannot fill moves nothing, and a source that
19488        // is not there at all is the two kinds of nothing the two commands have.
19489        f.run(&[b"RPUSH", src, b"e", b"f"]);
19490        assert_eq!(
19491            f.run(&[
19492                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19493            ]),
19494            "*-1\r\n"
19495        );
19496        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19497        assert_eq!(
19498            f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19499            "$-1\r\n"
19500        );
19501        assert_eq!(
19502            f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19503            "*-1\r\n"
19504        );
19505
19506        // A destination of the wrong type is refused before anything is taken,
19507        // which is the order that matters most here, since an element already
19508        // out of the source would have nowhere to go back to.
19509        f.run(&[b"SET", plain, b"v"]);
19510        assert_eq!(
19511            f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19512            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19513        );
19514        assert_eq!(
19515            f.run(&[b"LLEN", src]),
19516            ":2\r\n",
19517            "and left the source alone"
19518        );
19519        assert_eq!(
19520            f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19521            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19522        );
19523        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19524    }
19525
19526    /// A parked client served by a push that landed on another stripe.
19527    ///
19528    /// A waiter remembers the database and not the stripe, which is the point:
19529    /// serving it runs the same attempt the command ran, and the attempt finds
19530    /// the stripe each of its keys is on for itself.
19531    #[test]
19532    fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19533        let mut f = Fixture::striped(8);
19534        let other = apart(&mut f, "q");
19535        let (q, far) = (b"q".as_slice(), other.as_bytes());
19536
19537        assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19538        assert_eq!(f.server.parked(), 1);
19539        f.run(&[b"RPUSH", far, b"v"]);
19540        let mut out = Out::new(Proto::Resp2);
19541        assert!(f.server.serve_waiter(0, 0, &mut out));
19542        let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19543        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19544        assert_eq!(
19545            f.run(&[b"EXISTS", far]),
19546            ":0\r\n",
19547            "and it took the element with it"
19548        );
19549
19550        // And a move across two stripes is served the same way, by the push
19551        // that fills its source.
19552        f.server.forget_waiters(7);
19553        assert_eq!(
19554            f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19555            Flow::Block
19556        );
19557        f.run(&[b"RPUSH", q, b"w"]);
19558        let mut out = Out::new(Proto::Resp2);
19559        assert!(f.server.serve_waiter(0, 0, &mut out));
19560        assert_eq!(
19561            core::str::from_utf8(out.as_slice()).expect("ascii"),
19562            "$1\r\nw\r\n"
19563        );
19564        assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19565    }
19566
19567    /// Every stream command, on one stripe and on eight.
19568    ///
19569    /// Every ID is written out rather than left to the clock, so the two servers
19570    /// are being compared on what they store and not on how long the test took
19571    /// to get from one of them to the other.
19572    #[test]
19573    fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19574        let script: &[&[&[u8]]] = &[
19575            &[b"XADD", b"s", b"1-1", b"a", b"1"],
19576            &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19577            &[b"XADD", b"s", b"3-1", b"d", b"4"],
19578            &[b"XADD", b"s", b"1-1", b"e", b"5"],
19579            &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19580            &[b"XLEN", b"s"],
19581            &[b"XLEN", b"gone"],
19582            &[b"XRANGE", b"s", b"-", b"+"],
19583            &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19584            &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19585            &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19586            &[b"XREVRANGE", b"s", b"+", b"-"],
19587            &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19588            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19589            &[b"XREAD", b"STREAMS", b"s", b"$"],
19590            // The groups, which is where most of the state is.
19591            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19592            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19593            &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19594            &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19595            &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19596            &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19597            &[
19598                b"XREADGROUP",
19599                b"GROUP",
19600                b"g",
19601                b"c1",
19602                b"COUNT",
19603                b"1",
19604                b"STREAMS",
19605                b"s",
19606                b"0",
19607            ],
19608            &[
19609                b"XREADGROUP",
19610                b"GROUP",
19611                b"nope",
19612                b"c1",
19613                b"STREAMS",
19614                b"s",
19615                b">",
19616            ],
19617            &[b"XPENDING", b"s", b"g"],
19618            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19619            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19620            &[b"XPENDING", b"s", b"nope"],
19621            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19622            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19623            &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19624            &[b"XACK", b"s", b"g", b"1-1"],
19625            &[b"XACK", b"s", b"g", b"1-1"],
19626            &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19627            &[b"XPENDING", b"s", b"g"],
19628            &[b"XINFO", b"STREAM", b"s"],
19629            &[b"XINFO", b"GROUPS", b"s"],
19630            &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19631            &[b"XINFO", b"STREAM", b"gone"],
19632            // Deleting, trimming and moving the ID on.
19633            &[b"XDEL", b"s", b"3-1"],
19634            &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19635            &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19636            &[b"XADD", b"s", b"9-1", b"z", b"9"],
19637            &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19638            &[b"XTRIM", b"s", b"MINID", b"9"],
19639            &[b"XSETID", b"s", b"99-1"],
19640            &[b"XSETID", b"s", b"1-1"],
19641            &[b"XLEN", b"s"],
19642            &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19643            &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19644            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19645            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19646            // And the errors.
19647            &[b"SET", b"plain", b"v"],
19648            &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19649            &[b"XLEN", b"plain"],
19650            &[b"XREAD", b"STREAMS", b"plain", b"0"],
19651            &[b"XRANGE", b"s", b"bogus", b"+"],
19652            &[b"XADD", b"s", b"1-1", b"a"],
19653            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19654            &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19655        ];
19656
19657        let mut one = Fixture::new();
19658        let mut many = Fixture::striped(8);
19659        for parts in script {
19660            let a = one.run(parts);
19661            let b = many.run(parts);
19662            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19663        }
19664    }
19665
19666    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
19667    ///
19668    /// Nothing is shared between the two streams, so the only thing this can go
19669    /// wrong at is looking both of them up, which is exactly what a read that
19670    /// held one database and walked it would get wrong.
19671    #[test]
19672    fn a_stream_read_across_stripes_reads_every_key() {
19673        let mut f = Fixture::striped(8);
19674        let other = apart(&mut f, "s1");
19675        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19676
19677        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19678        f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19679        let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19680        assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19681        assert!(got.contains("1-1"), "the first one is in there: {got}");
19682        assert!(got.contains("2-1"), "and so is the second: {got}");
19683
19684        // A group read looks its group up on every key before it reads any of
19685        // them, so a group that is missing on the far key stops the near one.
19686        f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19687        let got = f.run(&[
19688            b"XREADGROUP",
19689            b"GROUP",
19690            b"g",
19691            b"c",
19692            b"STREAMS",
19693            s1,
19694            s2,
19695            b">",
19696            b">",
19697        ]);
19698        assert!(got.starts_with("-NOGROUP"), "{got}");
19699        assert_eq!(
19700            f.run(&[b"XPENDING", s1, b"g"]),
19701            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19702            "and read nothing from the key that did have the group"
19703        );
19704
19705        f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19706        let got = f.run(&[
19707            b"XREADGROUP",
19708            b"GROUP",
19709            b"g",
19710            b"c",
19711            b"STREAMS",
19712            s1,
19713            s2,
19714            b">",
19715            b">",
19716        ]);
19717        assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19718    }
19719
19720    /// A client parked on an `XREAD` woken by an entry on another stripe.
19721    #[test]
19722    fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19723        let mut f = Fixture::striped(8);
19724        let other = apart(&mut f, "s1");
19725        let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19726        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19727        f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19728
19729        assert_eq!(
19730            f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19731                .0,
19732            Flow::Block
19733        );
19734        f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19735        let mut out = Out::new(Proto::Resp2);
19736        assert!(f.server.serve_waiter(0, 0, &mut out));
19737        let want = format!(
19738            "*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",
19739            other.len()
19740        );
19741        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19742    }
19743
19744    /// Every JSON command, on one stripe and on eight.
19745    #[test]
19746    fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19747        let script: &[&[&[u8]]] = &[
19748            &[
19749                b"JSON.SET",
19750                b"d",
19751                b"$",
19752                br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19753            ],
19754            &[b"JSON.SET", b"d", b"$.a", b"2"],
19755            &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19756            &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19757            &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19758            &[b"JSON.GET", b"d"],
19759            &[b"JSON.GET", b"d", b"$.b"],
19760            &[b"JSON.GET", b"gone", b"$"],
19761            &[b"JSON.TYPE", b"d", b"$.b"],
19762            &[b"JSON.TYPE", b"d", b"$.s"],
19763            &[b"JSON.TOGGLE", b"d", b"$.t"],
19764            &[b"JSON.ARRLEN", b"d", b"$.b"],
19765            &[b"JSON.OBJLEN", b"d", b"$"],
19766            &[b"JSON.OBJKEYS", b"d", b"$"],
19767            &[b"JSON.STRLEN", b"d", b"$.s"],
19768            &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19769            &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19770            &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19771            &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19772            &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19773            &[b"JSON.ARRPOP", b"d", b"$.b"],
19774            &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19775            &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19776            &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19777            &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19778            &[b"JSON.RESP", b"d", b"$.b"],
19779            &[b"JSON.DEBUG", b"MEMORY", b"d"],
19780            &[b"JSON.CLEAR", b"d", b"$.b"],
19781            &[b"JSON.DEL", b"d", b"$.m"],
19782            &[b"JSON.FORGET", b"d", b"$.nothere"],
19783            // The two that name more than one key.
19784            &[
19785                b"JSON.MSET",
19786                b"m1",
19787                b"$",
19788                b"1",
19789                b"m2",
19790                b"$",
19791                b"2",
19792                b"m3",
19793                b"$",
19794                b"3",
19795            ],
19796            &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19797            &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19798            &[b"JSON.GET", b"m1", b"$"],
19799            &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19800            &[b"JSON.GET", b"m2", b"$"],
19801            // And the errors.
19802            &[b"SET", b"plain", b"v"],
19803            &[b"JSON.GET", b"plain", b"$"],
19804            &[b"JSON.SET", b"plain", b"$", b"1"],
19805            &[b"JSON.MGET", b"m1", b"plain", b"$"],
19806            &[b"JSON.SET", b"d", b"$.b", b"["],
19807            &[b"JSON.DEL", b"plain"],
19808        ];
19809
19810        let mut one = Fixture::new();
19811        let mut many = Fixture::striped(8);
19812        for parts in script {
19813            let a = one.run(parts);
19814            let b = many.run(parts);
19815            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19816        }
19817    }
19818
19819    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
19820    ///
19821    /// `JSON.MSET` works every triple out against the keyspace as it was before
19822    /// the command and writes nothing until all of them are known to work, so
19823    /// the thing to check is that a triple that cannot be written stops the
19824    /// ones on other stripes as well as the ones on its own.
19825    #[test]
19826    fn a_json_multi_write_across_stripes_reaches_every_key() {
19827        let mut f = Fixture::striped(8);
19828        let second = apart(&mut f, "m1");
19829        let third = apart(&mut f, &second);
19830        let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19831
19832        assert_eq!(
19833            f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19834            "+OK\r\n"
19835        );
19836        assert_eq!(
19837            f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19838            "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19839        );
19840
19841        // A value that is not JSON is refused before anything is written, and
19842        // the key on the far stripe keeps what it had.
19843        assert_eq!(
19844            f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19845            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19846        );
19847        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19848
19849        // A path that names nowhere is not an error. That triple is skipped,
19850        // the ones on the other stripes are still written, and the reply is a
19851        // nil rather than OK.
19852        assert_eq!(
19853            f.run(&[
19854                b"JSON.MSET",
19855                m1,
19856                b"$",
19857                b"9",
19858                m2,
19859                b"$.deep",
19860                b"9",
19861                m3,
19862                b"$",
19863                b"7"
19864            ]),
19865            "$-1\r\n"
19866        );
19867        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19868        assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19869        assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19870    }
19871
19872    /// Every geospatial command, on one stripe and on eight.
19873    #[test]
19874    fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19875        let script: &[&[&[u8]]] = &[
19876            &[
19877                b"GEOADD",
19878                b"g",
19879                b"13.361389",
19880                b"38.115556",
19881                b"palermo",
19882                b"15.087269",
19883                b"37.502669",
19884                b"catania",
19885            ],
19886            &[
19887                b"GEOADD",
19888                b"g",
19889                b"NX",
19890                b"13.361389",
19891                b"38.115556",
19892                b"palermo",
19893            ],
19894            &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19895            &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19896            &[b"GEOHASH", b"g", b"palermo", b"catania"],
19897            &[b"GEODIST", b"g", b"palermo", b"catania"],
19898            &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19899            &[b"GEODIST", b"g", b"palermo", b"nothere"],
19900            &[
19901                b"GEOSEARCH",
19902                b"g",
19903                b"FROMLONLAT",
19904                b"15",
19905                b"37",
19906                b"BYRADIUS",
19907                b"200",
19908                b"KM",
19909                b"ASC",
19910                b"WITHCOORD",
19911                b"WITHDIST",
19912                b"WITHHASH",
19913            ],
19914            &[
19915                b"GEOSEARCH",
19916                b"g",
19917                b"FROMMEMBER",
19918                b"palermo",
19919                b"BYBOX",
19920                b"400",
19921                b"400",
19922                b"KM",
19923                b"DESC",
19924            ],
19925            &[
19926                b"GEORADIUS",
19927                b"g",
19928                b"15",
19929                b"37",
19930                b"200",
19931                b"KM",
19932                b"COUNT",
19933                b"1",
19934            ],
19935            &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
19936            &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
19937            &[
19938                b"GEOSEARCHSTORE",
19939                b"dst",
19940                b"g",
19941                b"FROMLONLAT",
19942                b"15",
19943                b"37",
19944                b"BYRADIUS",
19945                b"200",
19946                b"KM",
19947            ],
19948            &[b"ZRANGE", b"dst", b"0", b"-1"],
19949            &[
19950                b"GEOSEARCHSTORE",
19951                b"dst",
19952                b"g",
19953                b"FROMLONLAT",
19954                b"15",
19955                b"37",
19956                b"BYRADIUS",
19957                b"1",
19958                b"M",
19959                b"STOREDIST",
19960            ],
19961            &[b"EXISTS", b"dst"],
19962            &[
19963                b"GEORADIUS",
19964                b"g",
19965                b"15",
19966                b"37",
19967                b"200",
19968                b"KM",
19969                b"STORE",
19970                b"dst",
19971            ],
19972            &[b"ZCARD", b"dst"],
19973            // And the errors.
19974            &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
19975            &[b"SET", b"plain", b"v"],
19976            &[b"GEOPOS", b"plain", b"a"],
19977            &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
19978            &[
19979                b"GEOSEARCHSTORE",
19980                b"dst",
19981                b"g",
19982                b"FROMLONLAT",
19983                b"15",
19984                b"37",
19985                b"BYRADIUS",
19986                b"200",
19987                b"KM",
19988                b"WITHCOORD",
19989            ],
19990        ];
19991
19992        let mut one = Fixture::new();
19993        let mut many = Fixture::striped(8);
19994        for parts in script {
19995            let a = one.run(parts);
19996            let b = many.run(parts);
19997            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19998        }
19999    }
20000
20001    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
20002    #[test]
20003    fn a_geo_search_store_across_stripes_writes_what_it_found() {
20004        let mut f = Fixture::striped(8);
20005        let other = apart(&mut f, "g");
20006        let third = apart(&mut f, &other);
20007        let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
20008
20009        f.run(&[
20010            b"GEOADD",
20011            g,
20012            b"13.361389",
20013            b"38.115556",
20014            b"palermo",
20015            b"15.087269",
20016            b"37.502669",
20017            b"catania",
20018        ]);
20019        assert_eq!(
20020            f.run(&[
20021                b"GEOSEARCHSTORE",
20022                dst,
20023                g,
20024                b"FROMLONLAT",
20025                b"15",
20026                b"37",
20027                b"BYRADIUS",
20028                b"200",
20029                b"KM",
20030                b"ASC",
20031            ]),
20032            ":2\r\n"
20033        );
20034        assert_eq!(
20035            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
20036            "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
20037            "the geohash is the score, so the order is not the search order"
20038        );
20039        assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
20040
20041        // `STOREDIST` stores the distance in the unit the search was asked in,
20042        // which is the destination stripe's sorted set and not the source's.
20043        assert_eq!(
20044            f.run(&[
20045                b"GEOSEARCHSTORE",
20046                dst,
20047                g,
20048                b"FROMMEMBER",
20049                b"palermo",
20050                b"BYRADIUS",
20051                b"200",
20052                b"KM",
20053                b"STOREDIST",
20054            ]),
20055            ":2\r\n"
20056        );
20057        assert_eq!(
20058            f.run(&[b"ZSCORE", dst, b"palermo"]),
20059            "$1\r\n0\r\n",
20060            "the centre is nought away from itself"
20061        );
20062
20063        // A search that found nothing deletes the destination on its own
20064        // stripe, and a source of the wrong type is refused with the
20065        // destination left alone.
20066        assert_eq!(
20067            f.run(&[
20068                b"GEOSEARCHSTORE",
20069                dst,
20070                g,
20071                b"FROMLONLAT",
20072                b"0",
20073                b"0",
20074                b"BYRADIUS",
20075                b"1",
20076                b"M",
20077            ]),
20078            ":0\r\n"
20079        );
20080        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
20081        f.run(&[
20082            b"GEOSEARCHSTORE",
20083            dst,
20084            g,
20085            b"FROMLONLAT",
20086            b"15",
20087            b"37",
20088            b"BYRADIUS",
20089            b"200",
20090            b"KM",
20091        ]);
20092        f.run(&[b"SET", plain, b"v"]);
20093        assert_eq!(
20094            f.run(&[
20095                b"GEOSEARCHSTORE",
20096                dst,
20097                plain,
20098                b"FROMLONLAT",
20099                b"15",
20100                b"37",
20101                b"BYRADIUS",
20102                b"200",
20103                b"KM",
20104            ]),
20105            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
20106        );
20107        assert_eq!(
20108            f.run(&[b"ZCARD", dst]),
20109            ":2\r\n",
20110            "and left the destination"
20111        );
20112    }
20113
20114    /// Every time series command, on one stripe and on eight.
20115    ///
20116    /// Every timestamp is written out rather than left to the clock, so the two
20117    /// servers are compared on the samples they hold and not on how long the
20118    /// test took to get from one of them to the other.
20119    #[test]
20120    fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
20121        let script: &[&[&[u8]]] = &[
20122            &[
20123                b"TS.CREATE",
20124                b"ts:a",
20125                b"LABELS",
20126                b"sensor",
20127                b"a",
20128                b"room",
20129                b"1",
20130            ],
20131            &[b"TS.CREATE", b"ts:a"],
20132            &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
20133            &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
20134            &[
20135                b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
20136            ],
20137            &[
20138                b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
20139            ],
20140            &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
20141            &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
20142            &[b"TS.GET", b"ts:a"],
20143            &[b"TS.GET", b"gone"],
20144            &[b"TS.RANGE", b"ts:a", b"-", b"+"],
20145            &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
20146            &[
20147                b"TS.RANGE",
20148                b"ts:a",
20149                b"-",
20150                b"+",
20151                b"AGGREGATION",
20152                b"avg",
20153                b"2000",
20154            ],
20155            &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
20156            &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20157            &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20158            &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
20159            &[b"TS.READ", b"ts:a", b"0"],
20160            &[b"TS.READ", b"ts:a", b"+"],
20161            // The filters, which are the ones that have to walk every stripe.
20162            &[b"TS.QUERYINDEX", b"sensor=a"],
20163            &[b"TS.QUERYINDEX", b"room=1"],
20164            &[b"TS.QUERYINDEX", b"room=9"],
20165            &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
20166            &[
20167                b"TS.QUERYLABELS",
20168                b"VALUES",
20169                b"sensor",
20170                b"FILTER",
20171                b"room=1",
20172            ],
20173            &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
20174            &[
20175                b"TS.MGET",
20176                b"SELECTED_LABELS",
20177                b"sensor",
20178                b"FILTER",
20179                b"sensor=a",
20180            ],
20181            &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
20182            &[
20183                b"TS.MREVRANGE",
20184                b"-",
20185                b"+",
20186                b"WITHLABELS",
20187                b"FILTER",
20188                b"sensor=a",
20189            ],
20190            &[
20191                b"TS.MRANGE",
20192                b"-",
20193                b"+",
20194                b"FILTER",
20195                b"room=1",
20196                b"GROUPBY",
20197                b"room",
20198                b"REDUCE",
20199                b"max",
20200            ],
20201            &[b"TS.INFO", b"ts:a"],
20202            // And a rule, which is the one thing here that names two keys.
20203            &[
20204                b"TS.CREATERULE",
20205                b"ts:a",
20206                b"ts:down",
20207                b"AGGREGATION",
20208                b"avg",
20209                b"1000",
20210            ],
20211            &[b"TS.CREATE", b"ts:down"],
20212            &[
20213                b"TS.CREATERULE",
20214                b"ts:a",
20215                b"ts:down",
20216                b"AGGREGATION",
20217                b"avg",
20218                b"1000",
20219            ],
20220            &[b"TS.ADD", b"ts:a", b"5000", b"4"],
20221            &[b"TS.ADD", b"ts:a", b"6000", b"5"],
20222            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20223            &[b"TS.GET", b"ts:down", b"LATEST"],
20224            &[b"TS.INFO", b"ts:down"],
20225            &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
20226            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20227            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20228            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20229            &[b"TS.DEL", b"ts:a", b"0", b"1000"],
20230            // And the errors.
20231            &[b"SET", b"plain", b"v"],
20232            &[b"TS.ADD", b"plain", b"1", b"1"],
20233            &[b"TS.GET", b"plain"],
20234            &[b"TS.READ", b"plain", b"0"],
20235            &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
20236            &[b"TS.RANGE", b"gone", b"-", b"+"],
20237            &[b"TS.INFO", b"gone"],
20238        ];
20239
20240        let mut one = Fixture::new();
20241        let mut many = Fixture::striped(8);
20242        for parts in script {
20243            let a = one.run(parts);
20244            let b = many.run(parts);
20245            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20246        }
20247    }
20248
20249    /// A compaction rule whose two ends are on two stripes.
20250    ///
20251    /// This is the one thing in the family that walks from a key to another key,
20252    /// and it walks it in both directions: a sample on the source closes a
20253    /// bucket on the destination, a `LATEST` read on the destination folds the
20254    /// bucket the source is still filling, and a delete on the source rewrites
20255    /// what the destination already held. The same script is run against a
20256    /// server one stripe wide, where the two keys share a store, and against one
20257    /// eight stripes wide, where they do not.
20258    #[test]
20259    fn a_compaction_rule_across_stripes_reaches_both_ends() {
20260        let mut many = Fixture::striped(8);
20261        let other = apart(&mut many, "src");
20262        let (src, dst) = (b"src".as_slice(), other.as_bytes());
20263        let mut one = Fixture::new();
20264        let mut both = |parts: &[&[u8]]| {
20265            let a = one.run(parts);
20266            let b = many.run(parts);
20267            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20268            a
20269        };
20270
20271        both(&[b"TS.CREATE", src]);
20272        both(&[b"TS.CREATE", dst]);
20273        assert_eq!(
20274            both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
20275            "+OK\r\n"
20276        );
20277        both(&[b"TS.ADD", src, b"1000", b"1"]);
20278        both(&[b"TS.ADD", src, b"1500", b"3"]);
20279        // The bucket the source is filling is not written down yet, and asking
20280        // for it works it out off the source.
20281        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20282        let open = both(&[b"TS.GET", dst, b"LATEST"]);
20283        assert!(open.contains(":1000"), "the open bucket is folded: {open}");
20284
20285        // A sample past the bucket closes it, which is the write that has to
20286        // land on the other stripe.
20287        both(&[b"TS.ADD", src, b"2000", b"5"]);
20288        let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
20289        assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
20290        assert!(got.contains(":1000"), "{got}");
20291
20292        // And a delete on the source takes it away again.
20293        both(&[b"TS.DEL", src, b"1000", b"1999"]);
20294        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20295
20296        // Both ends still know about each other, and the link comes apart from
20297        // the source.
20298        assert!(
20299            both(&[b"TS.INFO", dst]).contains("src"),
20300            "the source is named"
20301        );
20302        assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
20303        assert_eq!(
20304            both(&[b"TS.DELETERULE", src, dst]),
20305            "-ERR TSDB: compaction rule does not exist\r\n"
20306        );
20307    }
20308
20309    /// A label filter takes the series it names wherever they landed.
20310    #[test]
20311    fn a_label_query_across_stripes_finds_every_series() {
20312        let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
20313        let mut many = Fixture::striped(8);
20314        let mut homes: Vec<usize> = names
20315            .iter()
20316            .map(|name| many.server.striped(0).stripe_of(name))
20317            .collect();
20318        homes.sort_unstable();
20319        homes.dedup();
20320        assert!(homes.len() > 1, "the six keys are not all on one stripe");
20321
20322        let mut one = Fixture::new();
20323        let mut both = |parts: &[&[u8]]| {
20324            let a = one.run(parts);
20325            let b = many.run(parts);
20326            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20327            a
20328        };
20329        for name in &names {
20330            both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
20331            both(&[b"TS.ADD", name, b"1000", b"1"]);
20332        }
20333
20334        let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
20335        assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
20336        assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20337        assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20338        assert_eq!(
20339            both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
20340            "*1\r\n$4\r\nroom\r\n"
20341        );
20342    }
20343
20344    /// Every hash command, and the field import beside it, on one stripe and on
20345    /// eight.
20346    ///
20347    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
20348    /// stripes do not draw the same numbers, so the only draw here is off a hash
20349    /// holding one field, where every generator gives the same answer.
20350    #[test]
20351    fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
20352        let script: &[&[&[u8]]] = &[
20353            &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
20354            &[b"HMSET", b"h", b"c", b"3"],
20355            &[b"HSETNX", b"h", b"a", b"9"],
20356            &[b"HSETNX", b"h", b"d", b"4"],
20357            &[b"HGET", b"h", b"a"],
20358            &[b"HGET", b"h", b"nope"],
20359            &[b"HMGET", b"h", b"a", b"nope"],
20360            &[b"HLEN", b"h"],
20361            &[b"HEXISTS", b"h", b"a"],
20362            &[b"HSTRLEN", b"h", b"a"],
20363            &[b"HGETALL", b"h"],
20364            &[b"HKEYS", b"h"],
20365            &[b"HVALS", b"h"],
20366            &[b"HINCRBY", b"h", b"a", b"5"],
20367            &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
20368            &[b"HSCAN", b"h", b"0"],
20369            &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
20370            &[b"HSCAN", b"h", b"0", b"NOVALUES"],
20371            &[b"HDEL", b"h", b"d"],
20372            &[b"HSET", b"one", b"f", b"v"],
20373            &[b"HRANDFIELD", b"one"],
20374            &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
20375            // The field deadlines.
20376            &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
20377            &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
20378            &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
20379            &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20380            &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20381            &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
20382            &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
20383            &[b"HGET", b"h", b"b"],
20384            // The three that came later and word everything their own way.
20385            &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
20386            &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
20387            &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
20388            &[b"HGET", b"h", b"e"],
20389            // And the import, whose key is the third word.
20390            &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
20391            &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
20392            &[b"HGETALL", b"imp"],
20393            &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
20394            &[b"HIMPORT", b"DISCARD", b"fs"],
20395            // And the errors.
20396            &[b"SET", b"plain", b"v"],
20397            &[b"HSET", b"plain", b"a", b"1"],
20398            &[b"HGETALL", b"plain"],
20399            &[b"HGET", b"gone", b"a"],
20400            &[b"HINCRBY", b"h", b"a", b"nan"],
20401        ];
20402
20403        let mut one = Fixture::new();
20404        let mut many = Fixture::striped(8);
20405        // The field deadlines are absolute milliseconds worked out from the
20406        // clock, so both servers are put on the same one rather than left to
20407        // read the wall a moment apart.
20408        one.server.set_clock_ms(1_700_000_000_000);
20409        many.server.set_clock_ms(1_700_000_000_000);
20410        for parts in script {
20411            let a = one.run(parts);
20412            let b = many.run(parts);
20413            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20414        }
20415    }
20416
20417    /// Every array command, on one stripe and on eight.
20418    #[test]
20419    fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20420        let script: &[&[&[u8]]] = &[
20421            &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20422            &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20423            &[b"ARGET", b"a", b"1"],
20424            &[b"ARGET", b"a", b"99"],
20425            &[b"ARMGET", b"a", b"0", b"5", b"99"],
20426            &[b"ARGETRANGE", b"a", b"0", b"7"],
20427            &[b"ARLEN", b"a"],
20428            &[b"ARCOUNT", b"a"],
20429            &[b"ARINSERT", b"a", b"m", b"n"],
20430            &[b"ARSCAN", b"a", b"0", b"20"],
20431            &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20432            &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20433            &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20434            &[b"ARLASTITEMS", b"a", b"2"],
20435            &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20436            &[b"ARNEXT", b"a"],
20437            &[b"ARSEEK", b"a", b"3"],
20438            &[b"AROP", b"a", b"0", b"20", b"USED"],
20439            &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20440            &[b"ARINFO", b"a"],
20441            &[b"ARINFO", b"a", b"FULL"],
20442            &[b"ARDEL", b"a", b"0"],
20443            &[b"ARDELRANGE", b"a", b"1", b"2"],
20444            &[b"ARCOUNT", b"a"],
20445            &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20446            &[b"ARGETRANGE", b"r", b"0", b"9"],
20447            // And the errors.
20448            &[b"SET", b"plain", b"v"],
20449            &[b"ARGET", b"plain", b"0"],
20450            &[b"ARSET", b"plain", b"0", b"v"],
20451            &[b"ARGET", b"gone", b"0"],
20452            &[b"ARSET", b"a", b"bad", b"v"],
20453        ];
20454
20455        let mut one = Fixture::new();
20456        let mut many = Fixture::striped(8);
20457        for parts in script {
20458            let a = one.run(parts);
20459            let b = many.run(parts);
20460            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20461        }
20462    }
20463
20464    /// Every graph and vector set command, on one stripe and on eight.
20465    ///
20466    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
20467    /// not: it draws from the stripe's generator, and the stripes do not share
20468    /// one.
20469    #[test]
20470    fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20471        let script: &[&[&[u8]]] = &[
20472            &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20473            &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20474            &[b"G.NADD", b"g", b"n3"],
20475            &[b"G.NGET", b"g", b"n1"],
20476            &[b"G.NGET", b"g", b"gone"],
20477            &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20478            &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20479            &[b"G.OUT", b"g", b"n1", b"knows"],
20480            &[b"G.IN", b"g", b"n2", b"knows"],
20481            &[b"G.DEG", b"g", b"n1", b"knows"],
20482            &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20483            &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20484            &[b"G.PATH", b"g", b"n1", b"n3"],
20485            &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20486            &[b"G.NDEL", b"g", b"n3"],
20487            &[b"G.NGET", b"g", b"n3"],
20488            // The vector set, which is one index under one key.
20489            &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20490            &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20491            &[b"VCARD", b"v"],
20492            &[b"VDIM", b"v"],
20493            &[b"VEMB", b"v", b"e1"],
20494            &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20495            &[b"VSIM", b"v", b"ELE", b"e1"],
20496            &[b"VISMEMBER", b"v", b"e1"],
20497            &[b"VISMEMBER", b"v", b"gone"],
20498            &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20499            &[b"VGETATTR", b"v", b"e1"],
20500            &[b"VRANGE", b"v", b"-", b"+"],
20501            &[b"VLINKS", b"v", b"e1"],
20502            &[b"VINFO", b"v"],
20503            &[b"VREM", b"v", b"e2"],
20504            &[b"VCARD", b"v"],
20505            // And the errors.
20506            &[b"SET", b"plain", b"v"],
20507            &[b"G.NGET", b"plain", b"n1"],
20508            &[b"VCARD", b"plain"],
20509            &[b"G.NADD", b"gone2", b"n"],
20510            &[b"VEMB", b"gone3", b"e"],
20511        ];
20512
20513        let mut one = Fixture::new();
20514        let mut many = Fixture::striped(8);
20515        for parts in script {
20516            let a = one.run(parts);
20517            let b = many.run(parts);
20518            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20519        }
20520    }
20521
20522    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
20523    /// command, on one stripe and on eight.
20524    #[test]
20525    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20526        let script: &[&[&[u8]]] = &[
20527            // The bloom filter.
20528            &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20529            &[b"BF.ADD", b"bf", b"a"],
20530            &[b"BF.ADD", b"bf", b"a"],
20531            &[b"BF.MADD", b"bf", b"b", b"c"],
20532            &[b"BF.EXISTS", b"bf", b"a"],
20533            &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20534            &[b"BF.CARD", b"bf"],
20535            &[b"BF.INFO", b"bf"],
20536            &[b"BF.INFO", b"bf", b"CAPACITY"],
20537            &[b"BF.DEBUG", b"bf"],
20538            &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20539            &[b"BF.EXISTS", b"made", b"x"],
20540            &[b"BF.SCANDUMP", b"bf", b"0"],
20541            // The cuckoo filter.
20542            &[b"CF.RESERVE", b"cf", b"100"],
20543            &[b"CF.ADD", b"cf", b"a"],
20544            &[b"CF.ADDNX", b"cf", b"a"],
20545            &[b"CF.COUNT", b"cf", b"a"],
20546            &[b"CF.EXISTS", b"cf", b"a"],
20547            &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20548            &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20549            &[b"CF.DEL", b"cf", b"a"],
20550            &[b"CF.COMPACT", b"cf"],
20551            &[b"CF.INFO", b"cf"],
20552            &[b"CF.DEBUG", b"cf"],
20553            &[b"CF.SCANDUMP", b"cf", b"0"],
20554            // The count min sketch.
20555            &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20556            &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20557            &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20558            &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20559            &[b"CMS.INFO", b"cms"],
20560            // The top k sketch.
20561            &[b"TOPK.RESERVE", b"tk", b"3"],
20562            &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20563            &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20564            &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20565            &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20566            &[b"TOPK.LIST", b"tk"],
20567            &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20568            &[b"TOPK.INFO", b"tk"],
20569            // The t digest.
20570            &[b"TDIGEST.CREATE", b"td"],
20571            &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20572            &[b"TDIGEST.MIN", b"td"],
20573            &[b"TDIGEST.MAX", b"td"],
20574            &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20575            &[b"TDIGEST.CDF", b"td", b"3"],
20576            &[b"TDIGEST.RANK", b"td", b"3"],
20577            &[b"TDIGEST.REVRANK", b"td", b"3"],
20578            &[b"TDIGEST.BYRANK", b"td", b"0"],
20579            &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20580            &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20581            &[b"TDIGEST.INFO", b"td"],
20582            &[b"TDIGEST.RESET", b"td"],
20583            &[b"TDIGEST.MIN", b"td"],
20584            // And the errors.
20585            &[b"SET", b"plain", b"v"],
20586            &[b"BF.ADD", b"plain", b"a"],
20587            &[b"CF.ADD", b"plain", b"a"],
20588            &[b"CMS.QUERY", b"plain", b"a"],
20589            &[b"TOPK.ADD", b"plain", b"a"],
20590            &[b"TDIGEST.ADD", b"plain", b"1"],
20591            &[b"CMS.INFO", b"gone"],
20592            &[b"TOPK.INFO", b"gone"],
20593            &[b"TDIGEST.INFO", b"gone"],
20594        ];
20595
20596        let mut one = Fixture::new();
20597        let mut many = Fixture::striped(8);
20598        for parts in script {
20599            let a = one.run(parts);
20600            let b = many.run(parts);
20601            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20602        }
20603    }
20604
20605    /// The two sketch merges, with their sources on stripes of their own.
20606    ///
20607    /// These are the only two commands in the ten groups that name more than one
20608    /// key, and both read a run of sources and write a destination, so both go
20609    /// wrong in the same way if a merge holds one store and looks every source up
20610    /// in it.
20611    #[test]
20612    fn a_sketch_merge_across_stripes_reads_every_source() {
20613        let mut many = Fixture::striped(8);
20614        let other = apart(&mut many, "s1");
20615        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20616        let mut one = Fixture::new();
20617        let mut both = |parts: &[&[u8]]| {
20618            let a = one.run(parts);
20619            let b = many.run(parts);
20620            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20621            a
20622        };
20623
20624        // The count min sketch. The destination has to be the sources' shape,
20625        // and it is named first, so all three keys are read before anything is
20626        // written.
20627        for key in [b"cd".as_slice(), s1, s2] {
20628            both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20629        }
20630        both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20631        both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20632        assert_eq!(
20633            both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20634            "+OK\r\n",
20635            "the merge took both sources"
20636        );
20637        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20638        // And with weights, which are read against the sources in order.
20639        both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20640        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20641        // A source that is not a sketch is answered before anything is written.
20642        both(&[b"SET", b"plain", b"v"]);
20643        assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20644        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20645
20646        // The t digest, which builds its destination and then puts it in place.
20647        // The two source keys are used again here, so what they held goes first.
20648        both(&[b"FLUSHALL"]);
20649        both(&[b"TDIGEST.CREATE", b"td"]);
20650        both(&[b"TDIGEST.CREATE", s1]);
20651        both(&[b"TDIGEST.CREATE", s2]);
20652        both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20653        both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20654        assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20655        assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20656        assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20657    }
20658
20659    /// Every shape of `SORT`, on one stripe and on eight.
20660    ///
20661    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
20662    /// destination are four different names and nothing lines them up, so on
20663    /// eight stripes this script is reading and writing all over the database
20664    /// while on one it is doing what it always did.
20665    #[test]
20666    fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20667        let script: &[&[&[u8]]] = &[
20668            &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20669            &[b"SORT", b"l"],
20670            &[b"SORT", b"l", b"DESC"],
20671            &[b"SORT", b"l", b"ALPHA"],
20672            &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20673            &[b"SORT_RO", b"l"],
20674            // A weight per element, so the order comes off keys the command
20675            // never named.
20676            &[
20677                b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20678            ],
20679            &[b"SORT", b"l", b"BY", b"w_*"],
20680            &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20681            &[b"DEL", b"w_2"],
20682            &[b"SORT", b"l", b"BY", b"w_*"],
20683            // And the answer off another set of keys again, with `#` mixed in
20684            // so the rows are not all lookups.
20685            &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20686            &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20687            // A pattern that reaches into a hash, which is another key again.
20688            &[b"HSET", b"h_1", b"f", b"9"],
20689            &[b"HSET", b"h_2", b"f", b"8"],
20690            &[b"HSET", b"h_3", b"f", b"7"],
20691            &[b"HSET", b"h_10", b"f", b"6"],
20692            &[b"SORT", b"l", b"BY", b"h_*->f"],
20693            &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20694            // The destination, which is a fourth place to land.
20695            &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20696            &[b"LRANGE", b"out", b"0", b"-1"],
20697            &[b"SORT", b"l", b"STORE", b"l"],
20698            &[b"LRANGE", b"l", b"0", b"-1"],
20699            // An empty result takes the destination away rather than leaving a
20700            // list of nothing behind.
20701            &[b"SORT", b"missing", b"STORE", b"out"],
20702            &[b"EXISTS", b"out"],
20703            // A set and a sorted set sort the same way a list does, and a set
20704            // written to a destination is sorted even when nothing asked.
20705            &[b"SADD", b"s", b"c", b"a", b"b"],
20706            &[b"SORT", b"s", b"ALPHA"],
20707            &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20708            &[b"LRANGE", b"out", b"0", b"-1"],
20709            &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20710            &[b"SORT", b"z", b"BY", b"nosort"],
20711            &[b"SORT", b"z", b"ALPHA", b"DESC"],
20712            // And the two ways it refuses: a key of the wrong type, and an
20713            // element that is not a number under a numeric sort.
20714            &[b"SET", b"str", b"v"],
20715            &[b"SORT", b"str"],
20716            &[b"RPUSH", b"words", b"one", b"two"],
20717            &[b"SORT", b"words"],
20718            &[b"SORT_RO", b"l", b"STORE", b"out"],
20719        ];
20720
20721        let mut one = Fixture::new();
20722        let mut many = Fixture::striped(8);
20723        for parts in script {
20724            let a = one.run(parts);
20725            let b = many.run(parts);
20726            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20727        }
20728    }
20729
20730    /// One `SORT` whose four kinds of key are on stripes of their own.
20731    ///
20732    /// The script above spreads keys around by writing enough of them, and this
20733    /// one checks the spread rather than trusting it: the list, the weight key
20734    /// for one of its elements and the destination are asserted to be in three
20735    /// places before the command runs.
20736    #[test]
20737    fn a_sort_across_stripes_reads_every_pattern_key() {
20738        let mut f = Fixture::striped(8);
20739        let out = apart(&mut f, "l");
20740        let (list, dest) = (b"l".as_slice(), out.as_bytes());
20741
20742        f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20743        f.run(&[
20744            b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20745        ]);
20746        f.run(&[
20747            b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20748        ]);
20749
20750        // The weights are four keys and they are not all in one place, which is
20751        // the thing that would go unnoticed if the command held a stripe.
20752        let db = f.server.striped(0);
20753        let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20754            .iter()
20755            .map(|k| db.stripe_of(k.as_slice()))
20756            .collect();
20757        assert!(
20758            weights.iter().any(|s| *s != weights[0]),
20759            "the four weight keys all landed on one stripe, so this proves nothing"
20760        );
20761
20762        assert_eq!(
20763            f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20764            "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20765            "the order came off the weights and the answer off the data keys"
20766        );
20767        assert_eq!(
20768            f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20769            ":4\r\n"
20770        );
20771        assert_eq!(
20772            f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20773            "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20774            "the destination is on a stripe of its own and got the whole answer"
20775        );
20776    }
20777
20778    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
20779    /// decide what shape it is stored in.
20780    ///
20781    /// This is the setting that would go wrong quietly. A stripe that kept the
20782    /// old ladder would hold the same hash in a different encoding from the
20783    /// stripe next to it, and the only thing that would ever say so is
20784    /// `OBJECT ENCODING`, which is why the check is on that.
20785    #[test]
20786    fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20787        let mut f = Fixture::striped(8);
20788        let other = apart(&mut f, "h");
20789        let (first, second) = (b"h".as_slice(), other.as_bytes());
20790
20791        assert_eq!(
20792            f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20793            "+OK\r\n"
20794        );
20795        assert_eq!(
20796            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20797            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20798            "the read comes off one stripe and has to answer for all of them"
20799        );
20800        for key in [first, second] {
20801            f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20802            assert_eq!(
20803                f.run(&[b"OBJECT", b"ENCODING", key]),
20804                "$8\r\nlistpack\r\n",
20805                "two fields is still under the ladder"
20806            );
20807            f.run(&[b"HSET", key, b"c", b"3"]);
20808            assert_eq!(
20809                f.run(&[b"OBJECT", b"ENCODING", key]),
20810                "$9\r\nhashtable\r\n",
20811                "three fields is over it, on whichever stripe the key is on"
20812            );
20813        }
20814
20815        // And the policy, which every stripe has to agree about for the same
20816        // reason: an eviction draws from one stripe at a time.
20817        assert_eq!(
20818            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20819            "+OK\r\n"
20820        );
20821        let db = f.server.striped(0);
20822        assert!(
20823            (0..db.width()).all(|i| db.hold_stripe(i).policy().name() == "allkeys-lru"),
20824            "a stripe kept the old policy"
20825        );
20826    }
20827
20828    /// What an index holds, as the two numbers `FT.INFO` reports about it.
20829    ///
20830    /// Read off the registry rather than parsed back out of an `FT.INFO` reply,
20831    /// because the reply is thirty odd fields and these two are the ones the
20832    /// keyspace hook moves.
20833    fn held(f: &Fixture, name: &[u8]) -> (usize, u32) {
20834        let search = f.server.search.lock();
20835        let index = search.named(name).expect("the index is there");
20836        (index.held.docs.len(), index.held.docs.last())
20837    }
20838
20839    /// A hash written under an index's prefix reaches it, and one written
20840    /// outside the prefix does not.
20841    #[test]
20842    fn a_hash_that_is_written_reaches_the_index_that_follows_it() {
20843        let mut f = Fixture::new();
20844        f.run(&[
20845            b"FT.CREATE",
20846            b"ix",
20847            b"PREFIX",
20848            b"1",
20849            b"p:",
20850            b"SCHEMA",
20851            b"t",
20852            b"TEXT",
20853        ]);
20854        f.run(&[b"HSET", b"p:1", b"t", b"running dogs"]);
20855        assert_eq!(held(&f, b"ix"), (1, 1));
20856        f.run(&[b"HSET", b"other:1", b"t", b"running dogs"]);
20857        assert_eq!(held(&f, b"ix"), (1, 1));
20858
20859        // Every field of the key and not the one the command named, since a
20860        // document is read from nothing every time.
20861        f.run(&[b"HSET", b"p:1", b"u", b"beta"]);
20862        f.run(&[b"HDEL", b"p:1", b"u"]);
20863        assert_eq!(held(&f, b"ix"), (1, 3));
20864        let search = f.server.search.lock();
20865        let index = search.named(b"ix").expect("there");
20866        assert_eq!(index.held.docs.id(b"p:1"), Some(3));
20867    }
20868
20869    /// A fresh index reads the keys that were already there, and walks past a
20870    /// key of the wrong type without counting a failure.
20871    #[test]
20872    fn a_fresh_index_reads_the_keys_that_were_already_there() {
20873        let mut f = Fixture::new();
20874        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20875        f.run(&[b"SET", b"p:str", b"not a hash"]);
20876        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
20877        f.run(&[
20878            b"FT.CREATE",
20879            b"ix",
20880            b"PREFIX",
20881            b"1",
20882            b"p:",
20883            b"SCHEMA",
20884            b"t",
20885            b"TEXT",
20886        ]);
20887
20888        assert_eq!(held(&f, b"ix"), (1, 1));
20889        let search = f.server.search.lock();
20890        let index = search.named(b"ix").expect("there");
20891        assert_eq!(index.trouble.whole().failures(), 0);
20892    }
20893
20894    /// `SKIPINITIALSCAN` leaves what was there alone, and a later write to one
20895    /// of those keys still lands.
20896    #[test]
20897    fn an_index_that_skipped_the_scan_fills_up_on_the_next_write() {
20898        let mut f = Fixture::new();
20899        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20900        f.run(&[
20901            b"FT.CREATE",
20902            b"ix",
20903            b"PREFIX",
20904            b"1",
20905            b"p:",
20906            b"SKIPINITIALSCAN",
20907            b"SCHEMA",
20908            b"t",
20909            b"TEXT",
20910        ]);
20911        assert_eq!(held(&f, b"ix"), (0, 0));
20912        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20913        assert_eq!(held(&f, b"ix"), (1, 1));
20914    }
20915
20916    /// A command that changed nothing leaves the document where it was, which
20917    /// is not the same as a command that was not a write.
20918    ///
20919    /// All five of these were measured against 8.10.1. Writing the same value
20920    /// again moves the number and a deadline set for later does not, which is
20921    /// the pair that makes the rule "the fields are not what they were" rather
20922    /// than "this was a write".
20923    #[test]
20924    fn only_a_real_change_gives_the_document_a_new_number() {
20925        let mut f = Fixture::new();
20926        f.run(&[
20927            b"FT.CREATE",
20928            b"ix",
20929            b"PREFIX",
20930            b"1",
20931            b"p:",
20932            b"SCHEMA",
20933            b"t",
20934            b"TEXT",
20935        ]);
20936        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20937        assert_eq!(held(&f, b"ix"), (1, 1));
20938
20939        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20940        assert_eq!(held(&f, b"ix"), (1, 2), "the same value still rewrites");
20941
20942        for quiet in [
20943            vec![b"HSETNX".as_slice(), b"p:1", b"t", b"other"],
20944            vec![b"HDEL".as_slice(), b"p:1", b"nosuch"],
20945            vec![b"HGET".as_slice(), b"p:1", b"t"],
20946            vec![b"HGETALL".as_slice(), b"p:1"],
20947            vec![b"HEXPIRE".as_slice(), b"p:1", b"100", b"FIELDS", b"1", b"t"],
20948            vec![b"HPERSIST".as_slice(), b"p:1", b"FIELDS", b"1", b"t"],
20949            vec![
20950                b"HGETEX".as_slice(),
20951                b"p:1",
20952                b"EX",
20953                b"100",
20954                b"FIELDS",
20955                b"1",
20956                b"t",
20957            ],
20958            vec![b"HGETDEL".as_slice(), b"p:1", b"FIELDS", b"1", b"nosuch"],
20959        ] {
20960            f.run(&quiet);
20961            assert_eq!(held(&f, b"ix"), (1, 2), "{:?} moved the document", quiet[0]);
20962        }
20963
20964        // And the ones that do change something.
20965        f.run(&[b"HSET", b"p:2", b"n", b"1"]);
20966        f.run(&[b"HINCRBY", b"p:2", b"n", b"1"]);
20967        assert_eq!(held(&f, b"ix"), (2, 4));
20968        // A deadline that has already passed takes the field away, and taking
20969        // the last field away takes the key and the document with it. The
20970        // number still moves on the way past, because the field going and the
20971        // key going are two separate pieces of news and the first of them
20972        // writes the document one last time.
20973        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"n"]);
20974        assert_eq!(held(&f, b"ix"), (1, 5));
20975    }
20976
20977    /// The two ways of emptying a hash, which do not leave the same thing
20978    /// behind. `HDEL` of the last field spends no number and is counted as a
20979    /// refusal, and a deadline that has already passed spends one on a document
20980    /// nobody sees and is counted as nothing. Measured against 8.10.1 and not
20981    /// something anyone would guess.
20982    #[test]
20983    fn a_key_emptied_by_a_deadline_spends_a_number_and_one_emptied_by_hdel_does_not() {
20984        /// The index's own failure count.
20985        fn refused(f: &Fixture, name: &[u8]) -> u64 {
20986            let search = f.server.search.lock();
20987            let index = search.named(name).expect("the index is there");
20988            index.trouble.whole().failures()
20989        }
20990
20991        let mut f = Fixture::new();
20992        f.run(&[
20993            b"FT.CREATE",
20994            b"ix",
20995            b"PREFIX",
20996            b"1",
20997            b"p:",
20998            b"SCHEMA",
20999            b"t",
21000            b"TEXT",
21001        ]);
21002        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21003        assert_eq!(held(&f, b"ix"), (1, 1));
21004        f.run(&[b"HDEL", b"p:1", b"t"]);
21005        assert_eq!(
21006            held(&f, b"ix"),
21007            (0, 1),
21008            "HDEL of the last field spends none"
21009        );
21010        assert_eq!(refused(&f, b"ix"), 1, "and is counted as a refusal");
21011
21012        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21013        assert_eq!(held(&f, b"ix"), (1, 2));
21014        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"t"]);
21015        assert_eq!(held(&f, b"ix"), (0, 3), "a deadline spends one");
21016        assert_eq!(refused(&f, b"ix"), 1, "and is counted as nothing");
21017
21018        f.run(&[b"HSET", b"p:3", b"t", b"alpha"]);
21019        assert_eq!(held(&f, b"ix"), (1, 4));
21020        f.run(&[b"HGETDEL", b"p:3", b"FIELDS", b"1", b"t"]);
21021        assert_eq!(held(&f, b"ix"), (0, 5), "and so does HGETDEL");
21022
21023        // Two fields and one command is one rewrite and not two, whichever way
21024        // the fields go.
21025        f.run(&[b"HSET", b"p:4", b"t", b"alpha", b"u", b"beta"]);
21026        assert_eq!(held(&f, b"ix"), (1, 6));
21027        f.run(&[b"HEXPIRE", b"p:4", b"0", b"FIELDS", b"2", b"t", b"u"]);
21028        assert_eq!(held(&f, b"ix"), (0, 7));
21029        assert_eq!(refused(&f, b"ix"), 1);
21030    }
21031
21032    /// `HSETEX` with a deadline that has already passed is two pieces of news
21033    /// from one command, so the number moves twice and the value never reaches
21034    /// the index.
21035    #[test]
21036    fn a_field_written_already_past_its_deadline_moves_the_number_twice() {
21037        let mut f = Fixture::new();
21038        f.run(&[
21039            b"FT.CREATE",
21040            b"ix",
21041            b"PREFIX",
21042            b"1",
21043            b"p:",
21044            b"SCHEMA",
21045            b"t",
21046            b"TEXT",
21047            b"u",
21048            b"TEXT",
21049        ]);
21050        f.run(&[b"HSET", b"p:1", b"u", b"keepme"]);
21051        assert_eq!(held(&f, b"ix"), (1, 1));
21052        f.run(&[
21053            b"HSETEX", b"p:1", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21054        ]);
21055        assert_eq!(
21056            held(&f, b"ix"),
21057            (1, 3),
21058            "the key lived and the field did not"
21059        );
21060
21061        // And the same when the key does not survive it.
21062        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21063        assert_eq!(held(&f, b"ix"), (2, 4));
21064        f.run(&[
21065            b"HSETEX", b"p:2", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21066        ]);
21067        assert_eq!(held(&f, b"ix"), (1, 6));
21068    }
21069
21070    /// A key that will not read is counted against the index and against the
21071    /// field, and `FT.INFO` says so.
21072    #[test]
21073    fn a_hash_that_will_not_read_is_counted_where_ft_info_reports_it() {
21074        let mut f = Fixture::new();
21075        f.run(&[
21076            b"FT.CREATE",
21077            b"ix",
21078            b"PREFIX",
21079            b"1",
21080            b"p:",
21081            b"SCHEMA",
21082            b"n",
21083            b"NUMERIC",
21084        ]);
21085        f.run(&[b"HSET", b"p:1", b"n", b"notanumber"]);
21086        assert_eq!(held(&f, b"ix"), (0, 0));
21087
21088        let reply = f.run(&[b"FT.INFO", b"ix"]);
21089        assert!(
21090            reply.contains("SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value: 'notanumber'"),
21091            "{reply}"
21092        );
21093        assert!(reply.contains("hash_indexing_failures"), "{reply}");
21094    }
21095
21096    /// An index can only be made on database zero, and the check comes after
21097    /// the `IFNX` shortcut and before everything else.
21098    #[test]
21099    fn an_index_can_only_be_made_on_database_zero() {
21100        let mut f = Fixture::new();
21101        f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]);
21102        f.run(&[b"SELECT", b"1"]);
21103        let refused = "-Cannot create index on db != 0\r\n";
21104        assert_eq!(
21105            f.run(&[b"FT.CREATE", b"jx", b"SCHEMA", b"t", b"TEXT"]),
21106            refused
21107        );
21108        // The name is taken, and it still answers about the database.
21109        assert_eq!(
21110            f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21111            refused
21112        );
21113        // And so does one whose arguments are nonsense.
21114        assert_eq!(
21115            f.run(&[b"FT.CREATE", b"zz", b"BOGUS", b"SCHEMA", b"t", b"TEXT"]),
21116            refused
21117        );
21118        // `IFNX` over a name that is taken is the one that gets through.
21119        assert_eq!(
21120            f.run(&[b"FT._CREATEIFNX", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21121            "+OK\r\n"
21122        );
21123        assert_eq!(f.server.search.lock().len(), 1);
21124    }
21125
21126    /// The scan reads the database the create was run on, and after that the
21127    /// index follows its keys in every database.
21128    ///
21129    /// The asymmetry is a real server's, measured, and it is the sort of thing
21130    /// nobody would arrive at by choosing.
21131    #[test]
21132    fn the_scan_is_one_database_and_the_following_is_all_of_them() {
21133        let mut f = Fixture::new();
21134        f.run(&[b"SELECT", b"1"]);
21135        f.run(&[b"HSET", b"p:9", b"t", b"on one"]);
21136        f.run(&[b"SELECT", b"0"]);
21137        f.run(&[b"HSET", b"p:0", b"t", b"on zero"]);
21138        f.run(&[
21139            b"FT.CREATE",
21140            b"ix",
21141            b"PREFIX",
21142            b"1",
21143            b"p:",
21144            b"SCHEMA",
21145            b"t",
21146            b"TEXT",
21147        ]);
21148        assert_eq!(held(&f, b"ix"), (1, 1), "the scan read database zero only");
21149
21150        f.run(&[b"SELECT", b"1"]);
21151        f.run(&[b"HSET", b"p:8", b"t", b"later"]);
21152        assert_eq!(
21153            held(&f, b"ix"),
21154            (2, 2),
21155            "and then it follows every database"
21156        );
21157    }
21158}