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    ///
405    /// Shared, because compaction is asked for from two places: the maintenance
406    /// turn, which is one thread, and a command that went over the memory limit
407    /// and is trying to get back under it, which is any thread. Two threads that
408    /// read the same cursor start on the same database, and what that costs is
409    /// one of them finding the other has already moved what was there.
410    next_db: AtomicUsize,
411    /// One bit per database, set when a command ran against it.
412    ///
413    /// The maintenance turn after every batch used to ask all sixteen
414    /// databases whether they had anything to collect, and asking costs a load
415    /// and a store in each one. Fifteen of those are cold lines on a server
416    /// where every client is on database zero, which is every server, and the
417    /// answer is no every time. This is the cheap half of the question: a
418    /// database nobody has touched since it last said no cannot have started
419    /// saying yes.
420    ///
421    /// The maintenance turn's own mask and not a shared one. Threads mark what
422    /// they have touched in [`Local::dirty`] and the turn takes those with
423    /// [`Server::collect_marks`] before it reads this, so nothing on a command
424    /// path writes here.
425    dirty: u64,
426    /// What the connections are holding, kept by the engine.
427    ///
428    /// Shared, because every thread has connections and the memory total is one
429    /// total. Each thread adds and subtracts its own change rather than storing
430    /// a figure it worked out, so two threads whose buffers grew in the same
431    /// moment both count.
432    conn_bytes: AtomicUsize,
433    /// The `maxmemory` limit in bytes, zero when there is not one.
434    ///
435    /// Zero is the default and it is the whole reason the check in front of
436    /// every write is one comparison against a field that is already warm. It
437    /// is read by every command on every thread and written by a client that
438    /// sends `CONFIG SET`, so it is a number the threads can share rather than
439    /// a field one of them owns.
440    maxmemory: AtomicU64,
441    /// Where a database gets a store from the first time it needs one.
442    ///
443    /// A closure and not a store, because there are sixteen databases and a
444    /// server that fills memory on database zero should not have opened
445    /// anything for the other fifteen. Nothing is asked of this until a memory
446    /// limit is actually reached, so a server that never fills memory never
447    /// opens a file, and a server that has no file never has one of these.
448    ///
449    /// `None` from the closure means that database cannot have one, which is
450    /// how the caller says the file it opened has no more room for logs.
451    ///
452    /// Behind a lock because it is a closure the caller gave us and there is no
453    /// saying it can be run by two threads at once. It is asked once per
454    /// database, the first time that database has to move something, so a
455    /// server that has reached its memory limit takes this lock sixteen times
456    /// in its life.
457    store: Lock<Option<Box<StoreSource>>>,
458    /// The `maxstore` limit in bytes, `None` when there is not one.
459    ///
460    /// The storage limit, and the other half of the inversion `14` section 4.1
461    /// describes. `maxmemory` is a limit on memory and the right answer to a
462    /// memory limit on a system with a file under it is to move data to the
463    /// file, not to delete it. Deleting is the right answer to a limit on the
464    /// file, and this is that limit.
465    ///
466    /// Zero is not "no limit" here, which is the one place this reads
467    /// differently from `maxmemory` and is the difference that makes a drop in
468    /// cache possible. A storage budget of zero bytes means nothing may live on
469    /// the file, so migration cannot make room and eviction is the only thing
470    /// left, which is Redis exactly. `None` is no limit and is the default,
471    /// which with `noeviction` means the database grows until the disk is full
472    /// and then writes fail, which is what a database does.
473    ///
474    /// Shared between the threads the same way `maxmemory` is, and no limit is
475    /// [`NO_MAXSTORE`] rather than a second field saying whether the first one
476    /// counts. Two fields cannot be read as one, and a limit that was on when
477    /// the bytes were read and off by the time the number was is a limit that
478    /// answers from a server that never existed.
479    maxstore: AtomicU64,
480    /// What [`Server::memory_bytes`] said at the last maintenance turn.
481    ///
482    /// The reading is a walk over every collection in every database and cannot
483    /// go on a command path, so the command path reads this instead and is at
484    /// most one batch behind. What that costs is overshoot: a server can end a
485    /// batch holding one batch's worth of allocation more than its limit before
486    /// anything notices. A batch is 64 commands, so that is bounded by what 64
487    /// commands can allocate and not by how long the server runs.
488    ///
489    /// Only kept up to date when there is a limit to judge it against. A server
490    /// with no `maxmemory` never reads it and never pays for it.
491    ///
492    /// Shared, because it is read in front of every write on every thread and
493    /// written by whichever thread last took a reading. A reader that catches it
494    /// mid write gets one of the two readings and both of them were true a
495    /// moment ago, which is all this number ever claims to be.
496    used: AtomicUsize,
497    /// Which database the next eviction draws from.
498    ///
499    /// Its own cursor and not [`Server::next_db`], because eviction and
500    /// compaction move at different rates and sharing one would make the
501    /// database that gets compacted depend on how many keys were evicted.
502    ///
503    /// Shared for the same reason [`Server::next_db`] is, and with the same
504    /// answer: two threads evicting at once may pick the same database, and one
505    /// of them finds the other got there first and moves on.
506    evict_db: AtomicUsize,
507    /// Which database the next active expiry sweep starts at.
508    ///
509    /// A third cursor for the same reason there is a second one. A sweep runs on
510    /// every turn of the loop and compaction runs when there is dead space, so
511    /// sharing a cursor would make which database gets swept depend on which one
512    /// was last collected.
513    expire_db: usize,
514    /// The millisecond the last active expiry sweep ran on, so the next one on
515    /// the same millisecond does not bother.
516    expire_ms: u64,
517    /// Clients parked on a blocking command.
518    ///
519    /// Behind a lock because a client parks on the thread that ran its command
520    /// and is woken by whichever thread later puts something under a key it
521    /// named, and those are not the same thread. The lock is only ever taken to
522    /// park somebody, to serve somebody or to forget a connection that has gone,
523    /// so a command that does not block never touches it.
524    waiters: Lock<Waiters>,
525    /// How many clients are parked.
526    ///
527    /// Beside the list rather than read out of it, because every command asks
528    /// whether anybody is waiting and nearly every answer is no. Taking a lock
529    /// to be told no would be a cache line every thread has to own to ask, which
530    /// is the cost the list was put behind a lock to avoid.
531    ///
532    /// Written under the lock, by whoever changed the list, so the number and
533    /// the list agree except while a change is in progress. A reader that asks
534    /// during one is told about the moment before it, and the worst that costs
535    /// is a walk of the list that serves nobody or one that has not started yet
536    /// and happens on the next command instead.
537    parked: AtomicUsize,
538    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
539    ///
540    /// Empty on a server nobody has migrated a key out of, which is nearly all
541    /// of them, and it costs a vector's three words to be empty.
542    ///
543    /// Behind a lock because a socket cannot be written by two threads at once
544    /// and a cache of them cannot be searched by one while another is taking an
545    /// entry out. It is held for the whole of a migration, which is a round trip
546    /// to another server, so two threads migrating at the same time take turns.
547    /// That is the right way round: the alternative is a socket per thread per
548    /// peer, and a `MIGRATE` is not what a server spends its time on.
549    peers: Lock<migrate::Peers>,
550    /// What each thread that runs commands here keeps to itself.
551    ///
552    /// A fixed list, because a thread reading its own entry must not have the
553    /// list move under it, and how many threads there will be is known before
554    /// any of them starts. A server nobody told otherwise has one.
555    locals: Box<[Local]>,
556    /// How many entries have been handed out.
557    claimed: AtomicUsize,
558    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
559    ///
560    /// Absolute, and resolved once when the server is built rather than every
561    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
562    /// entitled to hand one of them to a copy tool, so a relative path that
563    /// meant something different after a `chdir` would be a path that stops
564    /// working for reasons nobody could see.
565    dir: PathBuf,
566    /// What backup is running, if one is.
567    ///
568    /// On the server and not on a session, because a backup outlives the
569    /// connection that asked for it and any other connection can seal it.
570    ///
571    /// Behind a lock because there is one backup at a time and any thread can be
572    /// the one that starts, seals or abandons it. It is held while the base file
573    /// is written, which is what keeps two `BACKUP START` commands from writing
574    /// over each other's files.
575    backup: Lock<backup::State>,
576    /// Whether a sealed backup is sitting on disk.
577    ///
578    /// Beside the state rather than read out of it, because every batch of
579    /// commands asks whether there is a backup old enough to sweep away and on
580    /// nearly every server the answer is that there is no backup at all. A load
581    /// answers that. Written under the lock by whoever moved the phase, so a
582    /// reader that asks mid-change sees the moment before and sweeps one batch
583    /// later, which is a file staying on disk for a few microseconds longer than
584    /// it had to.
585    sealed: AtomicBool,
586    /// The search indexes and the names pointing at them.
587    ///
588    /// On the server and not on a database, which is the one collection in this
589    /// build that is. A real server keeps its indexes in the search module, the
590    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
591    /// indexes made on database zero. `search.rs` has the rest of why.
592    ///
593    /// A server nobody has made an index on holds two empty vectors here, which
594    /// is six words and no allocation.
595    ///
596    /// Behind a lock because an index is made and dropped by whichever thread
597    /// ran the command, and the table it goes in is one table. Only the `FT`
598    /// commands take it, so nothing a working server spends its time on comes
599    /// through here.
600    search: Lock<Registry>,
601    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
602    ///
603    /// A flag rather than an exit, because the command layer is not what owns
604    /// the process. It runs inside a batch that has other commands behind it
605    /// and inside a driver that has a socket file to take away and a file to
606    /// close, and a server that calls `exit` from a command handler skips all
607    /// of that. So the command says stop and the driver stops, on the same turn
608    /// and through the same door a signal uses.
609    stopping: AtomicBool,
610}
611
612impl Server {
613    /// A server with [`DATABASES`] empty databases on the system clock.
614    #[must_use]
615    pub fn new() -> Server {
616        let clock = Clock::system();
617        Server {
618            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
619            width: 1,
620            clock,
621            started_ms: clock.now_ms(),
622            next_db: AtomicUsize::new(0),
623            dirty: ALL_DATABASES,
624            conn_bytes: AtomicUsize::new(0),
625            maxmemory: AtomicU64::new(0),
626            store: Lock::new(None),
627            maxstore: AtomicU64::new(NO_MAXSTORE),
628            used: AtomicUsize::new(0),
629            evict_db: AtomicUsize::new(0),
630            expire_db: 0,
631            expire_ms: 0,
632            waiters: Lock::default(),
633            parked: AtomicUsize::new(0),
634            peers: Lock::default(),
635            locals: one_thread(),
636            claimed: AtomicUsize::new(0),
637            dir: working_dir(),
638            backup: Lock::default(),
639            sealed: AtomicBool::new(false),
640            search: Lock::new(Registry::new()),
641            stopping: AtomicBool::new(false),
642        }
643    }
644
645    /// A server whose databases are cut into `width` stripes each.
646    ///
647    /// Not reachable from the command line yet. Every command group answers on
648    /// a server of any width now and so does everything that walks a whole
649    /// database, and the tests run each group at a width of one and a width of
650    /// eight and check the two agree.
651    ///
652    /// What is left before this is what `--threads` sets is the engine. A
653    /// database being several objects is what makes more than one thread
654    /// possible, and it is not what makes more than one thread happen.
655    #[must_use]
656    pub fn with_width(width: usize) -> Server {
657        let clock = Clock::system();
658        let mut server = Server::new();
659        server.dbs = (0..DATABASES)
660            .map(|_| Db::with_clock(clock, width))
661            .collect();
662        server.width = server.dbs[0].width();
663        server
664    }
665
666    /// A server on a clock the caller moves by hand, for tests.
667    #[must_use]
668    pub fn with_clock(clock: Clock) -> Server {
669        Server {
670            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
671            width: 1,
672            clock,
673            started_ms: clock.now_ms(),
674            next_db: AtomicUsize::new(0),
675            dirty: ALL_DATABASES,
676            conn_bytes: AtomicUsize::new(0),
677            maxmemory: AtomicU64::new(0),
678            store: Lock::new(None),
679            maxstore: AtomicU64::new(NO_MAXSTORE),
680            used: AtomicUsize::new(0),
681            evict_db: AtomicUsize::new(0),
682            expire_db: 0,
683            expire_ms: 0,
684            waiters: Lock::default(),
685            parked: AtomicUsize::new(0),
686            peers: Lock::default(),
687            locals: one_thread(),
688            claimed: AtomicUsize::new(0),
689            dir: working_dir(),
690            backup: Lock::default(),
691            sealed: AtomicBool::new(false),
692            search: Lock::new(Registry::new()),
693            stopping: AtomicBool::new(false),
694        }
695    }
696
697    /// One database, by index.
698    ///
699    /// A caller that knows which key it wants names the one stripe the key is
700    /// on rather than working over the whole thing, which is what `at` and its
701    /// neighbours on [`Db`] are for. A caller that is about a database rather
702    /// than about a key, which is the snapshot walk and a setting, works over
703    /// all of them.
704    ///
705    /// The database is marked as having had something run against it, which is
706    /// what this does that [`Server::striped_ref`] does not. Anything that only
707    /// reads asks for that one and leaves the mark alone.
708    ///
709    /// The borrow is shared, and what makes that enough is that a database is
710    /// several stripes behind a lock each. A caller that wants to change
711    /// something holds the stripe it is changing, so two threads working on two
712    /// keys work at once and two working on one key take turns, which is the
713    /// whole point of cutting a database up.
714    ///
715    /// # Panics
716    ///
717    /// If `i` is not a database. `SELECT` is the only way a client changes the
718    /// index and it checks, so an index that is out of range here is a bug in
719    /// the caller and not something a client can ask for.
720    pub fn striped(&self, i: usize) -> &Db {
721        self.mine().mark(1u64 << i);
722        &self.dbs[i]
723    }
724
725    /// Every keyspace on the server, which is every stripe of every database.
726    ///
727    /// What the aggregates walk. A total over the whole server is a total over
728    /// all of these and the stripe boundaries do not appear in it, which is
729    /// what makes the numbers `INFO` reports the same numbers whatever the
730    /// server was cut into.
731    fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
732        self.dbs
733            .iter()
734            .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
735    }
736
737    /// How many keyspaces there are, counting every stripe of every database.
738    ///
739    /// The maintenance turns walk these rather than the databases, because a
740    /// stripe is the thing that holds an arena and a deadline heap and so it is
741    /// the thing that has anything to collect.
742    const fn slots(&self) -> usize {
743        DATABASES * self.width
744    }
745
746    /// Which database slot `i` belongs to.
747    const fn slot_db(&self, i: usize) -> usize {
748        i / self.width
749    }
750
751    /// Keyspace `i` of [`Server::slots`].
752    fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
753        let (db, stripe) = (i / self.width, i % self.width);
754        self.dbs[db].stripe_mut(stripe)
755    }
756
757    /// The same, without taking it mutably.
758    fn slot(&self, i: usize) -> Held<'_, Keyspace> {
759        let (db, stripe) = (i / self.width, i % self.width);
760        self.dbs[db].hold_stripe(stripe)
761    }
762
763    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
764    #[must_use]
765    pub fn dir(&self) -> &Path {
766        &self.dir
767    }
768
769    /// Point the server at a different directory, which `yodb serve --dir` does.
770    ///
771    /// Only before it is serving. There is no `CONFIG SET dir` here and there
772    /// is none on a real server either without turning protected configs on,
773    /// for the good reason that moving it out from under a running backup would
774    /// leave files nothing can find again.
775    pub fn set_dir(&mut self, dir: PathBuf) {
776        self.dir = dir;
777    }
778
779    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
780    ///
781    /// Once per batch, from the same maintenance turn that collects the arena.
782    /// It reads two fields and returns on a server that has never taken a
783    /// backup, which is nearly all of them.
784    pub fn backup_expire(&self) {
785        backup::expire(self);
786    }
787
788    /// Ask for the server to stop, which is what `SHUTDOWN` does.
789    ///
790    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
791    /// or ends the process, because none of those belong to this layer, and a
792    /// batch that is halfway through still has to finish and be written out.
793    pub fn stop(&self) {
794        self.stopping.store(true, Release);
795    }
796
797    /// Whether somebody has asked the server to stop.
798    ///
799    /// Read once per turn by the loop, next to the flag a signal sets. The two
800    /// mean the same thing and are separate only because one arrives from the
801    /// operating system and the other from a client.
802    #[must_use]
803    pub fn stopping(&self) -> bool {
804        self.stopping.load(Acquire)
805    }
806
807    /// One database, by index, without taking it mutably.
808    ///
809    /// What the prefetch stage needs. It runs for all 64 commands in a batch
810    /// before any of them executes, so it cannot hold the mutable borrow `run`
811    /// is about to want, and it does not need one: warming a cache line reads
812    /// nothing and changes nothing.
813    #[must_use]
814    pub fn striped_ref(&self, i: usize) -> &Db {
815        &self.dbs[i]
816    }
817
818    /// The stripe that answers for a database when a setting is read back.
819    ///
820    /// A ladder setting and an eviction policy are one number on a real server,
821    /// and the fact that every stripe of every database carries a copy of it is
822    /// ours rather than the client's problem. A write puts the same value on
823    /// every one of them, so any stripe answers for all of them and this is the
824    /// first one.
825    fn settings(&self) -> Held<'_, Keyspace> {
826        self.dbs[0].hold_stripe(0)
827    }
828
829    /// Take a new clock reading and give it to every database.
830    ///
831    /// Once per turn of the event loop, which is the only place time moves. A
832    /// command asking what the time is gets the answer the whole batch got, so
833    /// two keys written by the same batch expire together (`04` section 3).
834    pub fn refresh_clock(&mut self) {
835        self.clock.refresh();
836        let now = self.clock.now_ms();
837        for db in &mut self.dbs {
838            db.set_clock_ms(now);
839        }
840    }
841
842    /// Move every clock here on by `ms`, for tests about expiry.
843    ///
844    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
845    /// except that it moves from wherever the clock is rather than to a stated
846    /// moment, which is what a test that wants a key to have expired asks for.
847    pub fn advance_clock_ms(&mut self, ms: u64) {
848        let now = self.clock.now_ms() + ms;
849        self.set_clock_ms(now);
850    }
851
852    /// Move every clock here to `ms` by hand, for tests about expiry.
853    ///
854    /// A test cannot wait a hundred seconds and a test that waits a hundred
855    /// milliseconds is a test that fails on a loaded machine, so time moves on
856    /// request. The system clock underneath will overwrite this on the next
857    /// [`Server::refresh_clock`], which is why this is only useful in a test
858    /// that drives commands directly rather than through the event loop.
859    pub fn set_clock_ms(&mut self, ms: u64) {
860        self.clock.set(ms);
861        for db in &mut self.dbs {
862            db.set_clock_ms(ms);
863        }
864    }
865
866    /// Seconds since this server was built.
867    #[must_use]
868    pub fn uptime_secs(&self) -> u64 {
869        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
870    }
871
872    /// Bytes held by every database's index and arena, plus the read and reply
873    /// buffers of every connection.
874    ///
875    /// The buffers are in here because they are real and because Redis counts
876    /// its own, so leaving them out would make the one number people compare
877    /// flattering rather than true. They are not a database, so nothing in the
878    /// keyspace can change them and the engine has to say when they move.
879    #[must_use]
880    pub fn memory_bytes(&self) -> usize {
881        self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes()
882    }
883
884    /// What the keyspace itself is holding, live records only.
885    ///
886    /// `used_memory` minus this is what the store costs to run: the index, the
887    /// space dead records are sitting in until compaction gets to them, and the
888    /// connections' buffers.
889    #[must_use]
890    pub fn dataset_bytes(&self) -> usize {
891        self.keyspaces()
892            .map(|db| db.map().arena().live_bytes() as usize)
893            .sum()
894    }
895
896    /// Bytes the arenas are holding, live and dead together.
897    #[must_use]
898    pub fn arena_bytes(&self) -> usize {
899        self.keyspaces()
900            .map(|db| db.map().arena().reserved_bytes() as usize)
901            .sum()
902    }
903
904    /// Bytes the indexes are holding.
905    #[must_use]
906    pub fn index_bytes(&self) -> usize {
907        self.keyspaces()
908            .map(|db| db.map().index().memory_bytes())
909            .sum()
910    }
911
912    /// What arena compaction has cost, across every database.
913    ///
914    /// The write amplification of value separation, which is invisible from the
915    /// outside otherwise: a client that writes a megabyte can leave the store
916    /// copying several more, and the only sign of it without these is that the
917    /// writes got slower.
918    #[must_use]
919    pub fn compaction(&self) -> yo_kv::Compaction {
920        self.keyspaces().map(|db| db.map().compaction()).fold(
921            yo_kv::Compaction::default(),
922            |a, b| yo_kv::Compaction {
923                walked: a.walked + b.walked,
924                moved: a.moved + b.moved,
925                bytes: a.bytes + b.bytes,
926            },
927        )
928    }
929
930    /// Arena segments whose pages are real, across every database.
931    #[must_use]
932    pub fn segment_count(&self) -> usize {
933        self.keyspaces()
934            .map(|db| db.map().arena().resident_segments())
935            .sum()
936    }
937
938    /// What the connections' read and reply buffers are holding.
939    #[must_use]
940    pub fn conn_bytes(&self) -> usize {
941        self.conn_bytes.load(Relaxed)
942    }
943
944    /// Note that the connections are holding `delta` bytes more than they were,
945    /// or fewer when it is negative.
946    ///
947    /// A delta and not a total because the alternative is a walk over every
948    /// connection, and the walk would have to happen on a turn of the loop
949    /// rather than when `INFO` asks, which puts the cost of a report on the
950    /// command path of a server nobody is asking.
951    pub fn note_conn_bytes(&mut self, delta: isize) {
952        // A read and a write and not a fetch and add, because the number is a
953        // sum of signed changes and the saturating part has to happen in the
954        // middle. Two threads that change their buffers in the same instant can
955        // lose one of the two changes, which is a report that is a few kilobytes
956        // out until the next connection on either thread moves it again.
957        self.conn_bytes
958            .store(self.conn_bytes().saturating_add_signed(delta), Relaxed);
959    }
960
961    /// Keys reclaimed by running into them after their deadline.
962    #[must_use]
963    pub fn expired_keys(&self) -> u64 {
964        self.keyspaces().map(|db| db.expired_keys()).sum()
965    }
966
967    /// Keys thrown away to make room, which is the other number entirely.
968    #[must_use]
969    pub fn evicted_keys(&self) -> u64 {
970        self.keyspaces().map(|db| db.evicted_keys()).sum()
971    }
972
973    /// Every command that has been seen, with its counters.
974    ///
975    /// Only the ones that have. A server reports a handful of lines rather than
976    /// one per command in the table, which is what Redis does and is the
977    /// difference between a section a person can read and one they cannot.
978    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
979        (0..table::count())
980            .map(|at| (table::name_at(at), self.command_stat(at)))
981            .filter(|(_, row)| row.seen())
982    }
983
984    /// One command's counters, added up over every thread.
985    fn command_stat(&self, at: usize) -> CommandStat {
986        let mut sum = CommandStat::default();
987        for thread in &self.locals {
988            let row = &thread.cmdstats.0[at];
989            sum.calls += row.calls.get();
990            sum.rejected += row.rejected.get();
991            sum.failed += row.failed.get();
992        }
993        sum
994    }
995
996    /// The counters the calling thread writes into.
997    ///
998    /// The first call on a thread claims a set and every call after it is a
999    /// thread local read and an index. A server asked to count from more threads
1000    /// than it was built for wraps round and shares a set, which loses the odd
1001    /// count between two threads and cannot happen to a server `yodb serve`
1002    /// built, because that one is told how many threads it will have before it
1003    /// starts any of them.
1004    pub fn counted(&self) -> &Stats {
1005        &self.mine().stats
1006    }
1007
1008    /// Everything the calling thread keeps to itself.
1009    fn mine(&self) -> &Local {
1010        let mut slot = SLOT.get();
1011        if slot == usize::MAX {
1012            slot = self.claimed.fetch_add(1, Relaxed);
1013            SLOT.set(slot);
1014        }
1015        &self.locals[slot % self.locals.len()]
1016    }
1017
1018    /// Every thread's numbers added together, which is what `INFO` reports.
1019    #[must_use]
1020    pub fn totals(&self) -> Totals {
1021        let mut sum = Totals::default();
1022        for thread in &self.locals {
1023            sum.clients += thread.stats.clients.get();
1024            sum.connections += thread.stats.connections.get();
1025            sum.commands += thread.stats.commands.get();
1026        }
1027        sum
1028    }
1029
1030    /// Put the totals back to zero, which is `CONFIG RESETSTAT`.
1031    ///
1032    /// Every thread's set and not only the one asking, since the number the
1033    /// client is resetting is the sum it was just shown. The open connections
1034    /// are left alone because that is a gauge and not a total: the connections
1035    /// are still open.
1036    pub fn reset_stats(&self) {
1037        for thread in &self.locals {
1038            thread.stats.connections.zero();
1039            thread.stats.commands.zero();
1040        }
1041    }
1042
1043    /// Say how many threads will run commands here, before any of them does.
1044    ///
1045    /// What it changes is how many sets of counters there are. Called once at
1046    /// startup by whoever is about to start the threads, and calling it on a
1047    /// running server throws away what has been counted so far, which is why it
1048    /// wants the server to itself.
1049    pub fn set_threads(&mut self, threads: usize) {
1050        self.locals = slots(threads);
1051        self.claimed = AtomicUsize::new(0);
1052    }
1053
1054    /// The `maxmemory` limit in bytes, zero when there is not one.
1055    #[must_use]
1056    pub fn maxmemory(&self) -> u64 {
1057        self.maxmemory.load(Relaxed)
1058    }
1059
1060    /// Set the limit, and take a reading straight away.
1061    ///
1062    /// The reading is here rather than left to the next maintenance turn because
1063    /// a client that sets the limit and sends a write in the same batch expects
1064    /// the write to be judged against the limit it just set, and because the
1065    /// cached number is meaningless until the first time there is a limit to
1066    /// compare it with.
1067    ///
1068    /// Turning the limit on also turns on the running total every slab keeps of
1069    /// what its collections hold, and turning it off turns that back off, so a
1070    /// server with no limit is not paying to count something nobody reads. The
1071    /// first reading after switching it on is the walk that the total starts
1072    /// from, and it is the only walk.
1073    pub fn set_maxmemory(&self, bytes: u64) {
1074        self.maxmemory.store(bytes, Relaxed);
1075        for db in &self.dbs {
1076            db.track_memory(bytes != 0);
1077        }
1078        self.used.store(self.settled_memory(), Relaxed);
1079    }
1080
1081    /// Say where a database should get its store from when it needs one.
1082    ///
1083    /// This is what turns the eviction inversion on. Until it is called every
1084    /// database answers a memory limit by evicting, which is Redis, and after it
1085    /// is called a database under memory pressure moves values to whatever the
1086    /// closure hands back instead of throwing keys away.
1087    ///
1088    /// Called at most once per database and only under pressure, so a server
1089    /// that is given a file and never fills memory never touches it.
1090    pub fn set_store_source(
1091        &mut self,
1092        source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
1093    ) {
1094        *self.store.lock() = Some(Box::new(source));
1095    }
1096
1097    /// Whether this server has been given somewhere to put cold values.
1098    #[must_use]
1099    pub fn has_store_source(&self) -> bool {
1100        self.store.lock().is_some()
1101    }
1102
1103    /// Open database `at`'s store, if it has not got one and there is one to be
1104    /// had.
1105    ///
1106    /// A store that will not open leaves the database where it was, which is
1107    /// evicting, because a memory limit that cannot be answered by moving data
1108    /// still has to be answered.
1109    fn attach_store(&self, at: usize) {
1110        if self.slot(at).store_bytes().is_some() {
1111            return;
1112        }
1113        // The closure is run with its lock held and the keyspace is taken after
1114        // it has answered, so the file is opened once however many threads asked
1115        // for it and the stripe is not held while a file is being opened.
1116        let mut source = self.store.lock();
1117        let Some(source) = source.as_mut() else {
1118            return;
1119        };
1120        if let Some(blocks) = source(at) {
1121            self.slot(at).attach(blocks);
1122        }
1123    }
1124
1125    /// The `maxstore` limit in bytes, `None` when there is not one.
1126    #[must_use]
1127    pub fn maxstore(&self) -> Option<u64> {
1128        match self.maxstore.load(Relaxed) {
1129            NO_MAXSTORE => None,
1130            bytes => Some(bytes),
1131        }
1132    }
1133
1134    /// Set the storage limit, or clear it with `None`.
1135    ///
1136    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
1137    /// total, because this limit is compared against a number the store keeps
1138    /// and answers on demand, not against a walk.
1139    pub fn set_maxstore(&self, bytes: Option<u64>) {
1140        self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1141    }
1142
1143    /// What every attached store is holding, for `INFO memory`.
1144    ///
1145    /// Zero on a server with nothing attached, which is not the same as a server
1146    /// whose file is empty, and [`Server::regime`] is the field that tells those
1147    /// two apart.
1148    #[must_use]
1149    pub fn store_bytes(&self) -> u64 {
1150        self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1151    }
1152
1153    /// What the file has been asked to do, added up over every database.
1154    ///
1155    /// Counters and not levels, so they only ever go up and a run is the
1156    /// difference between two readings. G9 is a ratio over these: the faults a
1157    /// run took, divided by the point reads it issued, has to come out at 1.05
1158    /// or less with a working set ten times memory. There is no way to work that
1159    /// out from outside the server, so it is reported rather than inferred.
1160    ///
1161    /// A fault is a read that went to the store. Whether it also went to the
1162    /// device depends on the store: a log serves a read out of a resident page
1163    /// without touching anything. At ten times memory almost every fault is a
1164    /// real read, which is why the gate is written against this number, but the
1165    /// two are not the same thing and a run tight against the bar should be
1166    /// checked against what the operating system says.
1167    #[must_use]
1168    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1169        let mut total = yo_kv::tier::Stats::default();
1170        for db in self.keyspaces() {
1171            let Some(tier) = db.tier() else { continue };
1172            let s = tier.stats();
1173            total.demoted += s.demoted;
1174            total.promoted += s.promoted;
1175            total.faults += s.faults;
1176            total.served += s.served;
1177            total.bytes_out += s.bytes_out;
1178            total.bytes_in += s.bytes_in;
1179        }
1180        total
1181    }
1182
1183    /// Which way this server answers a memory limit, in one word for `INFO`.
1184    ///
1185    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
1186    /// inversion: a memory limit moves values to the file and nothing stored is
1187    /// lost. A server reports one word rather than leaving an operator to work
1188    /// it out from a limit, a setting and whether a file happens to be open.
1189    #[must_use]
1190    pub fn regime(&self) -> &'static str {
1191        if (0..self.slots()).any(|at| self.migrates(at)) {
1192            "migrate"
1193        } else {
1194            "evict"
1195        }
1196    }
1197
1198    /// Whether database `at` answers a memory limit by moving values to the
1199    /// file rather than by throwing keys away.
1200    ///
1201    /// Three things have to hold. There has to be somewhere to move them, which
1202    /// is a store attached to that database or a source that can open one, and
1203    /// on a server that was never given a file this is false everywhere and
1204    /// every database behaves exactly as it did.
1205    /// The storage budget has to be more than nothing, which is what
1206    /// `maxstore 0` says it is not. And the file has to be under that budget,
1207    /// because a full file is a storage limit reached and eviction is the right
1208    /// answer to a storage limit.
1209    fn migrates(&self, at: usize) -> bool {
1210        let cap = self.maxstore();
1211        if cap == Some(0) {
1212            return false;
1213        }
1214        // Out of the stripe first. A match keeps whatever it is looking at
1215        // alive for the whole of itself, and that would be this stripe held
1216        // across the arms for no reason.
1217        let bytes = self.slot(at).store_bytes();
1218        match bytes {
1219            Some(held) => cap.is_none_or(|cap| held < cap),
1220            // Nothing attached, but somewhere to get one from the moment this
1221            // database needs it, which is what makes the answer yes rather than
1222            // no. Opening it here would mean `INFO` opened files.
1223            None => self.store.lock().is_some(),
1224        }
1225    }
1226
1227    /// Take a fresh memory reading, which the maintenance turn does once a batch.
1228    ///
1229    /// Nothing at all when there is no limit, which is the default and is every
1230    /// server that has not asked for one.
1231    pub fn refresh_memory(&self) {
1232        if self.maxmemory() != 0 {
1233            self.used.store(self.settled_memory(), Relaxed);
1234        }
1235    }
1236
1237    /// [`Server::memory_bytes`], asked the cheap way.
1238    ///
1239    /// The same number. The difference is that this asks each database only
1240    /// about the collections that could have moved since the last time, which is
1241    /// what a batch touched rather than what the server holds, so it can be
1242    /// asked once a batch and again on every command that is over the limit.
1243    fn settled_memory(&self) -> usize {
1244        self.keyspaces()
1245            .map(|mut db| db.settled_memory_bytes())
1246            .sum::<usize>()
1247            + self.conn_bytes()
1248    }
1249
1250    /// Make room under the `maxmemory` limit, throwing keys away if that is what
1251    /// it takes. Answers whether there is anything left it could throw away.
1252    ///
1253    /// Redis runs the same thing from `processCommand` before every command and
1254    /// so does this: a client that writes has to be judged at the moment it
1255    /// writes, not a batch later, or the limit is a suggestion.
1256    ///
1257    /// Three things happen in the loop and all three are needed. Eviction picks
1258    /// a key and drops it. Compaction gives the pages back, because dropping a
1259    /// key marks its record dead and returns nothing on its own, so a loop that
1260    /// only evicted would throw the whole keyspace away and watch the number
1261    /// stay where it was. The reading is taken again each time round, because
1262    /// the two of them together are the only thing that moves it.
1263    ///
1264    /// # Why running out of budget is not a no
1265    ///
1266    /// `false` means there was nothing left to evict, which is `noeviction`, or
1267    /// a `volatile` policy on a database where nothing has a deadline, or a
1268    /// keyspace that is already empty. It does not mean the server is still over
1269    /// its limit, and that difference is Redis's: `performEvictions` answers
1270    /// `EVICT_FAIL` only when it has run out of things to delete, and
1271    /// `processCommand` refuses the client on that and on nothing else. Running
1272    /// out of time part way through a job it is doing well comes back as
1273    /// `EVICT_RUNNING` and the command goes through, because a server that is
1274    /// evicting steadily and refusing every write while it does it is worse for
1275    /// the client than a little overshoot.
1276    ///
1277    /// # What the limit is worth
1278    ///
1279    /// Space comes back a segment at a time and a segment is two megabytes, so
1280    /// this holds a server to its limit give or take a segment. A `maxmemory` of
1281    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
1282    /// megabytes is asking for a precision this store does not have.
1283    pub fn make_room(&self) -> bool {
1284        let limit = self.maxmemory();
1285        if limit == 0 || self.used.load(Relaxed) as u64 <= limit {
1286            return true;
1287        }
1288        // The cached reading is a batch old and the batch may have compacted
1289        // since, so take a fresh one before throwing anything away. It is the
1290        // settled reading and not the walk, so what this costs is the handful of
1291        // collections the last batch touched and not the whole database.
1292        let mut used = self.settled_memory();
1293        self.used.store(used, Relaxed);
1294        let mut budget = EVICT_BUDGET;
1295        while used as u64 > limit {
1296            let over = used - limit as usize;
1297            if !self.relieve_step(over) {
1298                return false;
1299            }
1300            self.compact_hard_step();
1301            used = self.settled_memory();
1302            self.used.store(used, Relaxed);
1303            budget -= 1;
1304            if budget == 0 {
1305                break;
1306            }
1307        }
1308        true
1309    }
1310
1311    /// Give back `over` bytes from whichever database can, by moving values to
1312    /// the file where there is one and by throwing keys away where there is not.
1313    ///
1314    /// The two answers are the eviction inversion and which one a database gets
1315    /// is [`Server::migrates`]. Answers whether anything was given back at all,
1316    /// and `false` is what refuses the client's write.
1317    ///
1318    /// A store that will not take the bytes counts as nothing given back, so the
1319    /// write is refused rather than turned into a deletion. A disk that is
1320    /// misbehaving is a reason to stop accepting writes and it is not a reason
1321    /// to start losing data that was accepted already.
1322    ///
1323    /// Round robin from a cursor rather than always starting at database zero,
1324    /// so a server using more than one of them does not empty the first before
1325    /// touching the second. Almost every server is on database zero only, where
1326    /// this is one call that answers and fifteen that say the map is empty.
1327    fn relieve_step(&self, over: usize) -> bool {
1328        let from = self.evict_db.load(Relaxed);
1329        for turn in 0..self.slots() {
1330            let i = (from + turn) % self.slots();
1331            // An empty keyspace has nothing to move and opening a log for one
1332            // would cost a resident page window to find that out.
1333            let used = !self.slot(i).is_empty();
1334            let gave = if used && self.migrates(i) {
1335                self.attach_store(i);
1336                // Whether it made room and not whether it moved a key. A round
1337                // that demoted nothing and handed back a segment is a round
1338                // that made room, and reading only the count refuses the write
1339                // that provoked it.
1340                self.slot(i)
1341                    .relieve(over)
1342                    .is_ok_and(yo_kv::tier::Relief::made_room)
1343            } else {
1344                self.slot(i).evict_one()
1345            };
1346            if gave {
1347                self.evict_db.store((i + 1) % self.slots(), Relaxed);
1348                self.mine().mark(1u64 << self.slot_db(i));
1349                return true;
1350            }
1351        }
1352        false
1353    }
1354
1355    /// The sweep the shard loop calls, at most once a millisecond.
1356    ///
1357    /// The gate is the whole difference between this and [`Server::expire_step`].
1358    /// A maintenance slice runs on every turn of the loop and a turn is a
1359    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
1360    /// thousand times per millisecond and spend a real share of the shard on
1361    /// looking for keys that cannot have died since the last look. Nothing in a
1362    /// database changes fast enough to be worth asking about more often than the
1363    /// clock can tell the difference, and the clock here is milliseconds.
1364    ///
1365    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
1366    /// hertz, so this is not the thing that decides how promptly memory comes
1367    /// back. What it decides is that an idle server sweeps a thousand times a
1368    /// second rather than a million.
1369    pub fn expire_slice(&mut self, budget: usize) -> usize {
1370        let now = self.clock.now_ms();
1371        if now == self.expire_ms {
1372            return 0;
1373        }
1374        self.expire_ms = now;
1375        self.expire_step(budget)
1376    }
1377
1378    /// Sweep dead keys out of the databases, spending at most `budget` looks.
1379    ///
1380    /// Answers what it spent, so the caller can charge its maintenance slice for
1381    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
1382    ///
1383    /// Round robin from its own cursor, and every database gets offered whatever
1384    /// is left of the budget rather than a sixteenth of it each, so a server on
1385    /// database zero only, which is nearly every server, spends the whole slice
1386    /// where the keys are. The fifteen empty ones cost a comparison apiece
1387    /// because a database with no key carrying a deadline says so without
1388    /// drawing anything.
1389    ///
1390    /// The cursor moves to the database after whichever one did the work, so two
1391    /// busy databases take turns instead of the lower numbered one starving the
1392    /// other.
1393    pub fn expire_step(&mut self, budget: usize) -> usize {
1394        let mut spent = 0;
1395        for turn in 0..self.slots() {
1396            if spent >= budget {
1397                break;
1398            }
1399            let i = (self.expire_db + turn) % self.slots();
1400            let c = self.slot_mut(i).expire_cycle(budget - spent);
1401            spent += c.examined;
1402            if c.expired > 0 {
1403                self.expire_db = (i + 1) % self.slots();
1404                self.dirty |= 1u64 << self.slot_db(i);
1405            }
1406        }
1407        spent
1408    }
1409
1410    /// One slice of compaction for a server that is over its limit.
1411    ///
1412    /// Takes the databases in the same order [`Server::compact_step`] does and
1413    /// stops at the first one that had something to move, and it asks with the
1414    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
1415    fn compact_hard_step(&self) -> Option<usize> {
1416        let from = self.next_db.load(Relaxed);
1417        for turn in 0..self.slots() {
1418            let i = (from + turn) % self.slots();
1419            if let Some(moved) = self.slot(i).compact_hard() {
1420                self.next_db.store((i + 1) % self.slots(), Relaxed);
1421                return Some(moved);
1422            }
1423        }
1424        None
1425    }
1426
1427    /// Take what every thread has marked and add it to the turn's own mask.
1428    ///
1429    /// The mask the turn works from is its own and not a shared one, because a
1430    /// mask it read in place and then cleared a bit of would be a mask that lost
1431    /// whatever another thread marked in between. A swap cannot lose a mark: a
1432    /// thread that ors while the swap happens either gets its bit in before the
1433    /// swap or leaves it there afterwards, and the second one costs one look at
1434    /// a database the turn has already been through.
1435    fn collect_marks(&mut self) {
1436        let mut marked = 0;
1437        for thread in &self.locals {
1438            marked |= thread.dirty.swap(0, Relaxed);
1439        }
1440        self.dirty |= marked;
1441    }
1442
1443    /// Give one database's dead space back, if any database has enough of it to
1444    /// be worth the move. `None` when no database had a candidate.
1445    ///
1446    /// Once per batch, next to the clock. Overwriting a key writes a new record
1447    /// and counts the old one dead, so without this a server holds everything
1448    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
1449    /// a key against Redis at 144 for the same load, and the whole difference
1450    /// was dead records nothing ever came back for.
1451    ///
1452    /// At most one segment moves per call and the search starts one database
1453    /// further along each time, so the cost of asking is a comparison per
1454    /// database and the cost of acting is bounded by a segment.
1455    pub fn compact_step(&mut self) -> Option<usize> {
1456        self.collect_marks();
1457        let from = self.next_db.load(Relaxed);
1458        for turn in 0..self.slots() {
1459            let i = (from + turn) % self.slots();
1460            // Nothing has run against this database since it last said it had
1461            // nothing to collect, so it still has nothing to collect and the
1462            // line it lives on stays where it is.
1463            let at = self.slot_db(i);
1464            if self.dirty & (1 << at) == 0 {
1465                continue;
1466            }
1467            if let Some(moved) = self.slot_mut(i).compact_step() {
1468                self.next_db.store((i + 1) % self.slots(), Relaxed);
1469                return Some(moved);
1470            }
1471            // Only once every stripe of the database has said it has nothing,
1472            // since the bit is per database and one stripe answering for all of
1473            // them would stop the others being asked at all.
1474            if i % self.width == self.width - 1 {
1475                self.dirty &= !(1u64 << at);
1476            }
1477        }
1478        None
1479    }
1480}
1481
1482impl Default for Server {
1483    fn default() -> Server {
1484        Server::new()
1485    }
1486}
1487
1488/// What one connection has chosen.
1489pub struct Session {
1490    db: usize,
1491    id: u64,
1492    name: Vec<u8>,
1493    /// The `HIMPORT` fieldsets this connection has prepared.
1494    ///
1495    /// Connection state and not keyspace state, which is the reference's design
1496    /// and not a shortcut: a fieldset is invisible to every other connection and
1497    /// the keys built from one outlive it.
1498    sets: himport::Fieldsets,
1499}
1500
1501impl Session {
1502    /// A new connection, on database zero with no name.
1503    #[must_use]
1504    pub fn new(id: u64) -> Session {
1505        Session {
1506            db: 0,
1507            id,
1508            name: Vec::new(),
1509            sets: himport::Fieldsets::default(),
1510        }
1511    }
1512
1513    /// The connection id, which `HELLO` reports and `CLIENT` will.
1514    #[must_use]
1515    pub const fn id(&self) -> u64 {
1516        self.id
1517    }
1518
1519    /// Which database this connection is working in.
1520    #[must_use]
1521    pub const fn db(&self) -> usize {
1522        self.db
1523    }
1524
1525    /// The name the client gave itself, empty if it gave none.
1526    #[must_use]
1527    pub fn name(&self) -> &[u8] {
1528        &self.name
1529    }
1530
1531    /// Put everything back the way it was when the connection was opened.
1532    ///
1533    /// The protocol is not here because it is not here: it lives in the reply
1534    /// buffer, and `RESET` sets it back there.
1535    pub fn reset(&mut self) {
1536        self.db = 0;
1537        self.name.clear();
1538        // `SELECT` leaves these alone and `RESET` does not, both checked
1539        // against 8.10.1, which is the one pair of answers you could not guess
1540        // from what the command is for.
1541        self.sets.clear();
1542    }
1543
1544    /// Record the name from `HELLO ... SETNAME`.
1545    fn set_name(&mut self, name: &[u8]) {
1546        yo_alloc::allow(|| {
1547            self.name.clear();
1548            self.name.extend_from_slice(name);
1549        });
1550    }
1551}
1552
1553/// Run one command and write its reply.
1554///
1555/// The name is looked up and the arity is checked here, once, so that no body
1556/// has to. Everything after that is the command's own.
1557pub fn execute(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1558    // The decoder never produces a command with no name. If one ever arrives,
1559    // it is not something to answer.
1560    if args.is_empty() {
1561        return Flow::Continue;
1562    }
1563    resolved(server, session, lookup(args.name()), args, out)
1564}
1565
1566/// The same, for a caller that has already found the command.
1567///
1568/// The engine frames a command before it runs it, and between those two it also
1569/// asks which key the command touches so the record can be prefetched. That is
1570/// two more chances to look the name up, and looking it up three times to run it
1571/// once is three times the cost of the cheapest thing in the path. So the engine
1572/// resolves the name where it frames the command, carries the answer on the
1573/// framed command, and both the other two take it from there.
1574///
1575/// `spec` is `None` for a name that is not a command, which is the same thing
1576/// [`lookup`] says and lands in the same reply.
1577pub fn resolved(
1578    server: &Server,
1579    session: &mut Session,
1580    spec: Option<&'static Spec>,
1581    args: Args<'_>,
1582    out: &mut Out,
1583) -> Flow {
1584    if args.is_empty() {
1585        return Flow::Continue;
1586    }
1587    server.mine().stats.commands.bump();
1588
1589    let Some(spec) = spec else {
1590        write_error(out, &args::unknown_command(args));
1591        return Flow::Continue;
1592    };
1593    if !arity_ok(spec, args.len()) {
1594        server.mine().cmdstats.at(spec).rejected.bump();
1595        write_error(out, &args::wrong_arity(spec.name));
1596        return Flow::Continue;
1597    }
1598
1599    // The limit first, so a server with no `maxmemory`, which is the default and
1600    // is nearly all of them, pays one comparison against a field that is already
1601    // warm. Every command and not only the writes, because that is where Redis
1602    // puts it: making room is the server's job whatever the client asked for,
1603    // and the flag only decides who gets told no when there is no room to make.
1604    //
1605    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1606    // Redis's list, so a command that only frees is let through with nothing
1607    // left, which is what lets a client dig itself out with `DEL`.
1608    if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1609        server.mine().cmdstats.at(spec).rejected.bump();
1610        out.error_line(b"OOM ", OOM);
1611        return Flow::Continue;
1612    }
1613
1614    // Which databases the maintenance turn after this batch has to ask. Marked
1615    // for every command and not only for the writes, because a read can make
1616    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1617    // record it dropped is exactly the kind of thing the collector is for.
1618    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1619    // two groups that hold them mark all of them rather than the session's.
1620    server.mine().mark(match spec.group {
1621        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1622        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1623            1u64 << session.db
1624        }
1625        _ => ALL_DATABASES,
1626    });
1627
1628    let mark = out.len();
1629    // Before the group, because the five that block are list commands and would
1630    // otherwise land in `lists`, which is handed one database and nothing that
1631    // could park a client. The flag is the right thing to branch on rather than
1632    // a list of names: it is what `COMMAND INFO` reports about exactly these
1633    // commands, and the sorted set and stream ones that arrive later carry it
1634    // too.
1635    let done = if spec.flags.contains(&"blocking") {
1636        blocking::execute(server, session, spec, args, out)
1637    } else {
1638        match spec.group {
1639            "string" => {
1640                let db = session.db;
1641                strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1642            }
1643            // Its own group and its own file, and the same values underneath:
1644            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1645            // something a `SET` left behind works.
1646            "bitmap" => {
1647                let db = session.db;
1648                bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1649            }
1650            // The same again: a sketch is a string with a documented layout, so
1651            // `GET` hands one to a client and `SET` takes it back.
1652            "hyperloglog" => {
1653                let db = session.db;
1654                hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1655            }
1656            "set" => {
1657                let db = session.db;
1658                sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1659            }
1660            // The one hash command whose state is not in the keyspace. A
1661            // fieldset belongs to the connection, so this is handed the session
1662            // as well as the database, the same exception `MIGRATE` gets in the
1663            // keyspace group for the socket it keeps.
1664            "hash" if spec.name == "himport" => {
1665                let db = session.db;
1666                himport::execute(&server.dbs[db], &mut session.sets, args, out)
1667                    .map(|()| Flow::Continue)
1668            }
1669            // The one group that reaches back into the server after it has
1670            // written its reply, because a hash is what a search index is
1671            // made of. What comes back is what the indexes have to be told,
1672            // which is not the same as whether the command was a write.
1673            "hash" => {
1674                let db = session.db;
1675                let changed = hashes::execute(&server.dbs[db], spec, args, out);
1676                changed.map(|changed| {
1677                    indexing::changed(server, db, args.get(1), changed);
1678                    Flow::Continue
1679                })
1680            }
1681            "list" => {
1682                let db = session.db;
1683                lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1684            }
1685            "zset" => {
1686                let db = session.db;
1687                zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1688            }
1689            // A geo key is a sorted set and these are sorted set commands with
1690            // arithmetic on the way in and on the way out, so a client can ZREM
1691            // a place out of one and ZCARD it to count them.
1692            "geo" => {
1693                let db = session.db;
1694                geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1695            }
1696            "array" => {
1697                let db = session.db;
1698                arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1699            }
1700            "graph" => {
1701                let db = session.db;
1702                graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1703            }
1704            // A document under a key, reached by a path. The group is Redis's
1705            // module surface and the storage is ours, the same trade the vector
1706            // set group makes.
1707            "json" => {
1708                let db = session.db;
1709                json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1710            }
1711            "vector" => {
1712                let db = session.db;
1713                vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1714            }
1715            "bloom" => {
1716                let db = session.db;
1717                bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1718            }
1719            "cuckoo" => {
1720                let db = session.db;
1721                cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1722            }
1723            "cms" => {
1724                let db = session.db;
1725                cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1726            }
1727            "topk" => {
1728                let db = session.db;
1729                topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1730            }
1731            "tdigest" => {
1732                let db = session.db;
1733                tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1734            }
1735            "ts" => {
1736                let db = session.db;
1737                ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1738            }
1739            // The clock is read before the database is borrowed, because every
1740            // stream command needs the time and it lives on the server. An
1741            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1742            // `XINFO` reporting it all have to agree about what moment this is.
1743            "stream" => {
1744                let db = session.db;
1745                let now = server.now_ms();
1746                streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1747            }
1748            // The one keyspace command that needs more than the databases,
1749            // because the socket it talks down is held on the server between
1750            // commands and not opened again for each one.
1751            "keyspace" if spec.name == "migrate" => {
1752                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1753            }
1754            // Every database and not the one the session is on, because `COPY` takes
1755            // a `DB n` and writes into a database nobody selected. The other group
1756            // that reaches back into the server afterwards, and it hands back a list
1757            // rather than one answer, because `DEL a b c` is three keys and a rename
1758            // is two.
1759            "keyspace" => {
1760                let mut touched = indexing::Touched::new(server);
1761                let done =
1762                    keyspace::execute(&server.dbs, session.db, spec, args, out, &mut touched);
1763                done.map(|()| {
1764                    indexing::touched(server, &touched);
1765                    Flow::Continue
1766                })
1767            }
1768            // No database at all, because an index is not a key. The registry
1769            // is the whole of what these sixteen commands touch, and then
1770            // `FT.CREATE` hands back the name it made so the keys that
1771            // already match its prefix can be read into it. The lock goes
1772            // before the scan runs, since the scan takes it again for every
1773            // key it reads.
1774            "search" if spec.name == "FT.SEARCH" => {
1775                // The two search commands that read documents, and so the two
1776                // that need the keyspace as well as the registry. They take and
1777                // let go of the registry themselves, because they cannot hold
1778                // that and a stripe at the same time.
1779                search::find(server, session.db, args, out).map(|()| Flow::Continue)
1780            }
1781            "search" if spec.name == "FT.AGGREGATE" => {
1782                search::roll(server, session.db, args, out).map(|()| Flow::Continue)
1783            }
1784            "search" => {
1785                let db = session.db;
1786                let made = search::execute(&mut server.search.lock(), db, spec, args, out);
1787                made.map(|made| {
1788                    if let Some(name) = made {
1789                        indexing::scan(server, db, name);
1790                    }
1791                    Flow::Continue
1792                })
1793            }
1794            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1795            _ => server::execute(server, session, spec, args, out),
1796        }
1797    };
1798    let flow = match done {
1799        Ok(flow) => flow,
1800        Err(e) => {
1801            out.truncate(mark);
1802            write_error(out, &e);
1803            Flow::Continue
1804        }
1805    };
1806
1807    // Counted here and not before the call, which is where Redis counts it, so
1808    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1809    // same way theirs does.
1810    //
1811    // Failure is read off the reply rather than off the `Result`, because the
1812    // two are not the same set. A command that ran out of arguments comes back
1813    // as an `Err` and a command that was sent the wrong password writes its own
1814    // error line and comes back `Ok`, and both of those are a call that failed.
1815    // The first byte at the mark is what a client would branch on, and it is `-`
1816    // for an error on either protocol and `!` for RESP3's long form.
1817    let row = server.mine().cmdstats.at(spec);
1818    row.calls.bump();
1819    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1820        row.failed.bump();
1821    }
1822    flow
1823}
1824
1825/// The error line for an error value.
1826///
1827/// The prefix is what a client branches on, and there are three of them:
1828/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1829/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1830/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1831/// than routed through here. `OOM` is not a [`Code`] of its own because
1832/// [`Code::Full`] already covers the string that is too long for
1833/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1834fn write_error(out: &mut Out, e: &Error) {
1835    let prefix: &[u8] = match e.code() {
1836        Code::WrongType => b"WRONGTYPE ",
1837        // Only the HyperLogLog commands answer this one, and the prefix is the
1838        // sentence a client branches on to tell a sketch it cannot read from a
1839        // sketch it sent wrong.
1840        Code::Corrupt => b"INVALIDOBJ ",
1841        _ => b"ERR ",
1842    };
1843    out.error_line(prefix, e.message().as_bytes());
1844}
1845
1846#[cfg(test)]
1847mod tests {
1848    use super::*;
1849    use crate::proto::{Limits, Proto};
1850    use crate::request::Argv;
1851
1852    /// Build the wire bytes for a command.
1853    ///
1854    /// Tests go through the codec rather than around it, so an argument in a
1855    /// test is the same borrowed slice a connection produces.
1856    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1857        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1858        for p in parts {
1859            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1860            wire.extend_from_slice(p);
1861            wire.extend_from_slice(b"\r\n");
1862        }
1863        wire
1864    }
1865
1866    /// A server, a connection and a buffer, driven the way the reactor will.
1867    struct Fixture {
1868        server: Server,
1869        session: Session,
1870        argv: Argv,
1871        out: Out,
1872    }
1873
1874    impl Fixture {
1875        fn new() -> Fixture {
1876            Fixture::on(Server::new())
1877        }
1878
1879        /// The same, on a server whose databases are cut into `width` stripes.
1880        fn striped(width: usize) -> Fixture {
1881            Fixture::on(Server::with_width(width))
1882        }
1883
1884        fn on(server: Server) -> Fixture {
1885            Fixture {
1886                server,
1887                session: Session::new(7),
1888                argv: Argv::new(),
1889                out: Out::new(Proto::Resp2),
1890            }
1891        }
1892
1893        /// Run one command and answer with the bytes it wrote.
1894        fn run(&mut self, parts: &[&[u8]]) -> String {
1895            self.flow(parts).1
1896        }
1897
1898        /// Run one command and answer with the bytes exactly as written.
1899        ///
1900        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
1901        /// every reply that is text and destroys a `DUMP` payload, since a
1902        /// payload is arbitrary bytes and a checksum on the end of them.
1903        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1904            let wire = encode(parts);
1905            self.argv.decode(&wire, &Limits::default()).unwrap();
1906            self.out.clear();
1907            execute(
1908                &self.server,
1909                &mut self.session,
1910                Args::new(&self.argv, &wire),
1911                &mut self.out,
1912            );
1913            self.out.as_slice().to_vec()
1914        }
1915
1916        /// Move every clock in the server on by `ms`.
1917        fn advance(&mut self, ms: u64) {
1918            self.server.advance_clock_ms(ms);
1919        }
1920
1921        /// The same, with what the connection should do next.
1922        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1923            let wire = encode(parts);
1924            self.argv.decode(&wire, &Limits::default()).unwrap();
1925            self.out.clear();
1926            let flow = execute(
1927                &self.server,
1928                &mut self.session,
1929                Args::new(&self.argv, &wire),
1930                &mut self.out,
1931            );
1932            (
1933                flow,
1934                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1935            )
1936        }
1937    }
1938
1939    /// What a client does all day: write the same keys again and again. Every
1940    /// one of those writes leaves the previous record behind, so a server that
1941    /// never compacts holds every version of every key it has ever been sent.
1942    #[test]
1943    fn rewriting_the_same_keys_does_not_grow_the_server() {
1944        let mut f = Fixture::new();
1945        let val = vec![b'v'; 1024];
1946        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1947
1948        for k in &keys {
1949            f.run(&[b"SET", k, &val]);
1950        }
1951        f.server.compact_step();
1952        let after_first = f.server.memory_bytes();
1953
1954        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
1955        // of it. Thirty two megabytes written to hold sixty four kilobytes,
1956        // which is the shape of a real workload and is enough churn to fill
1957        // sixteen segments if nothing ever comes back.
1958        for _ in 0..500 {
1959            for k in &keys {
1960                f.run(&[b"SET", k, &val]);
1961            }
1962            f.server.compact_step();
1963        }
1964
1965        assert!(
1966            f.server.memory_bytes() <= after_first * 2,
1967            "held {} after five hundred passes against {after_first} after one",
1968            f.server.memory_bytes()
1969        );
1970        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1971        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1972    }
1973
1974    /// The same churn on a database nobody starts on, either side of a quiet
1975    /// spell long enough for the maintenance turn to stop asking about it.
1976    ///
1977    /// The turn after each batch skips a database that has already said it has
1978    /// nothing to collect and has not been touched since, which is what keeps a
1979    /// server whose clients are all on database zero from loading and storing
1980    /// in the other fifteen every batch to be told no. Two things could go
1981    /// wrong with that. A database might never be marked at all, so this uses
1982    /// database nine, which nothing marks by accident. And a database whose
1983    /// mark was cleared might never get it back, so this drains the collector
1984    /// until it says there is nothing left, checks the mark really is gone, and
1985    /// then writes another thirty two megabytes through the same sixty four
1986    /// keys. If either went wrong the server would hold all of it.
1987    #[test]
1988    fn a_database_nobody_started_on_is_still_collected() {
1989        let mut f = Fixture::new();
1990        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1991        let val = vec![b'v'; 1024];
1992        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1993
1994        for k in &keys {
1995            f.run(&[b"SET", k, &val]);
1996        }
1997        while f.server.compact_step().is_some() {}
1998        assert_eq!(
1999            f.server.dirty & (1 << 9),
2000            0,
2001            "database nine was drained and should not be asked again until it is written to"
2002        );
2003        let after_first = f.server.memory_bytes();
2004
2005        for _ in 0..500 {
2006            for k in &keys {
2007                f.run(&[b"SET", k, &val]);
2008            }
2009            f.server.compact_step();
2010        }
2011
2012        assert!(
2013            f.server.memory_bytes() <= after_first * 2,
2014            "held {} after five hundred passes against {after_first} after one",
2015            f.server.memory_bytes()
2016        );
2017        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
2018        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
2019        // And nothing landed anywhere else on the way.
2020        f.run(&[b"SELECT", b"0"]);
2021        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2022    }
2023
2024    #[test]
2025    fn a_command_goes_from_bytes_to_bytes() {
2026        let mut f = Fixture::new();
2027        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2028        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
2029        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2030        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
2031        // The name is matched whatever case it came in, and so are the options.
2032        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
2033        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
2034    }
2035
2036    #[test]
2037    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
2038        let mut f = Fixture::new();
2039        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
2040        // A key named twice exists twice and can only be deleted once, and both
2041        // of those are Redis's answers rather than tidier ones.
2042        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
2043        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
2044        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2045        // UNLINK is the same body and reports the same way.
2046        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
2047        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2048    }
2049
2050    #[test]
2051    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
2052        let mut f = Fixture::new();
2053        f.run(&[b"SET", b"k", b"v"]);
2054        // A simple string on both protocols, which is unusual: most replies
2055        // that carry a word are bulk strings.
2056        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
2057        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
2058    }
2059
2060    #[test]
2061    fn touch_counts_the_way_exists_counts() {
2062        let mut f = Fixture::new();
2063        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2064        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
2065        assert_eq!(
2066            f.run(&[b"TOUCH", b"a", b"a"]),
2067            ":2\r\n",
2068            "twice counts twice"
2069        );
2070        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
2071        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
2072    }
2073
2074    #[test]
2075    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
2076        let mut f = Fixture::new();
2077        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2078        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
2079
2080        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
2081        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2082        assert_eq!(
2083            f.run(&[b"TTL", b"b"]),
2084            ":100\r\n",
2085            "the source's and not b's"
2086        );
2087        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2088    }
2089
2090    #[test]
2091    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
2092        let mut f = Fixture::new();
2093        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
2094        // The source is checked before the destination, so this is the error
2095        // and not the zero RENAMENX would otherwise answer for a taken name.
2096        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
2097    }
2098
2099    #[test]
2100    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
2101        let mut f = Fixture::new();
2102        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
2103
2104        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
2105        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2106        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
2107        // one call the two disagree about and neither does any work for.
2108        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
2109        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
2110        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
2111        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
2112    }
2113
2114    #[test]
2115    fn renaming_a_set_does_not_touch_a_member() {
2116        let mut f = Fixture::new();
2117        for i in 0..300 {
2118            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
2119        }
2120        let before = f.server.memory_bytes();
2121
2122        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
2123        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
2124        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
2125        assert!(
2126            f.server.memory_bytes().abs_diff(before) < 256,
2127            "the members were copied: {} against {before}",
2128            f.server.memory_bytes()
2129        );
2130    }
2131
2132    #[test]
2133    fn a_copy_is_a_second_value_and_not_a_second_name() {
2134        let mut f = Fixture::new();
2135        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
2136
2137        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
2138        f.run(&[b"SADD", b"t", b"m3"]);
2139        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
2140        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
2141    }
2142
2143    /// Every type a key can hold, copied, because two of them used to panic.
2144    ///
2145    /// `COPY` reads the value out of the source through one match on the type
2146    /// tag, and that match had a catch all at the bottom from back when a set
2147    /// and a hash were the only bodies. The list and the sorted set landed after
2148    /// it and nobody came back, so `COPY mylist other` took the shard down. It
2149    /// is an ordinary command against a type the server supports everywhere
2150    /// else, so this walks all five rather than the two that were broken: the
2151    /// point is that the next type cannot land the same way.
2152    #[test]
2153    fn every_type_can_be_copied() {
2154        let mut f = Fixture::new();
2155        f.run(&[b"SET", b"str", b"v1"]);
2156        f.run(&[b"SADD", b"set", b"m1"]);
2157        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2158        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
2159        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
2160
2161        for name in [
2162            &b"str"[..],
2163            &b"set"[..],
2164            &b"hash"[..],
2165            &b"list"[..],
2166            &b"zset"[..],
2167        ] {
2168            let dst = [name, b":copy"].concat();
2169            assert_eq!(
2170                f.run(&[b"COPY", name, &dst]),
2171                ":1\r\n",
2172                "copying {}",
2173                String::from_utf8_lossy(name)
2174            );
2175            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
2176        }
2177
2178        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
2179            let mut want = String::from("*2\r\n");
2180            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
2181            want
2182        });
2183        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
2184
2185        // And the copy is its own value, not a second name for the source.
2186        f.run(&[b"RPUSH", b"list:copy", b"c"]);
2187        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
2188        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
2189    }
2190
2191    #[test]
2192    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
2193        let mut f = Fixture::new();
2194        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2195        f.run(&[b"SET", b"b", b"v2"]);
2196
2197        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
2198        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2199        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
2200        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2201        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
2202        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
2203    }
2204
2205    #[test]
2206    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
2207        let mut f = Fixture::new();
2208        f.run(&[b"SET", b"a", b"v1"]);
2209
2210        // Same key, different database, so this is not the same object and is
2211        // an ordinary copy. Same key in the same database is the error below.
2212        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
2213        f.run(&[b"SELECT", b"1"]);
2214        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
2215        assert_eq!(
2216            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
2217            ":0\r\n",
2218            "taken"
2219        );
2220        assert_eq!(
2221            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
2222            ":1\r\n"
2223        );
2224    }
2225
2226    #[test]
2227    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
2228        let mut f = Fixture::new();
2229        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2230        assert_eq!(
2231            f.run(&[b"SORT", b"l"]),
2232            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2233        );
2234        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
2235        assert_eq!(
2236            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
2237            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2238        );
2239        assert_eq!(
2240            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
2241            "*1\r\n$1\r\n2\r\n"
2242        );
2243    }
2244
2245    #[test]
2246    fn sort_reads_a_key_per_element_for_by_and_for_get() {
2247        let mut f = Fixture::new();
2248        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
2249        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
2250        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
2251        // misses, which is a nil in the middle of the array and not a short one.
2252        assert_eq!(
2253            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
2254            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
2255        );
2256    }
2257
2258    #[test]
2259    fn sort_store_writes_a_list_and_answers_its_length() {
2260        let mut f = Fixture::new();
2261        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2262        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
2263        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
2264        assert_eq!(
2265            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
2266            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2267        );
2268        // An empty result takes the destination with it rather than leaving a
2269        // list that holds nothing.
2270        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
2271        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
2272    }
2273
2274    #[test]
2275    fn sort_ro_does_not_know_the_word_store() {
2276        let mut f = Fixture::new();
2277        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
2278        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
2279        assert_eq!(
2280            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
2281            "-ERR syntax error\r\n"
2282        );
2283        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2284    }
2285
2286    #[test]
2287    fn sort_refuses_what_it_cannot_sort() {
2288        let mut f = Fixture::new();
2289        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
2290        f.run(&[b"SET", b"s", b"x"]);
2291        assert_eq!(
2292            f.run(&[b"SORT", b"s"]),
2293            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
2294        );
2295        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
2296        assert_eq!(
2297            f.run(&[b"SORT", b"words"]),
2298            "-ERR One or more scores can't be converted into double\r\n"
2299        );
2300        assert_eq!(
2301            f.run(&[b"SORT", b"words", b"ALPHA"]),
2302            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
2303        );
2304        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
2305    }
2306
2307    #[test]
2308    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
2309        let mut f = Fixture::new();
2310        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
2311        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
2312        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2313        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2314        assert_eq!(
2315            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
2316            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2317        );
2318        // And back, which proves the body survived the trip rather than being
2319        // rebuilt from a copy that happened to look the same.
2320        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
2321        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2322    }
2323
2324    #[test]
2325    fn move_answers_zero_when_either_end_says_no() {
2326        let mut f = Fixture::new();
2327        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
2328        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
2329        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2330        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
2331        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2332        // The destination is taken, so nothing moves and the source is still
2333        // there with what it had.
2334        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
2335        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
2336        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2337        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
2338    }
2339
2340    #[test]
2341    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
2342        let mut f = Fixture::new();
2343        assert_eq!(
2344            f.run(&[b"MOVE", b"a", b"0"]),
2345            "-ERR source and destination objects are the same\r\n"
2346        );
2347        assert_eq!(
2348            f.run(&[b"MOVE", b"a", b"99"]),
2349            "-ERR DB index is out of range\r\n"
2350        );
2351        assert_eq!(
2352            f.run(&[b"MOVE", b"a", b"-1"]),
2353            "-ERR DB index is out of range\r\n"
2354        );
2355        assert_eq!(
2356            f.run(&[b"MOVE", b"a", b"x"]),
2357            "-ERR value is not an integer or out of range\r\n"
2358        );
2359    }
2360
2361    #[test]
2362    fn swapdb_swaps_what_two_connections_would_see() {
2363        let mut f = Fixture::new();
2364        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
2365        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2366        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
2367        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2368
2369        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2370        // Still on database zero, and database zero is a different database.
2371        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
2372        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2373        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2374        // A database swapped with itself is fine and changes nothing.
2375        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
2376        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2377    }
2378
2379    /// The swap is stripe by stripe, so a database cut into more than one
2380    /// stripe is the case that would catch it exchanging some of the keys and
2381    /// leaving the rest. Sixteen keys over four stripes is enough that every
2382    /// stripe has something in it whatever the hashes come out as.
2383    #[test]
2384    fn swapdb_swaps_every_stripe_of_a_wide_database() {
2385        let mut f = Fixture::striped(4);
2386        for i in 0..16u32 {
2387            let key = format!("k{i}");
2388            assert_eq!(f.run(&[b"SET", key.as_bytes(), b"zero"]), "+OK\r\n");
2389        }
2390        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2391        assert_eq!(f.run(&[b"SET", b"only", b"one"]), "+OK\r\n");
2392        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2393
2394        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2395        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2396        assert_eq!(f.run(&[b"GET", b"only"]), "$3\r\none\r\n");
2397        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2398        assert_eq!(f.run(&[b"DBSIZE"]), ":16\r\n");
2399        for i in 0..16u32 {
2400            let key = format!("k{i}");
2401            assert_eq!(f.run(&[b"GET", key.as_bytes()]), "$4\r\nzero\r\n");
2402        }
2403    }
2404
2405    #[test]
2406    fn swapdb_says_which_index_it_could_not_read() {
2407        let mut f = Fixture::new();
2408        assert_eq!(
2409            f.run(&[b"SWAPDB", b"x", b"1"]),
2410            "-ERR invalid first DB index\r\n"
2411        );
2412        assert_eq!(
2413            f.run(&[b"SWAPDB", b"0", b"y"]),
2414            "-ERR invalid second DB index\r\n"
2415        );
2416        // A number too big to be an index on a server that keeps one in an int
2417        // is the same complaint, and a plausible one that is not ours is the
2418        // range complaint instead. The split is Redis's.
2419        assert_eq!(
2420            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2421            "-ERR invalid first DB index\r\n"
2422        );
2423        assert_eq!(
2424            f.run(&[b"SWAPDB", b"0", b"99"]),
2425            "-ERR DB index is out of range\r\n"
2426        );
2427        assert_eq!(
2428            f.run(&[b"SWAPDB", b"-1", b"0"]),
2429            "-ERR DB index is out of range\r\n"
2430        );
2431    }
2432
2433    #[test]
2434    fn wait_answers_zero_replicas_without_waiting() {
2435        let mut f = Fixture::new();
2436        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2437        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2438        // A replica that is never going to arrive, and a timeout that would be
2439        // a real wait on a server that had one.
2440        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2441        // Negative replicas is not an error, because zero is already more than
2442        // it asked for.
2443        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2444        assert_eq!(
2445            f.run(&[b"WAIT", b"x", b"0"]),
2446            "-ERR value is not an integer or out of range\r\n"
2447        );
2448        assert_eq!(
2449            f.run(&[b"WAIT", b"0", b"-1"]),
2450            "-ERR timeout is negative\r\n"
2451        );
2452        assert_eq!(
2453            f.run(&[b"WAIT", b"0", b"1.5"]),
2454            "-ERR timeout is not an integer or out of range\r\n"
2455        );
2456    }
2457
2458    #[test]
2459    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2460        let mut f = Fixture::new();
2461        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2462        assert_eq!(
2463            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2464            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2465        );
2466        assert_eq!(
2467            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2468            "-ERR value is out of range, value must between 0 and 1\r\n"
2469        );
2470        assert_eq!(
2471            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2472            "-ERR value is out of range, must be positive\r\n"
2473        );
2474        // The arguments are all read before the server looks at itself, so a
2475        // bad timeout beats the append only complaint even with numlocal set.
2476        assert_eq!(
2477            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2478            "-ERR timeout is negative\r\n"
2479        );
2480    }
2481
2482    /// The bytes inside a bulk reply, with the header and the trailing break
2483    /// taken off. Every `DUMP` test needs this and none of them care how the
2484    /// length was written.
2485    fn payload(reply: &[u8]) -> Vec<u8> {
2486        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2487        reply[head + 2..reply.len() - 2].to_vec()
2488    }
2489
2490    #[test]
2491    fn a_value_survives_a_dump_and_a_restore() {
2492        let mut f = Fixture::new();
2493        f.run(&[b"SET", b"s", b"hello"]);
2494        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2495        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2496        f.run(&[b"SADD", b"u", b"x", b"y"]);
2497        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2498        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2499
2500        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2501            let mut copy = key.to_vec();
2502            copy.push(b'2');
2503            let bytes = payload(&f.raw(&[b"DUMP", key]));
2504            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
2505            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
2506        }
2507
2508        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2509        assert_eq!(
2510            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2511            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2512        );
2513        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2514        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2515        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2516        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2517        // The encoding survives too, since the payload names the plainest legal
2518        // type and the loader puts the value back on the rung it belongs on.
2519        assert_eq!(
2520            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2521            f.run(&[b"OBJECT", b"ENCODING", b"t"])
2522        );
2523    }
2524
2525    #[test]
2526    fn a_dumped_hash_keeps_its_field_deadlines() {
2527        let mut f = Fixture::new();
2528        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2529        assert_eq!(
2530            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2531            "*1\r\n:1\r\n"
2532        );
2533        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2534        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2535        assert_eq!(
2536            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2537            "*2\r\n:-1\r\n:100\r\n"
2538        );
2539    }
2540
2541    #[test]
2542    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2543        let mut f = Fixture::new();
2544        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2545        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2546        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2547        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2548        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2549        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2550        // An absolute deadline that has already gone is not an error. The key is
2551        // not created and the reply is the same OK a live one gets.
2552        assert_eq!(
2553            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2554            "+OK\r\n"
2555        );
2556        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2557    }
2558
2559    #[test]
2560    fn dump_answers_nothing_for_a_key_that_is_not_there() {
2561        let mut f = Fixture::new();
2562        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2563        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2564        f.advance(50);
2565        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2566    }
2567
2568    #[test]
2569    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2570        let mut f = Fixture::new();
2571        f.run(&[b"SET", b"a", b"first"]);
2572        f.run(&[b"SET", b"b", b"second"]);
2573        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2574        assert_eq!(
2575            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2576            "-BUSYKEY Target key name already exists.\r\n"
2577        );
2578        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2579        assert_eq!(
2580            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2581            "+OK\r\n"
2582        );
2583        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2584    }
2585
2586    /// The busy key comes before the payload, which is not the order the
2587    /// arguments read in. Whether a key is taken should not depend on whether
2588    /// the bytes behind it happened to be good.
2589    #[test]
2590    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2591        let mut f = Fixture::new();
2592        f.run(&[b"SET", b"a", b"v"]);
2593        assert_eq!(
2594            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2595            "-BUSYKEY Target key name already exists.\r\n"
2596        );
2597        // And the options come before even that, so a bad FREQ beats the busy
2598        // key the same way a bad DB beats a missing source in COPY.
2599        assert_eq!(
2600            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2601            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2602        );
2603    }
2604
2605    #[test]
2606    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2607        let mut f = Fixture::new();
2608        f.run(&[b"SET", b"a", b"hello"]);
2609        let good = payload(&f.raw(&[b"DUMP", b"a"]));
2610
2611        let mut flipped = good.clone();
2612        flipped[2] ^= 0x40;
2613        assert_eq!(
2614            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2615            "-ERR DUMP payload version or checksum are wrong\r\n"
2616        );
2617        assert_eq!(
2618            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2619            "-ERR DUMP payload version or checksum are wrong\r\n"
2620        );
2621        // A footer that is right over a body that is not. The type byte says
2622        // string and there is nothing behind it, so the checksum agrees and the
2623        // value does not exist.
2624        let mut truncated = good[..1].to_vec();
2625        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2626        let crc = yo_common::crc::crc64(0, &truncated);
2627        truncated.extend_from_slice(&crc.to_le_bytes());
2628        assert_eq!(
2629            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2630            "-ERR Bad data format\r\n"
2631        );
2632        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2633    }
2634
2635    #[test]
2636    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2637        let mut f = Fixture::new();
2638        f.run(&[b"SET", b"a", b"v"]);
2639        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2640        assert_eq!(
2641            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2642            "-ERR Invalid TTL value, must be >= 0\r\n"
2643        );
2644        assert_eq!(
2645            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2646            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2647        );
2648        assert_eq!(
2649            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2650            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2651        );
2652        // Both are accepted and both are then dropped, which is D-26.
2653        assert_eq!(
2654            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2655            "+OK\r\n"
2656        );
2657        assert_eq!(
2658            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2659            "+OK\r\n"
2660        );
2661    }
2662
2663    /// Neither word is refused for being the wrong one. Each is only accepted
2664    /// while the other is unset, so the second of the two falls through to the
2665    /// plain syntax error rather than getting a message of its own.
2666    #[test]
2667    fn restore_takes_idletime_or_freq_and_not_both() {
2668        let mut f = Fixture::new();
2669        f.run(&[b"SET", b"a", b"v"]);
2670        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2671        assert_eq!(
2672            f.run(&[
2673                b"RESTORE",
2674                b"b",
2675                b"0",
2676                &bytes,
2677                b"IDLETIME",
2678                b"1",
2679                b"FREQ",
2680                b"2"
2681            ]),
2682            "-ERR syntax error\r\n"
2683        );
2684        assert_eq!(
2685            f.run(&[
2686                b"RESTORE",
2687                b"b",
2688                b"0",
2689                &bytes,
2690                b"FREQ",
2691                b"2",
2692                b"IDLETIME",
2693                b"1"
2694            ]),
2695            "-ERR syntax error\r\n"
2696        );
2697        assert_eq!(
2698            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2699            "-ERR syntax error\r\n"
2700        );
2701        assert_eq!(
2702            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2703            "-ERR syntax error\r\n"
2704        );
2705    }
2706
2707    #[test]
2708    fn copy_checks_its_options_before_it_looks_for_anything() {
2709        let mut f = Fixture::new();
2710        // No key exists at all, and every one of these is still the option
2711        // complaint rather than a zero, which is the order a real server uses.
2712        assert_eq!(
2713            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2714            "-ERR DB index is out of range\r\n"
2715        );
2716        assert_eq!(
2717            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2718            "-ERR DB index is out of range\r\n"
2719        );
2720        assert_eq!(
2721            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2722            "-ERR value is not an integer or out of range\r\n"
2723        );
2724        assert_eq!(
2725            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2726            "-ERR syntax error\r\n"
2727        );
2728        assert_eq!(
2729            f.run(&[b"COPY", b"a", b"a"]),
2730            "-ERR source and destination objects are the same\r\n"
2731        );
2732        // Repeated, reordered and lowercased, and the last DB wins.
2733        assert_eq!(
2734            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2735            ":0\r\n"
2736        );
2737    }
2738
2739    #[test]
2740    fn time_is_two_bulk_strings_and_moves() {
2741        let mut f = Fixture::new();
2742        let first = f.run(&[b"TIME"]);
2743        assert!(first.starts_with("*2\r\n$"), "got {first}");
2744        let parts: Vec<&str> = first.split("\r\n").collect();
2745        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2746        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2747        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2748        assert!((0..1_000_000).contains(&micros), "got {micros}");
2749        // The coarse clock the keyspace uses is a cached millisecond that a
2750        // background tick refreshes, so a TIME built on it would answer the
2751        // same microsecond twice in a row here.
2752        assert_ne!(first, f.run(&[b"TIME"]));
2753    }
2754
2755    #[test]
2756    fn a_keyspace_scan_walks_every_key_once() {
2757        let mut f = Fixture::new();
2758        for i in 0..500 {
2759            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2760        }
2761
2762        let mut seen: Vec<String> = Vec::new();
2763        let mut cursor = "0".to_owned();
2764        let mut calls = 0;
2765        loop {
2766            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2767            seen.extend(keys);
2768            cursor = next;
2769            calls += 1;
2770            assert!(calls < 10_000, "the cursor is not advancing");
2771            if cursor == "0" {
2772                break;
2773            }
2774        }
2775
2776        seen.sort();
2777        seen.dedup();
2778        assert_eq!(seen.len(), 500, "every key once and only once");
2779        // And more than one call to get them, or the COUNT is being ignored and
2780        // the loop above proved nothing about resuming.
2781        assert!(calls > 1, "500 keys came back in one batch");
2782    }
2783
2784    #[test]
2785    fn a_scan_narrows_by_pattern_and_by_type() {
2786        let mut f = Fixture::new();
2787        f.run(&[b"SET", b"str", b"v"]);
2788        f.run(&[b"SADD", b"members", b"a"]);
2789        f.run(&[b"HSET", b"fields", b"f", b"v"]);
2790
2791        let all = |f: &mut Fixture, args: &[&[u8]]| {
2792            let mut out: Vec<String> = Vec::new();
2793            let mut cursor = "0".to_owned();
2794            loop {
2795                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2796                line.extend_from_slice(args);
2797                let (next, keys) = scan_reply(&f.run(&line));
2798                out.extend(keys);
2799                cursor = next;
2800                if cursor == "0" {
2801                    break;
2802                }
2803            }
2804            out.sort();
2805            out
2806        };
2807
2808        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2809        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2810        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2811        // Case insensitive, the same as Redis's own comparison.
2812        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2813        // A type nothing can hold is not an error, it just matches nothing.
2814        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2815        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2816        // Both filters at once, and they are an and rather than an or.
2817        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2818    }
2819
2820    #[test]
2821    fn a_scan_says_what_is_wrong_with_it() {
2822        let mut f = Fixture::new();
2823        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2824        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2825        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2826        assert_eq!(
2827            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2828            "-ERR syntax error\r\n"
2829        );
2830        assert_eq!(
2831            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2832            "-ERR value is not an integer or out of range\r\n"
2833        );
2834        assert_eq!(
2835            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2836            "-ERR syntax error\r\n"
2837        );
2838        // A cursor the client made up is a cursor. It resumes somewhere
2839        // arbitrary and answers whatever is there, which is what Redis does and
2840        // is the only behaviour that does not need the server to remember every
2841        // cursor it has handed out.
2842        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2843    }
2844
2845    #[test]
2846    fn keys_and_randomkey_look_at_the_whole_database() {
2847        let mut f = Fixture::new();
2848        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2849        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2850
2851        for name in ["one", "two", "three"] {
2852            f.run(&[b"SET", name.as_bytes(), b"v"]);
2853        }
2854        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2855        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2856        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2857
2858        for _ in 0..50 {
2859            let got = f.run(&[b"RANDOMKEY"]);
2860            assert!(
2861                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2862                "got {got}"
2863            );
2864        }
2865    }
2866
2867    #[test]
2868    fn a_walk_does_not_answer_keys_that_have_expired() {
2869        let mut f = Fixture::new();
2870        f.run(&[b"SET", b"alive", b"v"]);
2871        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2872        f.server.advance_clock_ms(2);
2873        assert_eq!(
2874            f.run(&[b"DBSIZE"]),
2875            ":2\r\n",
2876            "nothing has collected it yet"
2877        );
2878
2879        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2880        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2881        assert_eq!(keys, ["alive"]);
2882        for _ in 0..20 {
2883            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2884        }
2885        // The walk collected it on the way past, which is what makes DBSIZE
2886        // here answer what Redis answers once its own cycle has been round.
2887        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2888    }
2889
2890    #[test]
2891    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2892        let mut f = Fixture::new();
2893        f.run(&[b"SET", b"k", b"v"]);
2894        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2895        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2896
2897        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2898        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2899        let ms = int(&f.run(&[b"PTTL", b"k"]));
2900        assert!((99_000..=100_000).contains(&ms), "got {ms}");
2901
2902        // The absolute pair, derived from the same one number the store kept.
2903        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2904        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2905        assert_eq!(at, (at_ms + 500) / 1000);
2906        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2907
2908        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2909        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2910        assert_eq!(
2911            f.run(&[b"PERSIST", b"k"]),
2912            ":0\r\n",
2913            "nothing to take off the second time"
2914        );
2915        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2916        assert_eq!(
2917            f.run(&[b"GET", b"k"]),
2918            "$1\r\nv\r\n",
2919            "and the value went through all of that untouched"
2920        );
2921    }
2922
2923    #[test]
2924    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2925        let mut f = Fixture::new();
2926        f.run(&[b"SET", b"str", b"v"]);
2927        f.run(&[b"SADD", b"set", b"a", b"b"]);
2928        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2929
2930        for key in [b"str".as_slice(), b"set", b"hash"] {
2931            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2932            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2933        }
2934        // The body is not touched by any of that, which is the whole reason the
2935        // deadline lives in the record and the body lives somewhere else.
2936        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2937        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2938        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2939    }
2940
2941    #[test]
2942    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2943        let mut f = Fixture::new();
2944        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2945            f.run(&[b"SET", key, b"v"]);
2946        }
2947        // Four ways of naming a moment that has passed, and all four are a
2948        // delete answering 1 rather than an error. Zero is a moment, minus one
2949        // is a moment, and the hash field commands refuse the negative one.
2950        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2951        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2952        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2953        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2954        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2955        assert_eq!(
2956            f.run(&[b"EXPIRE", b"a", b"100"]),
2957            ":0\r\n",
2958            "and the key really went, so there is nothing to put a deadline on"
2959        );
2960    }
2961
2962    #[test]
2963    fn the_four_conditions_decide_whether_the_deadline_moves() {
2964        let mut f = Fixture::new();
2965        f.run(&[b"SET", b"k", b"v"]);
2966
2967        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2968        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2969        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2970        assert_eq!(
2971            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2972            ":1\r\n",
2973            "no deadline reads as infinitely far away, so LT passes where GT fails"
2974        );
2975
2976        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2977        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2978        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2979        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2980        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2981        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2982
2983        // The condition is answered before the past check, so this is a 0 and
2984        // the key survives. The other order would delete it.
2985        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2986        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2987        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2988        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2989    }
2990
2991    #[test]
2992    fn the_conditions_are_a_set_and_not_a_keyword() {
2993        let mut f = Fixture::new();
2994        f.run(&[b"SET", b"k", b"v"]);
2995
2996        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2997        assert_eq!(
2998            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2999            ":0\r\n",
3000            "the same keyword twice means it once, and NX now has a deadline to fail on"
3001        );
3002
3003        // XX with LT is the one pair that is not either of them on its own: LT
3004        // alone would accept a key with no deadline and this does not.
3005        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
3006        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
3007        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
3008        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
3009        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
3010        f.run(&[b"PERSIST", b"k"]);
3011        assert_eq!(
3012            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
3013            ":0\r\n",
3014            "where LT on its own would have taken it"
3015        );
3016        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
3017    }
3018
3019    #[test]
3020    fn a_key_is_gone_once_its_moment_passes() {
3021        let mut f = Fixture::new();
3022        f.run(&[b"SET", b"k", b"v"]);
3023        f.run(&[b"EXPIRE", b"k", b"100"]);
3024
3025        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
3026        f.server.set_clock_ms(at as u64 + 1);
3027        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3028        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
3029        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
3030        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3031    }
3032
3033    #[test]
3034    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
3035        let mut f = Fixture::new();
3036        f.run(&[b"SET", b"k", b"v"]);
3037        for (bad, want) in [
3038            (
3039                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
3040                "-ERR value is not an integer or out of range\r\n",
3041            ),
3042            (
3043                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
3044                "-ERR Unsupported option MAYBE\r\n",
3045            ),
3046            (
3047                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
3048                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
3049            ),
3050            (
3051                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
3052                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
3053            ),
3054            (
3055                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
3056                "-ERR GT and LT options at the same time are not compatible\r\n",
3057            ),
3058            // Seconds that overflow when multiplied into milliseconds. Every
3059            // message names the command it came from.
3060            (
3061                &[b"EXPIRE", b"k", b"9223372036854775807"],
3062                "-ERR invalid expire time in 'expire' command\r\n",
3063            ),
3064            (
3065                &[b"EXPIREAT", b"k", b"9223372036854775807"],
3066                "-ERR invalid expire time in 'expireat' command\r\n",
3067            ),
3068            (
3069                &[b"PEXPIRE", b"k", b"9223372036854775807"],
3070                "-ERR invalid expire time in 'pexpire' command\r\n",
3071            ),
3072        ] {
3073            assert_eq!(f.run(bad), want, "for {bad:?}");
3074        }
3075        assert_eq!(
3076            f.run(&[b"TTL", b"k"]),
3077            ":-1\r\n",
3078            "and none of those put a deadline on anything"
3079        );
3080
3081        // The one of the four that has no arithmetic to overflow. Redis takes
3082        // it and holds the number as given, and a record here holds forty six
3083        // bits, so it lands in the year 4199 instead. D-17.
3084        assert_eq!(
3085            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
3086            ":1\r\n"
3087        );
3088        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
3089    }
3090
3091    #[test]
3092    fn flushing_empties_this_database_or_every_one_of_them() {
3093        let mut f = Fixture::new();
3094        f.run(&[b"SELECT", b"0"]);
3095        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3096        f.run(&[b"SELECT", b"1"]);
3097        f.run(&[b"SET", b"c", b"3"]);
3098        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3099        // ASYNC and SYNC are both taken and neither changes anything, since the
3100        // keyspace is empty before the OK goes out either way.
3101        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
3102        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3103        // Only database one was emptied.
3104        f.run(&[b"SELECT", b"0"]);
3105        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
3106        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
3107        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3108        f.run(&[b"SELECT", b"1"]);
3109        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3110        // Anything else after the name is a syntax error, and so is a third
3111        // argument even when the second one is a word we take.
3112        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
3113        assert_eq!(
3114            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
3115            "-ERR syntax error\r\n"
3116        );
3117    }
3118
3119    #[test]
3120    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
3121        let mut f = Fixture::new();
3122        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
3123        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
3124        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
3125        // Nothing is cached, so nothing is there, one answer per hash asked
3126        // about.
3127        assert_eq!(
3128            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
3129            "*2\r\n:0\r\n:0\r\n"
3130        );
3131        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
3132        assert_eq!(
3133            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
3134            "*0\r\n"
3135        );
3136        assert_eq!(
3137            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
3138            "-ERR Library not found\r\n"
3139        );
3140
3141        // Redis's two messages here are its own, one per container, and one of
3142        // them reads like a typo.
3143        assert_eq!(
3144            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
3145            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
3146        );
3147        assert_eq!(
3148            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
3149            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
3150        );
3151        // A second argument after the mode is the generic one instead, because
3152        // the count is checked before the word is looked at.
3153        assert_eq!(
3154            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
3155            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
3156        );
3157        assert_eq!(
3158            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
3159            "-ERR Unknown argument bogus\r\n"
3160        );
3161        assert_eq!(
3162            f.run(&[b"SCRIPT", b"EXISTS"]),
3163            "-ERR wrong number of arguments for 'script|exists' command\r\n"
3164        );
3165
3166        // The ones that need an interpreter are not here, and say so rather
3167        // than answering OK to a load that loaded nothing.
3168        assert_eq!(
3169            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
3170            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
3171        );
3172        assert_eq!(
3173            f.run(&[b"FUNCTION", b"STATS"]),
3174            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
3175        );
3176    }
3177
3178    #[test]
3179    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
3180        let mut f = Fixture::new();
3181        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
3182        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
3183        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
3184        // Read back as a string it is still an integer, written out as digits
3185        // only because somebody asked for them.
3186        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
3187        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
3188        // A counter that is not a number is the error the store raises and this
3189        // layer only spells, which is the whole point of the split.
3190        f.run(&[b"SET", b"k", b"hello"]);
3191        assert_eq!(
3192            f.run(&[b"INCR", b"k"]),
3193            "-ERR value is not an integer or out of range\r\n"
3194        );
3195        assert_eq!(
3196            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
3197            "-ERR increment would produce NaN or Infinity\r\n"
3198        );
3199    }
3200
3201    /// Every one of these was read off a running 8.8. They are the answers a
3202    /// client library's own test suite checks, and the shapes are not
3203    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
3204    /// integer, `INCREX` is a pair.
3205    #[test]
3206    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
3207        let mut f = Fixture::new();
3208        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
3209        // The same digest a real 8.8 answers for the same five bytes, which is
3210        // what makes `IFDEQ` usable against a mixed deployment.
3211        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
3212        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
3213        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
3214        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
3215        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
3216        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
3217        assert_eq!(
3218            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
3219            "*2\r\n:1\r\n:0\r\n",
3220            "a refused increment reports the value it left alone and applied nothing"
3221        );
3222        assert_eq!(
3223            f.run(&[
3224                b"INCREX",
3225                b"n",
3226                b"BYINT",
3227                b"5",
3228                b"UBOUND",
3229                b"3",
3230                b"SATURATE"
3231            ]),
3232            "*2\r\n:3\r\n:2\r\n"
3233        );
3234        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
3235        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
3236    }
3237
3238    #[test]
3239    fn the_same_answers_come_out_in_resp3_spelling() {
3240        let mut f = Fixture::new();
3241        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
3242        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
3243        // A float counter is a double on RESP3 and the digits in a bulk string
3244        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
3245        assert_eq!(
3246            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
3247            "*2\r\n,1.5\r\n,1.5\r\n"
3248        );
3249        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
3250        // `RESET` puts the protocol back, which is the part that is easy to
3251        // miss and leaves a pooled connection speaking the wrong one.
3252        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
3253        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
3254    }
3255
3256    #[test]
3257    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
3258        let mut f = Fixture::new();
3259        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
3260        assert_eq!(flow, Flow::Continue);
3261        assert_eq!(
3262            reply,
3263            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
3264        );
3265        // A name with a line ending in it cannot write its own frame into the
3266        // stream, which is the reason the error writer maps them to spaces.
3267        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
3268        assert_eq!(reply.matches("\r\n").count(), 1);
3269    }
3270
3271    #[test]
3272    fn arity_is_checked_before_the_command_is() {
3273        let mut f = Fixture::new();
3274        assert_eq!(
3275            f.run(&[b"GET"]),
3276            "-ERR wrong number of arguments for 'get' command\r\n"
3277        );
3278        assert_eq!(
3279            f.run(&[b"MSET", b"k"]),
3280            "-ERR wrong number of arguments for 'mset' command\r\n"
3281        );
3282        // The table says `PING` takes one or more and a real server then
3283        // refuses three, which is the sort of thing that only shows up against
3284        // the real thing.
3285        assert_eq!(
3286            f.run(&[b"PING", b"a", b"b"]),
3287            "-ERR wrong number of arguments for 'ping' command\r\n"
3288        );
3289        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
3290        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
3291        // `DELEX` takes two or four and nothing between.
3292        assert_eq!(
3293            f.run(&[b"DELEX", b"k", b"IFEQ"]),
3294            "-ERR wrong number of arguments for 'delex' command\r\n"
3295        );
3296    }
3297
3298    /// The option rules, all of them measured against 8.8 rather than read off
3299    /// the documentation. The surprising one is that `SET` accepts the same
3300    /// keyword twice and `INCREX` does not.
3301    #[test]
3302    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
3303        let mut f = Fixture::new();
3304        let syntax = "-ERR syntax error\r\n";
3305        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
3306        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
3307        assert_eq!(
3308            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
3309            syntax
3310        );
3311        assert_eq!(
3312            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
3313            syntax
3314        );
3315        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
3316        // Twice is fine, and the last one wins.
3317        assert_eq!(
3318            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
3319            "+OK\r\n"
3320        );
3321        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
3322        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
3323        // `INCREX` refuses what `SET` allows.
3324        assert_eq!(
3325            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
3326            syntax
3327        );
3328        assert_eq!(
3329            f.run(&[b"INCREX", b"n", b"ENX"]),
3330            "-ERR ENX flag requires an expiration\r\n"
3331        );
3332        assert_eq!(
3333            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
3334            "-ERR UBOUND is not an integer or out of range\r\n"
3335        );
3336        assert_eq!(
3337            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
3338            "-ERR LBOUND can't be greater than UBOUND\r\n"
3339        );
3340        assert_eq!(
3341            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
3342            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
3343        );
3344    }
3345
3346    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
3347    /// key that is not there, which answers null without ever looking at the
3348    /// expiration it was given.
3349    #[test]
3350    fn the_expiry_rules_are_redis_own() {
3351        let mut f = Fixture::new();
3352        let bad = "-ERR invalid expire time in 'set' command\r\n";
3353        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
3354        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
3355        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
3356        assert_eq!(
3357            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
3358            bad
3359        );
3360        assert_eq!(
3361            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
3362            "-ERR value is not an integer or out of range\r\n"
3363        );
3364        assert_eq!(
3365            f.run(&[b"SETEX", b"k", b"0", b"v"]),
3366            "-ERR invalid expire time in 'setex' command\r\n"
3367        );
3368        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
3369        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
3370        assert_eq!(
3371            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
3372            "-ERR syntax error\r\n",
3373            "the option list is still checked before the key is looked up"
3374        );
3375        // A deadline in the past is accepted and the key goes with it.
3376        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3377        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
3378        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3379    }
3380
3381    #[test]
3382    fn mset_takes_its_pairs_from_the_read_buffer() {
3383        let mut f = Fixture::new();
3384        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
3385        assert_eq!(
3386            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
3387            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
3388        );
3389        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
3390        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
3391        assert_eq!(
3392            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
3393            "-ERR wrong number of key-value pairs\r\n"
3394        );
3395        assert_eq!(
3396            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
3397            "-ERR invalid numkeys value\r\n"
3398        );
3399        assert_eq!(
3400            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
3401            "-ERR invalid numkeys value\r\n"
3402        );
3403    }
3404
3405    #[test]
3406    fn lcs_answers_the_length_the_string_and_the_runs() {
3407        let mut f = Fixture::new();
3408        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
3409        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3410        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3411        assert_eq!(
3412            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3413            "*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"
3414        );
3415        // Without `IDX` the two options that only mean something with it are
3416        // accepted and ignored, which is what a real server does.
3417        assert_eq!(
3418            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3419            "$6\r\nmytext\r\n"
3420        );
3421    }
3422
3423    #[test]
3424    fn select_moves_the_connection_and_the_databases_stay_apart() {
3425        let mut f = Fixture::new();
3426        f.run(&[b"SET", b"k", b"zero"]);
3427        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3428        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3429        f.run(&[b"SET", b"k", b"four"]);
3430        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3431        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3432        assert_eq!(
3433            f.run(&[b"SELECT", b"99"]),
3434            "-ERR DB index is out of range\r\n"
3435        );
3436        assert_eq!(
3437            f.run(&[b"SELECT", b"-1"]),
3438            "-ERR DB index is out of range\r\n"
3439        );
3440        assert_eq!(
3441            f.run(&[b"SELECT", b"abc"]),
3442            "-ERR value is not an integer or out of range\r\n"
3443        );
3444        // `RESET` brings it back to zero.
3445        f.run(&[b"SELECT", b"4"]);
3446        f.run(&[b"RESET"]);
3447        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3448    }
3449
3450    #[test]
3451    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3452        let mut f = Fixture::new();
3453        let reply = f.run(&[b"HELLO"]);
3454        assert!(reply.starts_with("*14\r\n"), "{reply}");
3455        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3456        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3457        assert!(
3458            reply.contains(":7\r\n"),
3459            "the connection id is in there: {reply}"
3460        );
3461        assert_eq!(
3462            f.run(&[b"HELLO", b"4"]),
3463            "-NOPROTO unsupported protocol version\r\n"
3464        );
3465        assert_eq!(
3466            f.run(&[b"HELLO", b"abc"]),
3467            "-ERR Protocol version is not an integer or out of range\r\n"
3468        );
3469        assert_eq!(
3470            f.run(&[b"HELLO", b"3", b"SETNAME"]),
3471            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3472        );
3473        assert!(
3474            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3475                .starts_with("%7\r\n")
3476        );
3477        assert_eq!(f.session.name(), b"bob");
3478        f.run(&[b"RESET"]);
3479        assert_eq!(f.session.name(), b"");
3480    }
3481
3482    #[test]
3483    fn command_describes_this_server_in_the_shape_a_driver_reads() {
3484        let mut f = Fixture::new();
3485        let count = format!(":{}\r\n", COMMANDS.len());
3486        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3487        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3488        assert_eq!(
3489            info,
3490            "*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\
3491             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3492        );
3493        // A null in the list, and the plain one: `$-1` and not `*-1`.
3494        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3495        assert_eq!(
3496            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3497            "*1\r\n$8\r\ngetrange\r\n"
3498        );
3499        assert_eq!(
3500            f.run(&[b"COMMAND", b"NOPE"]),
3501            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3502        );
3503    }
3504
3505    /// A cluster aware client asks this question and then routes on the
3506    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
3507    /// that matters.
3508    #[test]
3509    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3510        let mut f = Fixture::new();
3511        assert_eq!(
3512            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3513            "*1\r\n$1\r\nk\r\n"
3514        );
3515        assert_eq!(
3516            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3517            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3518        );
3519        assert_eq!(
3520            f.run(&[
3521                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3522            ]),
3523            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3524        );
3525        assert_eq!(
3526            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3527            "-ERR The command has no key arguments\r\n"
3528        );
3529        assert_eq!(
3530            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3531            "-ERR Invalid number of arguments specified for command\r\n"
3532        );
3533    }
3534
3535    #[test]
3536    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3537        let mut f = Fixture::new();
3538        assert_eq!(
3539            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3540            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3541        );
3542        // A pattern matches more than one, and a setting two patterns both ask
3543        // for is still sent once.
3544        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3545        assert!(both.starts_with("*6\r\n"), "{both}");
3546        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3547        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3548        assert_eq!(
3549            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3550            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3551        );
3552        assert_eq!(
3553            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3554            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3555        );
3556        assert_eq!(
3557            f.run(&[b"CONFIG", b"GET"]),
3558            "-ERR wrong number of arguments for 'config|get' command\r\n"
3559        );
3560        // Too few arguments and an odd number of them are different
3561        // complaints, which is the sort of thing only the real server tells
3562        // you.
3563        assert_eq!(
3564            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3565            "-ERR wrong number of arguments for 'config|set' command\r\n"
3566        );
3567        assert_eq!(
3568            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3569            "-ERR syntax error\r\n"
3570        );
3571        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3572        assert_eq!(
3573            f.run(&[b"CONFIG", b"REWRITE"]),
3574            "-ERR The server is running without a config file\r\n"
3575        );
3576    }
3577
3578    #[test]
3579    fn the_eviction_policy_reads_back_what_was_written_to_it() {
3580        let mut f = Fixture::new();
3581        assert_eq!(
3582            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3583            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3584        );
3585        assert_eq!(
3586            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3587            "+OK\r\n",
3588            "the name is matched without regard to case, like every other one"
3589        );
3590        assert_eq!(
3591            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3592            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3593        );
3594        // And INFO agrees with CONFIG, which it did not when it was a literal.
3595        assert!(
3596            f.run(&[b"INFO", b"memory"])
3597                .contains("maxmemory_policy:allkeys-lfu"),
3598            "INFO and CONFIG disagree about the policy"
3599        );
3600        // The refusal names every legal value in the order the real server's
3601        // enum table lists them, because a client comparing the message compares
3602        // the whole string.
3603        assert_eq!(
3604            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3605            "-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"
3606        );
3607        // A bad pair leaves the good one in the same command alone, and the
3608        // policy is checked by the same pass that checks the numbers.
3609        assert_eq!(
3610            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3611            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3612        );
3613        f.run(&[
3614            b"CONFIG",
3615            b"SET",
3616            b"hash-max-listpack-entries",
3617            b"7",
3618            b"maxmemory-policy",
3619            b"nonsense",
3620        ]);
3621        assert_eq!(
3622            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3623            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3624        );
3625    }
3626
3627    #[test]
3628    fn the_three_eviction_numbers_read_back_too() {
3629        let mut f = Fixture::new();
3630        for (name, default, set) in [
3631            ("maxmemory-samples", "5", "12"),
3632            ("lfu-log-factor", "10", "3"),
3633            ("lfu-decay-time", "1", "60"),
3634        ] {
3635            let get = || {
3636                format!(
3637                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3638                    name.len(),
3639                    default.len()
3640                )
3641            };
3642            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3643            assert_eq!(
3644                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3645                "+OK\r\n"
3646            );
3647            assert_eq!(
3648                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3649                format!(
3650                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3651                    name.len(),
3652                    set.len()
3653                )
3654            );
3655            // A number that is not a number is refused with the same sentence
3656            // every other number gets, which names the setting the client typed.
3657            assert_eq!(
3658                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3659                format!(
3660                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3661                )
3662            );
3663        }
3664    }
3665
3666    #[test]
3667    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3668        let mut f = Fixture::new();
3669        assert_eq!(
3670            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3671            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3672            "no limit is the default"
3673        );
3674        // The pairing is Redis's and it is a trap: the bare letter is a power of
3675        // ten and the one with the b is a power of two.
3676        for (typed, bytes) in [
3677            (&b"1024"[..], "1024"),
3678            (b"1k", "1000"),
3679            (b"1kb", "1024"),
3680            (b"1M", "1000000"),
3681            (b"1Mb", "1048576"),
3682            (b"1gb", "1073741824"),
3683            (b"100mb", "104857600"),
3684        ] {
3685            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3686            assert_eq!(
3687                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3688                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3689                "set {}",
3690                String::from_utf8_lossy(typed)
3691            );
3692        }
3693        assert!(
3694            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3695            "the report agrees with the setting"
3696        );
3697
3698        // A unit nobody has heard of, and a negative number, which is not a very
3699        // large one however it is spelled.
3700        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3701            assert_eq!(
3702                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3703                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3704                "refused {}",
3705                String::from_utf8_lossy(bad)
3706            );
3707        }
3708        assert!(
3709            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3710            "and the refusal left the old one alone"
3711        );
3712    }
3713
3714    #[test]
3715    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3716        let mut f = Fixture::new();
3717        f.run(&[b"SET", b"here", b"already"]);
3718        // A byte, which is under what an empty server holds, so nothing this
3719        // command could do would get it under. The default policy is
3720        // `noeviction`, so nothing is what it does.
3721        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3722        assert_eq!(
3723            f.run(&[b"SET", b"k", b"v"]),
3724            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3725        );
3726        assert_eq!(
3727            f.run(&[b"LPUSH", b"l", b"v"]),
3728            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3729        );
3730        // Reading is allowed, and so is the one thing that would help.
3731        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3732        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3733        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3734
3735        // Taking the limit away lets the write through again.
3736        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3737        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3738    }
3739
3740    #[test]
3741    fn an_allkeys_policy_makes_room_instead_of_refusing() {
3742        let mut f = Fixture::new();
3743        let val = vec![b'v'; 256];
3744        for i in 0..24000u32 {
3745            let k = format!("key:{i:08}");
3746            f.run(&[b"SET", k.as_bytes(), &val]);
3747        }
3748        let full = f.server.memory_bytes();
3749        assert!(
3750            full > 3 * 1024 * 1024,
3751            "the arena is several segments: {full}"
3752        );
3753
3754        // Two megabytes under what it is holding, which is one segment's worth,
3755        // so getting there means giving a whole segment back and not just
3756        // dropping a few records.
3757        let limit = full - 2 * 1024 * 1024;
3758        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3759        f.run(&[
3760            b"CONFIG",
3761            b"SET",
3762            b"maxmemory",
3763            limit.to_string().as_bytes(),
3764        ]);
3765
3766        // Writes keep working the whole way down. The budget means one command
3767        // does not do it all, so this runs until the server has settled and
3768        // checks that nothing was refused on the way.
3769        for i in 0..2000u32 {
3770            let k = format!("new:{i:08}");
3771            assert_eq!(
3772                f.run(&[b"SET", k.as_bytes(), &val]),
3773                "+OK\r\n",
3774                "write {i} was refused"
3775            );
3776            f.server.refresh_memory();
3777            if f.server.memory_bytes() <= limit {
3778                break;
3779            }
3780        }
3781        assert!(
3782            f.server.memory_bytes() <= limit,
3783            "it never got under: {} against {limit}",
3784            f.server.memory_bytes()
3785        );
3786        let info = f.run(&[b"INFO", b"stats"]);
3787        assert!(!info.contains("evicted_keys:0"), "{info}");
3788        assert!(
3789            f.run(&[b"DBSIZE"]) != ":0\r\n",
3790            "and it did not empty the database to get there"
3791        );
3792    }
3793
3794    #[test]
3795    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3796        // The limit is judged against a number kept as the collections move,
3797        // rather than found by asking all of them, and the two have to be the
3798        // same number or the limit is enforced against a fiction. This does the
3799        // things that move it, which is growing a collection, shrinking one,
3800        // changing its representation, deleting it and reusing its slot, across
3801        // all five types, and checks the two against each other as it goes.
3802        let mut f = Fixture::new();
3803        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3804        let big = vec![b'v'; 200];
3805
3806        for i in 0..400u32 {
3807            let n = i.to_string();
3808            let n = n.as_bytes();
3809            f.run(&[b"SADD", b"s", n]);
3810            f.run(&[b"SADD", b"s2", &big]);
3811            f.run(&[b"HSET", b"h", n, &big]);
3812            f.run(&[b"RPUSH", b"l", &big]);
3813            f.run(&[b"ZADD", b"z", n, n]);
3814            f.run(&[b"ARSET", b"a", n, &big]);
3815            if i % 7 == 0 {
3816                f.run(&[b"SREM", b"s", n]);
3817                f.run(&[b"HDEL", b"h", n]);
3818                f.run(&[b"LPOP", b"l"]);
3819                f.run(&[b"ZREM", b"z", n]);
3820                f.run(&[b"ARDEL", b"a", n]);
3821            }
3822            if i % 53 == 0 {
3823                // Every type deleted and made again, so a slot goes on the free
3824                // list and comes back holding something else.
3825                f.run(&[b"DEL", b"s2"]);
3826            }
3827            assert_eq!(
3828                f.server.settled_memory(),
3829                f.server.memory_bytes(),
3830                "after round {i}"
3831            );
3832        }
3833
3834        // The run has to have built something, or the two numbers agreeing is
3835        // two zeroes agreeing.
3836        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3837        assert!(
3838            f.server.memory_bytes() > 512 * 1024,
3839            "{}",
3840            f.server.memory_bytes()
3841        );
3842
3843        // And it survives the collections going away entirely.
3844        f.run(&[b"FLUSHALL"]);
3845        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3846    }
3847
3848    #[test]
3849    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3850        // A server with no limit does not keep the running total, so setting a
3851        // limit on a database that is already full has to start it from a walk.
3852        // If it did not, the first reading would be zero and the server would
3853        // think it had all the room in the world.
3854        let mut f = Fixture::new();
3855        for i in 0..200u32 {
3856            let n = i.to_string();
3857            f.run(&[b"SADD", b"s", n.as_bytes()]);
3858            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3859        }
3860        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3861        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3862
3863        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3864        for i in 200..400u32 {
3865            let n = i.to_string();
3866            f.run(&[b"SADD", b"s", n.as_bytes()]);
3867        }
3868        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3869        assert_eq!(
3870            f.server.settled_memory(),
3871            f.server.memory_bytes(),
3872            "the writes it was not watching are in the number it started from"
3873        );
3874    }
3875
3876    #[test]
3877    fn evicted_keys_and_expired_keys_are_different_numbers() {
3878        let mut f = Fixture::new();
3879        // Nothing has been evicted and nothing can be under the default policy,
3880        // so this stays at zero while the other one moves.
3881        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3882        f.server.advance_clock_ms(20);
3883        f.run(&[b"GET", b"gone"]);
3884        let info = f.run(&[b"INFO", b"stats"]);
3885        assert!(info.contains("expired_keys:1"), "{info}");
3886        assert!(info.contains("evicted_keys:0"), "{info}");
3887    }
3888
3889    #[test]
3890    fn the_object_subcommands_follow_the_policy() {
3891        let mut f = Fixture::new();
3892        f.run(&[b"SET", b"s", b"v"]);
3893        // Under the default the clock is kept and the counter is not, and under
3894        // an LFU policy it is the other way round. Each subcommand refuses on
3895        // the side where its reading of the three bytes means nothing.
3896        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3897        assert!(
3898            f.run(&[b"OBJECT", b"FREQ", b"s"])
3899                .starts_with("-ERR An LFU maxmemory policy is not selected"),
3900        );
3901
3902        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3903        assert!(
3904            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3905                .starts_with("-ERR An LFU maxmemory policy is selected"),
3906        );
3907        // The key was written under a clock policy, so what comes back is that
3908        // clock read as a counter. It is a number and not an error, which is the
3909        // point: switching at runtime does not invalidate anything, it only makes
3910        // the old field mean something else until the key is used again.
3911        assert!(
3912            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3913            "FREQ should answer under an LFU policy"
3914        );
3915    }
3916
3917    #[test]
3918    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3919        let mut f = Fixture::new();
3920        f.run(&[b"SET", b"s", b"hello"]);
3921        f.run(&[b"SET", b"n", b"123"]);
3922        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3923        f.run(&[b"SADD", b"ss", b"a", b"b"]);
3924        f.run(&[b"HSET", b"h", b"f", b"v"]);
3925        for (key, want) in [
3926            (b"s".as_slice(), "embstr"),
3927            (b"n", "int"),
3928            (b"si", "intset"),
3929            (b"ss", "listpack"),
3930            (b"h", "listpack"),
3931        ] {
3932            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3933            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3934        }
3935
3936        // A field deadline widens the blob rather than promoting it, and this
3937        // is the only place a client can see that happen.
3938        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3939        assert_eq!(
3940            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3941            "$10\r\nlistpackex\r\n"
3942        );
3943
3944        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3945        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3946        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3947    }
3948
3949    #[test]
3950    fn object_answers_nil_for_a_key_that_is_not_there() {
3951        let mut f = Fixture::new();
3952        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3953            assert_eq!(
3954                f.run(&[b"OBJECT", sub, b"nokey"]),
3955                "$-1\r\n",
3956                "a nil and not an error, which is what 8.10.1 does"
3957            );
3958        }
3959        // And the key is looked up before FREQ has its complaint, so the
3960        // complaint only reaches a key that exists.
3961        f.run(&[b"SET", b"s", b"v"]);
3962        assert!(
3963            f.run(&[b"OBJECT", b"FREQ", b"s"])
3964                .starts_with("-ERR An LFU maxmemory policy is not"),
3965        );
3966        assert_eq!(
3967            f.run(&[b"OBJECT", b"NOPE", b"s"]),
3968            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3969        );
3970        assert_eq!(
3971            f.run(&[b"OBJECT", b"ENCODING"]),
3972            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3973        );
3974        assert_eq!(
3975            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3976            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3977        );
3978        assert_eq!(
3979            f.run(&[b"OBJECT"]),
3980            "-ERR wrong number of arguments for 'object' command\r\n"
3981        );
3982    }
3983
3984    #[test]
3985    fn config_moves_the_ladder_and_object_encoding_agrees() {
3986        let mut f = Fixture::new();
3987        assert_eq!(
3988            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3989            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3990            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3991        );
3992        // The old spelling is the same number under a different name, and a
3993        // glob that catches both sends both.
3994        assert_eq!(
3995            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3996            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3997        );
3998        assert!(
3999            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
4000                .starts_with("*8\r\n")
4001        );
4002        assert!(
4003            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
4004                .starts_with("*6\r\n")
4005        );
4006
4007        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
4008        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
4009
4010        assert_eq!(
4011            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
4012            "+OK\r\n",
4013            "written under the old name and read back under the new one"
4014        );
4015        assert_eq!(
4016            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
4017            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
4018        );
4019        assert_eq!(
4020            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4021            "$8\r\nlistpack\r\n",
4022            "the hash that already exists is left exactly where it was"
4023        );
4024        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
4025        assert_eq!(
4026            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
4027            "$9\r\nhashtable\r\n",
4028            "and the next one built goes straight to a table"
4029        );
4030
4031        // The set has three of these and all three move.
4032        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
4033        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
4034        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
4035        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
4036        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
4037        assert_eq!(
4038            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
4039            "$9\r\nhashtable\r\n"
4040        );
4041    }
4042
4043    #[test]
4044    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
4045        let mut f = Fixture::new();
4046        assert_eq!(
4047            f.run(&[
4048                b"CONFIG",
4049                b"SET",
4050                b"hash-max-listpack-entries",
4051                b"7",
4052                b"set-max-listpack-entries",
4053                b"abc"
4054            ]),
4055            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
4056        );
4057        assert_eq!(
4058            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
4059            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
4060            "the pair in front of the bad one did not go in"
4061        );
4062        // The name in the complaint is the one that was typed, so the old
4063        // spelling comes back as the old spelling.
4064        assert_eq!(
4065            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
4066            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
4067        );
4068        assert_eq!(
4069            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
4070            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
4071        );
4072        // A number past what an i64 holds is the parse complaint and not the
4073        // range one, which is upstream reading it before it checks it.
4074        assert_eq!(
4075            f.run(&[
4076                b"CONFIG",
4077                b"SET",
4078                b"set-max-intset-entries",
4079                b"99999999999999999999"
4080            ]),
4081            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
4082        );
4083        assert_eq!(
4084            f.run(&[
4085                b"CONFIG",
4086                b"SET",
4087                b"set-max-intset-entries",
4088                b"9223372036854775807"
4089            ]),
4090            "+OK\r\n"
4091        );
4092    }
4093
4094    #[test]
4095    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
4096        let mut f = Fixture::new();
4097        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
4098        f.run(&[b"SELECT", b"3"]);
4099        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4100        assert_eq!(
4101            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4102            "$9\r\nhashtable\r\n",
4103            "these are one server wide number in Redis, whatever a Keyspace carries"
4104        );
4105    }
4106
4107    #[test]
4108    fn info_reports_the_numbers_it_can_stand_behind() {
4109        let mut f = Fixture::new();
4110        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
4111        let all = f.run(&[b"INFO"]);
4112        assert!(all.contains("redis_version:8.8.0"), "{all}");
4113        assert!(
4114            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
4115            "{all}"
4116        );
4117        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
4118        assert!(all.contains("role:master"), "{all}");
4119        // One section is one section.
4120        let clients = f.run(&[b"INFO", b"clients"]);
4121        assert!(clients.contains("connected_clients:0"), "{clients}");
4122        assert!(!clients.contains("redis_version"), "{clients}");
4123        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
4124    }
4125
4126    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
4127    ///
4128    /// This is Redis's `unit/info-command` written against the fixture. Every
4129    /// assertion in it is one of theirs, in their order, and the two fields it
4130    /// turns on are the two that suite was failing on: `master_repl_offset`,
4131    /// which is in the default set, and `rejected_calls`, which is not.
4132    #[test]
4133    fn commandstats_is_asked_for_and_replication_is_not() {
4134        let mut f = Fixture::new();
4135        for arg in ["", "all", "default", "everything"] {
4136            let info = if arg.is_empty() {
4137                f.run(&[b"INFO"])
4138            } else {
4139                f.run(&[b"INFO", arg.as_bytes()])
4140            };
4141            assert!(info.contains("redis_version"), "{arg}: {info}");
4142            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
4143            assert!(info.contains("used_memory"), "{arg}: {info}");
4144            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
4145            let asked = arg == "all" || arg == "everything";
4146            assert_eq!(
4147                info.contains("rejected_calls"),
4148                asked,
4149                "{arg} should{} carry the command counters: {info}",
4150                if asked { "" } else { " not" }
4151            );
4152        }
4153
4154        let cpu = f.run(&[b"INFO", b"cpu"]);
4155        assert!(cpu.contains("used_cpu_user"), "{cpu}");
4156        assert!(!cpu.contains("used_memory"), "{cpu}");
4157
4158        // Their case, to make the point that a section name is not case
4159        // sensitive any more than a command name is.
4160        let stats = f.run(&[b"INFO", b"commandSTATS"]);
4161        assert!(!stats.contains("used_memory"), "{stats}");
4162        assert!(stats.contains("rejected_calls"), "{stats}");
4163
4164        // Two sections named, and neither of them pulls in a third.
4165        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
4166        assert!(pair.contains("used_cpu_user"), "{pair}");
4167        assert!(!pair.contains("master_repl_offset"), "{pair}");
4168
4169        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
4170        assert!(with_all.contains("used_memory"), "{with_all}");
4171        assert!(with_all.contains("master_repl_offset"), "{with_all}");
4172        assert!(with_all.contains("rejected_calls"), "{with_all}");
4173        // A section named twice is still written once.
4174        assert_eq!(
4175            with_all.matches("used_cpu_user_children").count(),
4176            1,
4177            "{with_all}"
4178        );
4179
4180        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
4181        assert!(with_default.contains("used_memory"), "{with_default}");
4182        assert!(
4183            with_default.contains("master_repl_offset"),
4184            "{with_default}"
4185        );
4186        assert!(!with_default.contains("rejected_calls"), "{with_default}");
4187        assert_eq!(
4188            with_default.matches("used_cpu_user_children").count(),
4189            1,
4190            "{with_default}"
4191        );
4192    }
4193
4194    /// The memory section says what this process may use, not what the machine
4195    /// has.
4196    ///
4197    /// The distinction is the whole point of it. A server inside a container
4198    /// that reports the host's memory is a server whose operator sizes it for
4199    /// memory it will be killed for touching, so all three numbers are there:
4200    /// what the machine has, what the cgroup allows, and the quarter of the
4201    /// tighter one that pools are sized from.
4202    #[test]
4203    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
4204        let mut f = Fixture::new();
4205        let info = f.run(&[b"INFO", b"memory"]);
4206        for field in [
4207            "total_system_memory:",
4208            "mem_cgroup_limit:",
4209            "mem_limit:",
4210            "mem_budget:",
4211        ] {
4212            assert!(info.contains(field), "no {field} in {info}");
4213        }
4214
4215        let field = |name: &str| -> u64 {
4216            info.lines()
4217                .find_map(|l| l.strip_prefix(name))
4218                .unwrap_or_else(|| panic!("no {name} in {info}"))
4219                .trim()
4220                .parse()
4221                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
4222        };
4223        let limit = field("mem_limit:");
4224        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
4225        // Zero means there is no limit to report, which is a real answer on a
4226        // machine with no cgroups and no way to ask how big it is.
4227        if limit != 0 {
4228            let host = field("total_system_memory:");
4229            let cgroup = field("mem_cgroup_limit:");
4230            assert!(
4231                limit == host || limit == cgroup,
4232                "the limit came from neither number: {info}"
4233            );
4234        }
4235    }
4236
4237    /// The three counters, each on the path that raises it.
4238    ///
4239    /// `calls` on a command that worked, `failed_calls` on one that ran and
4240    /// answered with an error, and `rejected_calls` on one that never ran at
4241    /// all. The last two are the pair that is easy to collapse into one number
4242    /// and that Redis keeps apart, because a client sending the wrong number of
4243    /// arguments and a client asking for a list element that is not there are
4244    /// not the same problem.
4245    #[test]
4246    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
4247        let mut f = Fixture::new();
4248        f.run(&[b"SET", b"k", b"v"]);
4249        f.run(&[b"SET", b"k", b"w"]);
4250        // Ran, and answered with an error, because `k` is not a list.
4251        f.run(&[b"LPUSH", b"k", b"x"]);
4252        // Never ran: `LPUSH` takes at least three arguments.
4253        f.run(&[b"LPUSH", b"k"]);
4254
4255        let stats = f.run(&[b"INFO", b"commandstats"]);
4256        assert!(
4257            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
4258            "{stats}"
4259        );
4260        assert!(
4261            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
4262            "{stats}"
4263        );
4264        assert!(
4265            !stats.contains("cmdstat_zadd"),
4266            "a command nobody has sent has no row: {stats}"
4267        );
4268    }
4269
4270    /// A cache that writes with a deadline and never reads back used to hold
4271    /// every key it had ever written, because lazy expiry needs somebody to walk
4272    /// past a key before it can reclaim it and nobody ever did.
4273    #[test]
4274    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
4275        let mut f = Fixture::new();
4276        for i in 0..3_000u32 {
4277            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4278        }
4279        for i in 0..1_000u32 {
4280            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4281        }
4282        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
4283        f.advance(100);
4284        assert_eq!(
4285            f.run(&[b"DBSIZE"]),
4286            ":4000\r\n",
4287            "DBSIZE counts records and nothing has read past the dead ones yet"
4288        );
4289
4290        // What the shard loop does, one slice at a time.
4291        let mut spent = 0;
4292        for _ in 0..2_000 {
4293            spent += f.server.expire_step(4096);
4294            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
4295                break;
4296            }
4297        }
4298        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
4299        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
4300        for i in 0..1_000u32 {
4301            assert_eq!(
4302                f.run(&[b"GET", format!("k{i}").as_bytes()]),
4303                "$1\r\nv\r\n",
4304                "it took a key that had no deadline"
4305            );
4306        }
4307    }
4308
4309    #[test]
4310    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
4311        let mut f = Fixture::new();
4312        for i in 0..2_000u32 {
4313            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4314        }
4315        assert_eq!(f.server.expire_step(4096), 0);
4316        // And one database having them does not make the other fifteen pay.
4317        f.run(&[b"SELECT", b"3"]);
4318        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
4319        f.advance(100);
4320        for _ in 0..64 {
4321            f.server.expire_step(4096);
4322        }
4323        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4324        f.run(&[b"SELECT", b"0"]);
4325        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
4326        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
4327    }
4328
4329    /// The gate, which is what stops a maintenance slice that runs every hundred
4330    /// nanoseconds from drawing a sample every hundred nanoseconds.
4331    #[test]
4332    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
4333        let mut f = Fixture::new();
4334        for i in 0..500u32 {
4335            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4336        }
4337        f.advance(100);
4338        let at = f.server.striped(0).now_ms();
4339        f.server.set_clock_ms(at);
4340        // A small budget, so that one slice cannot finish the job and a second
4341        // one having nothing to do would mean the gate and not an empty
4342        // database.
4343        assert!(f.server.expire_slice(8) > 0, "the first one works");
4344        for _ in 0..1_000 {
4345            assert_eq!(
4346                f.server.expire_slice(8),
4347                0,
4348                "the millisecond has not moved and neither should this"
4349            );
4350        }
4351        assert!(
4352            f.server.striped(0).expires() > 400,
4353            "there is plenty left to take"
4354        );
4355        f.server.set_clock_ms(at + 1);
4356        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
4357    }
4358
4359    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
4360    /// how much of a cache is volatile was reading a constant.
4361    #[test]
4362    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
4363        let mut f = Fixture::new();
4364        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4365        assert!(
4366            f.run(&[b"INFO", b"keyspace"])
4367                .contains("db0:keys=3,expires=0"),
4368            "none of them has one yet"
4369        );
4370        f.run(&[b"EXPIRE", b"a", b"1000"]);
4371        f.run(&[b"EXPIRE", b"b", b"1000"]);
4372        let two = f.run(&[b"INFO", b"keyspace"]);
4373        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
4374        f.run(&[b"PERSIST", b"a"]);
4375        f.run(&[b"DEL", b"b"]);
4376        let none = f.run(&[b"INFO", b"keyspace"]);
4377        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
4378
4379        // Each database answers for itself, the way Redis reports it.
4380        f.run(&[b"SELECT", b"1"]);
4381        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
4382        let both = f.run(&[b"INFO", b"keyspace"]);
4383        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
4384        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
4385    }
4386
4387    #[cfg(unix)]
4388    #[test]
4389    fn info_cpu_reports_processor_time_that_was_really_measured() {
4390        let mut f = Fixture::new();
4391        let cpu = f.run(&[b"INFO", b"cpu"]);
4392        assert!(cpu.contains("# CPU"), "{cpu}");
4393        // Redis's unit/info-command asks for this one by name in three tests.
4394        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
4395        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
4396        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
4397        assert!(!cpu.contains("redis_version"), "{cpu}");
4398
4399        // It is a measurement and not a constant, so it goes up when work
4400        // happens. A tight loop rather than a sleep, because sleeping is the
4401        // one thing that does not move this number.
4402        let before = used_cpu_user(&cpu);
4403        let mut n = 0u64;
4404        let mut rounds = 0;
4405        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
4406            for i in 0..1_000_000u64 {
4407                n = n.wrapping_add(i.wrapping_mul(i));
4408            }
4409            rounds += 1;
4410            // A bound rather than a spin, so a platform where this number does
4411            // not move fails here instead of hanging. Even a clock with whole
4412            // millisecond granularity gets there in the first round or two.
4413            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4414        }
4415    }
4416
4417    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
4418    #[cfg(unix)]
4419    fn used_cpu_user(info: &str) -> f64 {
4420        info.lines()
4421            .find_map(|l| l.strip_prefix("used_cpu_user:"))
4422            .expect("no used_cpu_user in the reply")
4423            .trim()
4424            .parse()
4425            .expect("used_cpu_user is not a number")
4426    }
4427
4428    /// The safety net under the rule that a body checks its arguments before
4429    /// it writes anything. `MGET` writes its array header first and then reads
4430    /// each key, so if a later argument could fail the header would already be
4431    /// out. Nothing in the string group does that today and this is what would
4432    /// catch the first one that did.
4433    #[test]
4434    fn a_command_that_fails_leaves_nothing_half_written() {
4435        let mut f = Fixture::new();
4436        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4437        assert_eq!(reply, "-ERR offset is out of range\r\n");
4438        assert!(!reply.contains(':'), "no integer went out in front of it");
4439    }
4440
4441    #[test]
4442    fn quit_answers_first_and_closes_after() {
4443        let mut f = Fixture::new();
4444        let (flow, reply) = f.flow(&[b"QUIT"]);
4445        assert_eq!(reply, "+OK\r\n");
4446        assert_eq!(flow, Flow::Close);
4447    }
4448
4449    /// A server that has not been asked to stop is not stopping, and one that
4450    /// has says so without writing anything back.
4451    ///
4452    /// The empty reply is the point. Redis answers nothing at all here and the
4453    /// client sees the socket close, and an `OK` would be a promise from a
4454    /// process that is about to not exist.
4455    #[test]
4456    fn shutdown_writes_nothing_and_sets_the_flag() {
4457        let mut f = Fixture::new();
4458        assert!(!f.server.stopping(), "nobody has asked yet");
4459
4460        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4461        assert_eq!(reply, "");
4462        assert_eq!(flow, Flow::Close);
4463        assert!(f.server.stopping());
4464    }
4465
4466    /// Every flag combination 8.10.1 takes, and every one it refuses.
4467    ///
4468    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
4469    /// contradict each other, `ABORT` says to do nothing so it cannot be
4470    /// combined with a word about how to do it, and repeating any one of them
4471    /// is fine. All of it was read off a running 8.10.1 rather than worked out
4472    /// from the documentation, which does not say.
4473    #[test]
4474    fn shutdown_takes_the_flags_redis_takes() {
4475        for flags in [
4476            &[b"NOSAVE".as_slice()][..],
4477            &[b"SAVE"],
4478            &[b"NOW"],
4479            &[b"FORCE"],
4480            &[b"nosave"],
4481            &[b"NOW", b"NOW"],
4482            &[b"SAVE", b"SAVE"],
4483            &[b"NOSAVE", b"NOW", b"FORCE"],
4484        ] {
4485            let mut f = Fixture::new();
4486            let mut parts = vec![b"SHUTDOWN".as_slice()];
4487            parts.extend_from_slice(flags);
4488            let (flow, reply) = f.flow(&parts);
4489            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4490            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4491            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4492        }
4493
4494        for flags in [
4495            &[b"BOGUS".as_slice()][..],
4496            &[b"SAVE", b"NOSAVE"],
4497            &[b"NOSAVE", b"SAVE"],
4498            &[b"ABORT", b"NOW"],
4499            &[b"NOSAVE", b"ABORT"],
4500            &[b"NOW", b"FORCE", b"ABORT"],
4501        ] {
4502            let mut f = Fixture::new();
4503            let mut parts = vec![b"SHUTDOWN".as_slice()];
4504            parts.extend_from_slice(flags);
4505            assert_eq!(
4506                f.run(&parts),
4507                "-ERR syntax error\r\n",
4508                "SHUTDOWN {flags:?} was accepted"
4509            );
4510            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4511        }
4512    }
4513
4514    /// `ABORT` has nothing to call off, ever.
4515    ///
4516    /// A shutdown here is decided and done inside one turn of the loop, so
4517    /// there is no window in which one is in progress. That makes Redis's
4518    /// message for a cancel with nothing to cancel the right answer every time
4519    /// rather than only when nothing happens to be pending. Two `ABORT`s is
4520    /// still one `ABORT`, which is what 8.10.1 does.
4521    #[test]
4522    fn shutdown_abort_never_has_anything_to_abort() {
4523        let mut f = Fixture::new();
4524        for parts in [
4525            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4526            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4527        ] {
4528            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4529            assert!(!f.server.stopping(), "an abort stopped the server");
4530        }
4531    }
4532
4533    /// A fixture whose server writes into a directory of its own.
4534    ///
4535    /// Every test here really writes files, because the whole point of the
4536    /// command is the files and a backup that is only a state machine would
4537    /// pass a test suite and fail the first person who tried to restore one.
4538    /// The directory carries the test's name so that the suite can run its
4539    /// tests in parallel the way it always does.
4540    struct Backups {
4541        f: Fixture,
4542        dir: PathBuf,
4543    }
4544
4545    impl Backups {
4546        fn new(name: &str) -> Backups {
4547            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4548            let _ = std::fs::remove_dir_all(&dir);
4549            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4550            let mut f = Fixture::new();
4551            f.server.set_dir(dir.clone());
4552            Backups { f, dir }
4553        }
4554
4555        fn run(&mut self, parts: &[&[u8]]) -> String {
4556            self.f.run(parts)
4557        }
4558
4559        /// The names in `backupdir`, sorted, so a test can say what is on disk.
4560        fn files(&self) -> Vec<String> {
4561            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4562                Ok(entries) => entries
4563                    .filter_map(|e| e.ok())
4564                    .map(|e| e.file_name().to_string_lossy().into_owned())
4565                    .collect(),
4566                Err(_) => Vec::new(),
4567            };
4568            names.sort();
4569            names
4570        }
4571
4572        fn read(&self, name: &str) -> Vec<u8> {
4573            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4574        }
4575    }
4576
4577    impl Drop for Backups {
4578        fn drop(&mut self) {
4579            let _ = std::fs::remove_dir_all(&self.dir);
4580        }
4581    }
4582
4583    /// The four states and the moves between them, in the order a client walks
4584    /// them, with the files checked at every step.
4585    #[test]
4586    fn backup_walks_the_states_the_reference_walks() {
4587        let mut b = Backups::new("states");
4588        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4589
4590        assert!(status(&mut b).contains("idle"));
4591        assert!(b.files().is_empty(), "an idle server has written a backup");
4592
4593        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4594        assert!(status(&mut b).contains("incrementing"));
4595        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4596
4597        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4598        assert!(status(&mut b).contains("sealed"));
4599        assert_eq!(
4600            b.files(),
4601            [
4602                "appendonly.aof.1.base.rdb",
4603                "appendonly.aof.1.incr.aof",
4604                "appendonly.aof.manifest",
4605            ]
4606        );
4607
4608        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4609        assert!(status(&mut b).contains("idle"));
4610        assert!(b.files().is_empty(), "cleanup left something behind");
4611    }
4612
4613    /// Every move that is refused, in the reference's words.
4614    #[test]
4615    fn backup_refuses_the_moves_the_reference_refuses() {
4616        let mut b = Backups::new("refusals");
4617
4618        assert_eq!(
4619            b.run(&[b"BACKUP", b"SEAL"]),
4620            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4621        );
4622        assert_eq!(
4623            b.run(&[b"BACKUP", b"ABORT"]),
4624            "-ERR No backup in progress\r\n"
4625        );
4626        // Cleanup from idle is not an error, it is a way of saying there was
4627        // nothing to clean up.
4628        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4629
4630        b.run(&[b"BACKUP", b"START"]);
4631        assert_eq!(
4632            b.run(&[b"BACKUP", b"START"]),
4633            "-ERR A backup is already in progress, ABORT it first\r\n"
4634        );
4635        assert_eq!(
4636            b.run(&[b"BACKUP", b"CLEANUP"]),
4637            "-ERR Backup is in progress\r\n"
4638        );
4639
4640        b.run(&[b"BACKUP", b"SEAL"]);
4641        assert_eq!(
4642            b.run(&[b"BACKUP", b"START"]),
4643            "-ERR A sealed backup exists, CLEANUP it first\r\n"
4644        );
4645        assert_eq!(
4646            b.run(&[b"BACKUP", b"SEAL"]),
4647            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4648        );
4649        assert_eq!(
4650            b.run(&[b"BACKUP", b"ABORT"]),
4651            "-ERR No backup in progress\r\n"
4652        );
4653    }
4654
4655    /// An abort takes the base file away and leaves a state saying who did it.
4656    ///
4657    /// The next backup takes the next sequence number rather than reusing the
4658    /// one whose files were just thrown away, so a directory somebody copied a
4659    /// half finished backup out of cannot end up with two different files under
4660    /// one name.
4661    #[test]
4662    fn backup_abort_removes_the_file_and_says_who_did_it() {
4663        let mut b = Backups::new("abort");
4664        b.run(&[b"BACKUP", b"START"]);
4665        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4666
4667        let status = b.run(&[b"BACKUP", b"STATUS"]);
4668        assert!(status.contains("failed"), "{status}");
4669        assert!(status.contains("aborted by user"), "{status}");
4670        assert!(b.files().is_empty(), "abort left the base file behind");
4671        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4672
4673        // A start from failed works, and is the second backup.
4674        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4675        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4676        let status = b.run(&[b"BACKUP", b"STATUS"]);
4677        assert!(status.contains("incrementing"), "{status}");
4678        assert!(!status.contains("aborted"), "the old error was kept");
4679    }
4680
4681    /// `LIST` names nothing, then one file, then three, and they are absolute.
4682    #[test]
4683    fn backup_list_names_the_files_that_are_pinned_so_far() {
4684        let mut b = Backups::new("list");
4685        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4686
4687        b.run(&[b"BACKUP", b"START"]);
4688        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4689        let base = base.to_string_lossy().into_owned();
4690        assert_eq!(
4691            b.run(&[b"BACKUP", b"LIST"]),
4692            format!("*1\r\n${}\r\n{base}\r\n", base.len())
4693        );
4694
4695        b.run(&[b"BACKUP", b"SEAL"]);
4696        let listed = b.run(&[b"BACKUP", b"LIST"]);
4697        assert!(listed.starts_with("*3\r\n"), "{listed}");
4698        // The order is the manifest's order, base then incremental then the
4699        // manifest itself, which is the order a restore needs them in.
4700        let names: Vec<&str> = listed
4701            .lines()
4702            .filter(|l| l.starts_with('/') || l.contains(":\\"))
4703            .collect();
4704        assert_eq!(names.len(), 3, "{listed}");
4705        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4706        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4707        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4708    }
4709
4710    /// The base file is the dataset as it was at `START` and not at `SEAL`.
4711    ///
4712    /// That is D-46 and it is the one thing about this a client can notice, so
4713    /// it is pinned here rather than left to be discovered by whoever restores
4714    /// one. The incremental file is empty for the same reason: there is no
4715    /// append only log underneath this server to copy the writes in between out
4716    /// of.
4717    #[test]
4718    fn a_backup_holds_the_dataset_as_it_was_at_start() {
4719        let mut b = Backups::new("contents");
4720        b.run(&[b"SET", b"bk", b"v1"]);
4721        b.run(&[b"BACKUP", b"START"]);
4722        b.run(&[b"SET", b"bk", b"v2"]);
4723        b.run(&[b"BACKUP", b"SEAL"]);
4724
4725        let base = b.read("appendonly.aof.1.base.rdb");
4726        assert!(base.starts_with(b"REDIS"), "not an RDB file");
4727        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4728        assert!(
4729            !base.windows(2).any(|w| w == b"v2"),
4730            "the base file moved on after START"
4731        );
4732        // The aux field a loader acts on, and the one that says this file is
4733        // the base of an append only file rather than a standalone dump. Its
4734        // value is the one byte string 1, which the encoder writes as an
4735        // integer the way a real server writes it.
4736        let at = base
4737            .windows(8)
4738            .position(|w| w == b"aof-base")
4739            .expect("no aof-base aux field");
4740        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4741
4742        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4743        assert_eq!(
4744            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4745            "file appendonly.aof.1.base.rdb seq 1 type b\n\
4746             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4747        );
4748    }
4749
4750    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
4751    /// RESP2, which is what every other map shaped reply in this server does.
4752    #[test]
4753    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4754        let mut b = Backups::new("status");
4755        b.f.server.set_clock_ms(1_700_000_000_000);
4756
4757        assert_eq!(
4758            b.run(&[b"BACKUP", b"STATUS"]),
4759            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4760             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4761        );
4762
4763        b.f.out = Out::new(Proto::Resp3);
4764        b.run(&[b"BACKUP", b"START"]);
4765        assert_eq!(
4766            b.run(&[b"BACKUP", b"STATUS"]),
4767            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4768             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4769        );
4770
4771        b.run(&[b"BACKUP", b"SEAL"]);
4772        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4773        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4774    }
4775
4776    /// A sealed backup that nobody cleans up goes away on its own once
4777    /// `backup-sealed-ttl` seconds have passed since the seal.
4778    #[test]
4779    fn a_sealed_backup_is_swept_away_after_the_timeout() {
4780        let mut b = Backups::new("ttl");
4781        b.f.server.set_clock_ms(1_000_000);
4782        assert_eq!(
4783            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4784            "+OK\r\n"
4785        );
4786        b.run(&[b"BACKUP", b"START"]);
4787        b.run(&[b"BACKUP", b"SEAL"]);
4788
4789        // A minute short of the deadline, nothing happens.
4790        b.f.server.set_clock_ms(1_000_000 + 59_000);
4791        b.f.server.backup_expire();
4792        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4793        assert_eq!(b.files().len(), 3);
4794
4795        b.f.server.set_clock_ms(1_000_000 + 60_000);
4796        b.f.server.backup_expire();
4797        let status = b.run(&[b"BACKUP", b"STATUS"]);
4798        assert!(status.contains("idle"), "{status}");
4799        assert!(b.files().is_empty(), "the timeout left the files behind");
4800
4801        // Zero is the default and means a sealed backup is kept for ever.
4802        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4803        b.run(&[b"BACKUP", b"START"]);
4804        b.run(&[b"BACKUP", b"SEAL"]);
4805        b.f.server.set_clock_ms(9_000_000_000);
4806        b.f.server.backup_expire();
4807        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4808    }
4809
4810    /// The three settings around the command, read and written the way 8.10.1
4811    /// reads and writes them.
4812    #[test]
4813    fn the_backup_settings_behave_the_way_the_reference_does() {
4814        let mut b = Backups::new("config");
4815        let dir = b.dir.to_string_lossy().into_owned();
4816
4817        assert_eq!(
4818            b.run(&[b"CONFIG", b"GET", b"dir"]),
4819            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4820        );
4821        assert_eq!(
4822            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4823            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4824        );
4825        assert_eq!(
4826            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4827            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4828        );
4829
4830        // `dir` is a protected config, so it is refused even for the value it
4831        // already holds, and `backupdirname` is immutable.
4832        assert_eq!(
4833            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4834            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4835        );
4836        assert_eq!(
4837            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4838            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4839        );
4840        assert!(
4841            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4842                .contains("argument couldn't be parsed into an integer")
4843        );
4844        assert!(
4845            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4846                .contains("argument must be between 0 and 9223372036854775807 inclusive")
4847        );
4848    }
4849
4850    /// The help text, which has `HELP` in it twice because the reference's does.
4851    #[test]
4852    fn backup_help_is_the_text_the_reference_sends() {
4853        let mut f = Fixture::new();
4854        let help = f.run(&[b"BACKUP", b"HELP"]);
4855        assert!(help.starts_with("*17\r\n"), "{help}");
4856        assert!(
4857            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4858        );
4859        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
4860        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
4861        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
4862    }
4863
4864    /// What a mistyped `BACKUP` gets told.
4865    ///
4866    /// The arity error names `backup` where the reference names `backup|start`,
4867    /// which is D-46: the table reports one arity for the container the way the
4868    /// reference does, and the per subcommand table that would carry the better
4869    /// name is not built yet. Every subcommand is exactly two words, so nothing
4870    /// legal is refused by it.
4871    #[test]
4872    fn backup_refuses_what_it_cannot_read() {
4873        let mut f = Fixture::new();
4874        assert_eq!(
4875            f.run(&[b"BACKUP"]),
4876            "-ERR wrong number of arguments for 'backup' command\r\n"
4877        );
4878        assert_eq!(
4879            f.run(&[b"BACKUP", b"START", b"x"]),
4880            "-ERR wrong number of arguments for 'backup' command\r\n"
4881        );
4882        assert_eq!(
4883            f.run(&[b"BACKUP", b"NOPE"]),
4884            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4885        );
4886    }
4887
4888    #[test]
4889    fn the_command_counter_counts_every_command_including_the_bad_ones() {
4890        let mut f = Fixture::new();
4891        f.run(&[b"PING"]);
4892        f.run(&[b"NOPE"]);
4893        f.run(&[b"GET"]);
4894        assert_eq!(f.server.totals().commands, 3);
4895    }
4896
4897    #[test]
4898    fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
4899        let mut server = Server::new();
4900        server.set_threads(2);
4901        // A fresh server has every database marked, so start from nothing to
4902        // see the one mark arrive.
4903        server.dirty = 0;
4904        server.locals[1].mark(1 << 9);
4905        server.collect_marks();
4906        assert_ne!(server.dirty & (1 << 9), 0);
4907        // And taken once rather than left to be taken again next turn.
4908        assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
4909    }
4910
4911    #[test]
4912    fn what_two_threads_counted_is_added_up_when_info_asks() {
4913        let mut server = Server::new();
4914        server.set_threads(2);
4915        // Written into the two sets by hand, because what is under test is the
4916        // adding up and not the claiming, and one test thread can only ever
4917        // claim one set.
4918        let ping = lookup(b"PING").expect("PING is a command");
4919        for (at, calls) in [(0, 2), (1, 3)] {
4920            let counters = &server.locals[at];
4921            for _ in 0..calls {
4922                counters.stats.commands.bump();
4923                counters.cmdstats.at(ping).calls.bump();
4924            }
4925            counters.stats.opened();
4926        }
4927        assert_eq!(server.totals().commands, 5);
4928        assert_eq!(server.totals().clients, 2);
4929        assert_eq!(server.totals().connections, 2);
4930        let rows: Vec<_> = server.command_stats().collect();
4931        assert_eq!(rows.len(), 1);
4932        assert_eq!(rows[0].0, "ping");
4933        assert_eq!(rows[0].1.calls, 5);
4934        // A reset takes the totals and leaves the open connections, which are
4935        // still open.
4936        server.reset_stats();
4937        assert_eq!(server.totals().commands, 0);
4938        assert_eq!(server.totals().connections, 0);
4939        assert_eq!(server.totals().clients, 2);
4940    }
4941
4942    #[test]
4943    fn the_parked_count_says_what_the_waiter_list_says() {
4944        let mut f = Fixture::new();
4945        assert_eq!(f.server.parked(), 0);
4946        for client in 1..=3u64 {
4947            f.session = Session::new(client);
4948            assert_eq!(f.flow(&[b"BLPOP", b"q", b"0"]).0, Flow::Block);
4949        }
4950        assert_eq!(f.server.parked(), 3);
4951        assert_eq!(f.server.waiters().len(), 3);
4952
4953        // The three ways the list gets shorter, each of which has to move the
4954        // number with it, because a number left behind is either a walk of the
4955        // list that never happens or one that runs off the end of it.
4956        f.server.drop_waiter(1);
4957        assert_eq!(f.server.parked(), f.server.waiters().len());
4958        f.server.forget_waiters(1);
4959        assert_eq!(f.server.parked(), f.server.waiters().len());
4960        f.run(&[b"RPUSH", b"q", b"v"]);
4961        let mut out = Out::new(Proto::Resp2);
4962        assert!(f.server.serve_waiter(0, 0, &mut out));
4963        f.server.drop_waiter(0);
4964        assert_eq!(f.server.parked(), 0);
4965        assert!(f.server.waiters().is_empty());
4966    }
4967
4968    #[test]
4969    fn a_set_goes_from_bytes_to_bytes() {
4970        let mut f = Fixture::new();
4971        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4972        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4973        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4974        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4975        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4976        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4977        assert_eq!(
4978            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4979            "*3\r\n:1\r\n:0\r\n:1\r\n"
4980        );
4981        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4982        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4983    }
4984
4985    #[test]
4986    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4987        let mut f = Fixture::new();
4988        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4989        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4990        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4991        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4992        assert_eq!(
4993            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4994            "*2\r\n:0\r\n:0\r\n"
4995        );
4996        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4997    }
4998
4999    #[test]
5000    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
5001        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
5002        // and one that gets a `*` hands it a list, without either of them being
5003        // told which command was sent.
5004        let mut f = Fixture::new();
5005        f.run(&[b"SADD", b"s", b"one"]);
5006        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
5007
5008        f.run(&[b"HELLO", b"3"]);
5009        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
5010    }
5011
5012    #[test]
5013    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
5014        // An intset holds the number, so these digits exist for the first time
5015        // in the reply buffer.
5016        let mut f = Fixture::new();
5017        f.run(&[b"SADD", b"s", b"42"]);
5018        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
5019        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
5020        assert_eq!(
5021            f.run(&[b"SISMEMBER", b"s", b"042"]),
5022            ":0\r\n",
5023            "the member is the bytes and not the number they parse to"
5024        );
5025    }
5026
5027    #[test]
5028    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
5029        let mut f = Fixture::new();
5030        f.run(&[b"SET", b"str", b"v"]);
5031        f.run(&[b"SADD", b"set", b"a"]);
5032
5033        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5034        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
5035        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
5036        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
5037        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
5038        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
5039        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
5040        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
5041        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
5042
5043        // MGET is the one that does not, because Redis gives nil for the odd
5044        // key out rather than failing the good keys next to it.
5045        assert_eq!(
5046            f.run(&[b"MGET", b"str", b"set", b"nope"]),
5047            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
5048        );
5049        // And plain SET overwrites any type, which takes the body with it.
5050        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
5051        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
5052    }
5053
5054    #[test]
5055    fn a_wrongtype_leaves_nothing_half_written() {
5056        // SMISMEMBER writes an array header and then one reply per member, so
5057        // it is the first command in the server that could get a header out in
5058        // front of an error if it checked its key in the wrong order.
5059        let mut f = Fixture::new();
5060        f.run(&[b"SET", b"k", b"v"]);
5061        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
5062        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
5063        assert!(!reply.contains('*'), "an array header went out in front");
5064    }
5065
5066    #[test]
5067    fn emptying_a_set_takes_the_key_with_it() {
5068        let mut f = Fixture::new();
5069        f.run(&[b"SADD", b"s", b"a", b"b"]);
5070        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
5071        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
5072        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5073        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
5074        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5075    }
5076
5077    /// Pull the cursor and the members out of one `SSCAN` reply.
5078    ///
5079    /// Crude on purpose. A test that walked a set through a real client would
5080    /// be testing the client, and what these tests are about is the shape of
5081    /// the bytes and the fact that a walk sees every member once.
5082    fn split_scan(reply: &str) -> (String, Vec<String>) {
5083        let mut lines = reply.split("\r\n");
5084        assert_eq!(lines.next(), Some("*2"), "got {reply}");
5085        lines.next().expect("the cursor header");
5086        let cursor = lines.next().expect("the cursor").to_owned();
5087        let header = lines.next().expect("the member header");
5088        let n: usize = header[1..].parse().expect("a member count");
5089        let mut members = Vec::with_capacity(n);
5090        for _ in 0..n {
5091            lines.next().expect("a member header");
5092            members.push(lines.next().expect("a member").to_owned());
5093        }
5094        (cursor, members)
5095    }
5096
5097    #[test]
5098    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
5099        let mut f = Fixture::new();
5100        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
5101
5102        let one = f.run(&[b"SPOP", b"s"]);
5103        assert!(
5104            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
5105            "got {one}"
5106        );
5107        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
5108
5109        // A count takes that many, and the last one takes the key with it.
5110        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
5111        assert!(rest.starts_with("*3\r\n"), "got {rest}");
5112        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5113        // And a pop at a key that is not there is a nil, not an empty bulk.
5114        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
5115        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
5116    }
5117
5118    #[test]
5119    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
5120        // The one place in the server where the reply type carries something
5121        // the command name does not. SPOP's members are distinct so a RESP3
5122        // client can build a set out of them. SRANDMEMBER with a negative count
5123        // can hand back the same member three times, and a set would lose two.
5124        let mut f = Fixture::new();
5125        f.run(&[b"HELLO", b"3"]);
5126        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
5127
5128        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
5129        // And a positive count is an array too, since Redis makes it one.
5130        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
5131
5132        // A negative count against a set of one is where the difference bites:
5133        // the same member three times, which is a three element reply and would
5134        // have been a one element reply if it had gone out as a set.
5135        f.run(&[b"SADD", b"one", b"z"]);
5136        assert_eq!(
5137            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
5138            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
5139        );
5140    }
5141
5142    #[test]
5143    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
5144        let mut f = Fixture::new();
5145        f.run(&[b"SADD", b"s", b"only"]);
5146        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5147        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5148        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
5149
5150        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
5151        // The count form answers an empty array rather than a nil, which is the
5152        // pair of answers Redis gives and is not the pair it looks like.
5153        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
5154        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
5155        // Asking for more than is there answers all of it once and not padding.
5156        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
5157    }
5158
5159    #[test]
5160    fn a_pop_count_that_is_not_a_positive_number_says_so() {
5161        let mut f = Fixture::new();
5162        f.run(&[b"SADD", b"s", b"a"]);
5163        let bad = "-ERR value is out of range, must be positive\r\n";
5164        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
5165        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
5166        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
5167        // Zero is allowed and is a real answer rather than an error.
5168        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
5169        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
5170    }
5171
5172    #[test]
5173    fn a_scan_walks_a_set_of_any_size_exactly_once() {
5174        let mut f = Fixture::new();
5175        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
5176        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
5177            .into_iter()
5178            .chain(members.iter().map(Vec::as_slice))
5179            .collect();
5180        f.run(&args);
5181
5182        let mut seen = Vec::new();
5183        let mut cursor = "0".to_owned();
5184        loop {
5185            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
5186            let (next, got) = split_scan(&reply);
5187            seen.extend(got);
5188            cursor = next;
5189            if cursor == "0" {
5190                break;
5191            }
5192        }
5193        seen.sort();
5194        seen.dedup();
5195        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
5196
5197        // A set small enough to be a listpack answers in one call whatever
5198        // cursor it was handed, which is what Redis does for that encoding.
5199        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
5200        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
5201        assert_eq!(cursor, "0");
5202        assert_eq!(got.len(), 3);
5203        // And a key that is not there is a finished scan of nothing.
5204        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
5205    }
5206
5207    #[test]
5208    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
5209        let mut f = Fixture::new();
5210        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
5211
5212        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
5213        let mut got = got;
5214        got.sort();
5215        assert_eq!(got, ["aa", "ab"]);
5216
5217        // An integer member has no digits stored anywhere, so MATCH is the one
5218        // place a scan pays to write some.
5219        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
5220        let mut got = got;
5221        got.sort();
5222        assert_eq!(got, ["12", "13"]);
5223
5224        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
5225        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
5226        assert_eq!(
5227            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
5228            "-ERR syntax error\r\n"
5229        );
5230        // A count under one is a syntax error and not a range error, which is
5231        // the odder of Redis's two answers and the reason it is copied exactly.
5232        assert_eq!(
5233            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
5234            "-ERR syntax error\r\n"
5235        );
5236    }
5237
5238    #[test]
5239    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
5240        let mut f = Fixture::new();
5241        f.run(&[b"SADD", b"src", b"a", b"b"]);
5242        f.run(&[b"SADD", b"dst", b"c"]);
5243
5244        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
5245        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
5246        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
5247        // A member that is not in the source is a zero and moves nothing.
5248        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
5249        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
5250
5251        // A destination that does not exist gets made, and a source that runs
5252        // out goes away.
5253        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
5254        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
5255        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
5256    }
5257
5258    #[test]
5259    fn moving_checks_the_types_in_the_order_redis_checks_them() {
5260        // Not the order it looks like it should be. A source that is not there
5261        // answers zero without ever looking at the destination, so this is a
5262        // zero and not a WRONGTYPE even though the destination is a string.
5263        let mut f = Fixture::new();
5264        f.run(&[b"SET", b"str", b"v"]);
5265        f.run(&[b"SADD", b"set", b"a"]);
5266
5267        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5268        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
5269        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
5270        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
5271        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
5272        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
5273        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
5274        assert_eq!(
5275            f.run(&[b"SISMEMBER", b"set", b"a"]),
5276            ":1\r\n",
5277            "and none of that moved anything"
5278        );
5279    }
5280
5281    #[test]
5282    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5283        // SSCAN writes an outer array header before it walks, so it is the
5284        // command most likely to get bytes out in front of an error.
5285        let mut f = Fixture::new();
5286        f.run(&[b"SADD", b"s", b"a"]);
5287        for bad in [
5288            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
5289            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
5290            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
5291        ] {
5292            let reply = f.run(bad);
5293            assert!(reply.starts_with("-ERR"), "got {reply}");
5294            assert!(!reply.contains('*'), "an array header went out in front");
5295        }
5296    }
5297
5298    #[test]
5299    fn a_hash_writes_reads_and_deletes_its_fields() {
5300        let mut f = Fixture::new();
5301        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
5302        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
5303        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5304        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
5305        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
5306        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
5307        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
5308        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
5309        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
5310        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
5311
5312        // The value the client sent is `9`, so HGET h b must not find the `2`
5313        // that is a value. A search with a step of one would have.
5314        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
5315
5316        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
5317        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
5318        assert_eq!(
5319            f.run(&[b"EXISTS", b"h"]),
5320            ":0\r\n",
5321            "and losing the last field lost the key"
5322        );
5323    }
5324
5325    #[test]
5326    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
5327        let mut f = Fixture::new();
5328        f.run(&[b"HSET", b"h", b"a", b"1"]);
5329        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5330        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
5331        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
5332        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
5333        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
5334
5335        f.run(&[b"HELLO", b"3"]);
5336        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
5337        assert_eq!(
5338            f.run(&[b"HGETALL", b"nokey"]),
5339            "%0\r\n",
5340            "a missing key is the empty hash and never a nil"
5341        );
5342        assert_eq!(
5343            f.run(&[b"HKEYS", b"h"]),
5344            "*1\r\n$1\r\na\r\n",
5345            "and the two that answer one side stay arrays"
5346        );
5347    }
5348
5349    #[test]
5350    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
5351        let mut f = Fixture::new();
5352        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
5353        assert_eq!(
5354            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
5355            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
5356            "the reply is positional, so b is a nil and not a gap"
5357        );
5358        assert_eq!(
5359            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
5360            "*2\r\n$-1\r\n$-1\r\n",
5361            "and a missing key is all nils rather than an empty array"
5362        );
5363
5364        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
5365        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
5366        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5367    }
5368
5369    #[test]
5370    fn a_hash_counts_up_and_says_so_when_it_cannot() {
5371        let mut f = Fixture::new();
5372        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
5373        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
5374        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
5375        assert_eq!(
5376            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
5377            "$4\r\n10.5\r\n",
5378            "a bulk string and not a double, on both protocols"
5379        );
5380
5381        f.run(&[b"HSET", b"h", b"s", b"words"]);
5382        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
5383        assert!(
5384            bad.starts_with("-ERR hash value is not an integer"),
5385            "{bad}"
5386        );
5387        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
5388        assert!(
5389            bad.starts_with("-ERR value is not an integer"),
5390            "a bad argument is not yet a hash value, {bad}"
5391        );
5392        assert_eq!(
5393            f.run(&[b"HGET", b"h", b"s"]),
5394            "$5\r\nwords\r\n",
5395            "and neither of them wrote anything"
5396        );
5397    }
5398
5399    #[test]
5400    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
5401        let mut f = Fixture::new();
5402        for i in 0..500 {
5403            let field = format!("field-{i}");
5404            let value = format!("value-{i}");
5405            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
5406        }
5407
5408        let mut seen: Vec<String> = Vec::new();
5409        let mut cursor = "0".to_owned();
5410        loop {
5411            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
5412            let (next, items) = scan_reply(&reply);
5413            assert_eq!(items.len() % 2, 0, "a pair went out half written");
5414            for pair in items.chunks(2) {
5415                assert_eq!(
5416                    pair[0].strip_prefix("field-"),
5417                    pair[1].strip_prefix("value-"),
5418                    "a field came back with someone else's value"
5419                );
5420                seen.push(pair[0].clone());
5421            }
5422            cursor = next;
5423            if cursor == "0" {
5424                break;
5425            }
5426        }
5427        seen.sort();
5428        seen.dedup();
5429        assert_eq!(seen.len(), 500, "every field once and only once");
5430
5431        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
5432        assert!(
5433            items.iter().all(|s| s.starts_with("field-")),
5434            "NOVALUES still sent the values"
5435        );
5436
5437        let (_, one) = scan_reply(&f.run(&[
5438            b"HSCAN",
5439            b"h",
5440            b"0",
5441            b"MATCH",
5442            b"field-499",
5443            b"COUNT",
5444            b"1000",
5445        ]));
5446        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
5447    }
5448
5449    #[test]
5450    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
5451        let mut f = Fixture::new();
5452        f.run(&[b"HSET", b"h", b"a", b"1"]);
5453        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
5454        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
5455        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
5456        assert_eq!(
5457            f.run(&[b"HRANDFIELD", b"h", b"3"]),
5458            "*1\r\n$1\r\na\r\n",
5459            "a positive count is capped at the size of the hash"
5460        );
5461        assert_eq!(
5462            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
5463            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
5464            "and a negative one repeats itself"
5465        );
5466        assert_eq!(
5467            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5468            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5469            "flat on RESP2"
5470        );
5471
5472        f.run(&[b"HELLO", b"3"]);
5473        assert_eq!(
5474            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5475            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5476            "and nested on RESP3, but still an array and never a map"
5477        );
5478    }
5479
5480    #[test]
5481    fn every_hash_command_says_wrongtype_and_writes_nothing() {
5482        let mut f = Fixture::new();
5483        f.run(&[b"SET", b"str", b"v"]);
5484        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5485
5486        for cmd in [
5487            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5488            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5489            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5490            &[b"HGET".as_slice(), b"str", b"f"][..],
5491            &[b"HMGET".as_slice(), b"str", b"f"][..],
5492            &[b"HDEL".as_slice(), b"str", b"f"][..],
5493            &[b"HLEN".as_slice(), b"str"][..],
5494            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5495            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5496            &[b"HGETALL".as_slice(), b"str"][..],
5497            &[b"HKEYS".as_slice(), b"str"][..],
5498            &[b"HVALS".as_slice(), b"str"][..],
5499            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5500            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5501            &[b"HRANDFIELD".as_slice(), b"str"][..],
5502            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5503            &[b"HSCAN".as_slice(), b"str", b"0"][..],
5504        ] {
5505            let reply = f.run(cmd);
5506            assert_eq!(reply, wrong, "{:?}", cmd[0]);
5507        }
5508        assert_eq!(
5509            f.run(&[b"GET", b"str"]),
5510            "$1\r\nv\r\n",
5511            "and none of them touched the value"
5512        );
5513    }
5514
5515    #[test]
5516    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5517        let mut f = Fixture::new();
5518        f.run(&[b"HSET", b"h", b"f", b"v"]);
5519        for bad in [
5520            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5521            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5522            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5523            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5524        ] {
5525            let reply = f.run(bad);
5526            assert!(reply.starts_with("-ERR"), "got {reply}");
5527            assert!(!reply.contains('*'), "an array header went out in front");
5528        }
5529    }
5530
5531    #[test]
5532    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5533        let mut f = Fixture::new();
5534        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5535        assert_eq!(
5536            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5537            "*1\r\n:1\r\n"
5538        );
5539        assert_eq!(
5540            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5541            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5542            "one answer per field, and the two sentinels are TTL's own"
5543        );
5544
5545        // The same deadline in the other three units, all of them derived from
5546        // the one number the store kept.
5547        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5548        assert!((99_000..=100_000).contains(&ms), "got {ms}");
5549        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5550        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5551        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5552        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5553
5554        assert_eq!(
5555            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5556            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5557            "one for the deadline taken off, and it does not say what it was"
5558        );
5559        assert_eq!(
5560            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5561            "*1\r\n:-1\r\n"
5562        );
5563        assert_eq!(
5564            f.run(&[b"HGET", b"h", b"a"]),
5565            "$1\r\n1\r\n",
5566            "and the field is still there with the value it had"
5567        );
5568    }
5569
5570    #[test]
5571    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5572        let mut f = Fixture::new();
5573        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5574        assert_eq!(
5575            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5576            "*1\r\n:2\r\n",
5577            "two, and not one, because nothing was stored"
5578        );
5579        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5580        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5581
5582        assert_eq!(
5583            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5584            "*1\r\n:2\r\n"
5585        );
5586        assert_eq!(
5587            f.run(&[b"EXISTS", b"h"]),
5588            ":0\r\n",
5589            "and the last field going took the key with it"
5590        );
5591
5592        // Zero is a delete and not an error, where minus one is an error. That
5593        // is Redis's split and it is easy to get backwards.
5594        f.run(&[b"HSET", b"h", b"a", b"1"]);
5595        assert_eq!(
5596            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5597            "*1\r\n:2\r\n"
5598        );
5599    }
5600
5601    #[test]
5602    fn a_field_is_gone_once_its_moment_passes() {
5603        let mut f = Fixture::new();
5604        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5605        assert_eq!(
5606            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5607            "*1\r\n:1\r\n"
5608        );
5609        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5610
5611        // Time moves once per turn of the event loop and nowhere else, so a
5612        // test moves it by hand rather than by sleeping. There is nothing to
5613        // sleep for: the deadline is a number and so is the clock.
5614        f.server.advance_clock_ms(60);
5615        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5616        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5617        assert_eq!(
5618            f.run(&[b"HGETALL", b"h"]),
5619            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5620            "and the walks do not hand back a field that has expired"
5621        );
5622    }
5623
5624    #[test]
5625    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5626        let mut f = Fixture::new();
5627        for cmd in [
5628            &[
5629                b"HEXPIRE".as_slice(),
5630                b"nokey",
5631                b"100",
5632                b"FIELDS",
5633                b"2",
5634                b"a",
5635                b"b",
5636            ][..],
5637            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5638            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5639            &[
5640                b"HEXPIRETIME".as_slice(),
5641                b"nokey",
5642                b"FIELDS",
5643                b"2",
5644                b"a",
5645                b"b",
5646            ][..],
5647            &[
5648                b"HPERSIST".as_slice(),
5649                b"nokey",
5650                b"FIELDS",
5651                b"2",
5652                b"a",
5653                b"b",
5654            ][..],
5655        ] {
5656            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5657        }
5658    }
5659
5660    #[test]
5661    fn writing_a_field_clears_the_deadline_that_was_on_it() {
5662        let mut f = Fixture::new();
5663        f.run(&[b"HSET", b"h", b"a", b"1"]);
5664        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5665        f.run(&[b"HSET", b"h", b"a", b"2"]);
5666        assert_eq!(
5667            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5668            "*1\r\n:-1\r\n",
5669            "Redis has done this since 7.4, and it is why HGETEX exists"
5670        );
5671    }
5672
5673    #[test]
5674    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5675        let mut f = Fixture::new();
5676        f.run(&[b"HSET", b"h", b"a", b"1"]);
5677        assert_eq!(
5678            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5679            "*1\r\n:0\r\n",
5680            "XX on a field with no deadline changes nothing"
5681        );
5682        assert_eq!(
5683            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5684            "*1\r\n:1\r\n"
5685        );
5686        assert_eq!(
5687            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5688            "*1\r\n:0\r\n",
5689            "and NX will not move one that is already there"
5690        );
5691        assert_eq!(
5692            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5693            "*1\r\n:0\r\n"
5694        );
5695        assert_eq!(
5696            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5697            "*1\r\n:1\r\n"
5698        );
5699        assert_eq!(
5700            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5701            "*1\r\n:1\r\n"
5702        );
5703        assert_eq!(
5704            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5705            "*1\r\n:50\r\n"
5706        );
5707    }
5708
5709    #[test]
5710    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5711        let mut f = Fixture::new();
5712        f.run(&[b"HSET", b"h", b"a", b"1"]);
5713        for (bad, want) in [
5714            (
5715                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5716                "-ERR invalid expire time, must be >= 0",
5717            ),
5718            (
5719                &[
5720                    b"HEXPIRE".as_slice(),
5721                    b"h",
5722                    b"9999999999999999",
5723                    b"FIELDS",
5724                    b"1",
5725                    b"a",
5726                ][..],
5727                "-ERR invalid expire time in 'hexpire' command",
5728            ),
5729            (
5730                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5731                "-ERR wrong number of arguments for 'hexpire' command",
5732            ),
5733            (
5734                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5735                "-ERR Parameter `numFields` should be greater than 0",
5736            ),
5737            (
5738                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5739                "-ERR wrong number of arguments",
5740            ),
5741            (
5742                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5743                "-ERR wrong number of arguments",
5744            ),
5745        ] {
5746            let reply = f.run(bad);
5747            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5748            assert!(!reply.contains('*'), "an array header went out in front");
5749        }
5750        assert_eq!(
5751            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5752            "*1\r\n:-1\r\n",
5753            "and not one of them put a deadline on anything"
5754        );
5755    }
5756
5757    #[test]
5758    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5759        let mut f = Fixture::new();
5760        f.run(&[b"SET", b"str", b"v"]);
5761        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5762
5763        for cmd in [
5764            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5765            &[
5766                b"HPEXPIRE".as_slice(),
5767                b"str",
5768                b"100",
5769                b"FIELDS",
5770                b"1",
5771                b"f",
5772            ][..],
5773            &[
5774                b"HEXPIREAT".as_slice(),
5775                b"str",
5776                b"9999999999",
5777                b"FIELDS",
5778                b"1",
5779                b"f",
5780            ][..],
5781            &[
5782                b"HPEXPIREAT".as_slice(),
5783                b"str",
5784                b"9999999999999",
5785                b"FIELDS",
5786                b"1",
5787                b"f",
5788            ][..],
5789            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5790            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5791            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5792            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5793            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5794        ] {
5795            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5796        }
5797        assert_eq!(
5798            f.run(&[b"GET", b"str"]),
5799            "$1\r\nv\r\n",
5800            "and none of them touched the value"
5801        );
5802    }
5803
5804    #[test]
5805    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5806        let mut f = Fixture::new();
5807        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5808        assert_eq!(
5809            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5810            "*2\r\n$1\r\n1\r\n$-1\r\n",
5811            "positional, so the field that was not there is a nil in its place"
5812        );
5813        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5814        assert_eq!(
5815            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5816            "*1\r\n$-1\r\n"
5817        );
5818        assert_eq!(
5819            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5820            "*1\r\n$1\r\n2\r\n"
5821        );
5822        assert_eq!(
5823            f.run(&[b"EXISTS", b"h"]),
5824            ":0\r\n",
5825            "and the last field took the key"
5826        );
5827    }
5828
5829    #[test]
5830    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5831        let mut f = Fixture::new();
5832        f.run(&[b"HSET", b"h", b"a", b"1"]);
5833        assert_eq!(
5834            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5835            "*1\r\n$1\r\n1\r\n"
5836        );
5837        assert_eq!(
5838            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5839            "*1\r\n:-1\r\n",
5840            "no option means leave it alone, which is the one place this is not GETEX"
5841        );
5842
5843        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
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        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5849        assert_eq!(
5850            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5851            "*1\r\n:100\r\n",
5852            "and a plain read really does leave it alone"
5853        );
5854        assert_eq!(
5855            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5856            "*1\r\n$1\r\n1\r\n"
5857        );
5858        assert_eq!(
5859            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5860            "*1\r\n:-1\r\n"
5861        );
5862
5863        assert_eq!(
5864            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5865            "*1\r\n$1\r\n1\r\n",
5866            "the value goes out before the deadline that has already gone is applied"
5867        );
5868        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5869        assert_eq!(
5870            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5871            "*1\r\n$-1\r\n"
5872        );
5873    }
5874
5875    #[test]
5876    fn hsetex_writes_all_of_it_or_none_of_it() {
5877        let mut f = Fixture::new();
5878        assert_eq!(
5879            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5880            ":1\r\n"
5881        );
5882        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5883        assert_eq!(
5884            f.run(&[
5885                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5886            ]),
5887            ":0\r\n",
5888            "FNX wants every field named to be missing"
5889        );
5890        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5891        assert_eq!(
5892            f.run(&[b"HEXISTS", b"h", b"new"]),
5893            ":0\r\n",
5894            "and none of the list was written"
5895        );
5896        assert_eq!(
5897            f.run(&[
5898                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5899            ]),
5900            ":0\r\n",
5901            "and FXX wants every one of them to be there"
5902        );
5903        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5904        assert_eq!(
5905            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5906            ":1\r\n"
5907        );
5908        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5909
5910        assert_eq!(
5911            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5912            ":0\r\n"
5913        );
5914        assert_eq!(
5915            f.run(&[b"EXISTS", b"gone"]),
5916            ":0\r\n",
5917            "a key with no fields cannot meet FXX and is not created trying"
5918        );
5919    }
5920
5921    #[test]
5922    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5923        let mut f = Fixture::new();
5924        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5925        assert_eq!(
5926            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5927            "*1\r\n:100\r\n"
5928        );
5929
5930        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5931        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5932        assert_eq!(
5933            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5934            "*1\r\n:100\r\n",
5935            "KEEPTTL put back what the write cleared"
5936        );
5937
5938        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5939        assert_eq!(
5940            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5941            "*1\r\n:-1\r\n",
5942            "and without it a write clears the deadline the way HSET does"
5943        );
5944
5945        // Any order, because Redis reads these in a loop and not in a fixed
5946        // sequence.
5947        assert_eq!(
5948            f.run(&[
5949                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5950            ]),
5951            ":1\r\n"
5952        );
5953        assert_eq!(
5954            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5955            "*1\r\n:100\r\n"
5956        );
5957
5958        assert_eq!(
5959            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5960            ":1\r\n",
5961            "written, and not the separate code the HEXPIRE family has for this"
5962        );
5963        assert_eq!(
5964            f.run(&[b"EXISTS", b"h"]),
5965            ":0\r\n",
5966            "and storing it and then removing it emptied the hash"
5967        );
5968    }
5969
5970    #[test]
5971    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5972        let mut f = Fixture::new();
5973        f.run(&[b"HSET", b"h", b"a", b"1"]);
5974        for (bad, want) in [
5975            // HGETDEL has three sentences of its own for these three mistakes.
5976            (
5977                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5978                "-ERR Number of fields must be a positive integer",
5979            ),
5980            (
5981                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5982                "-ERR The `numfields` parameter must match the number of arguments",
5983            ),
5984            (
5985                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5986                "-ERR Mandatory argument FIELDS is missing or not at the right position",
5987            ),
5988            // And HGETEX and HSETEX have three different ones between them.
5989            (
5990                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5991                "-ERR invalid number of fields",
5992            ),
5993            (
5994                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5995                "-ERR wrong number of arguments",
5996            ),
5997            (
5998                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5999                "-ERR unknown argument: FIELD",
6000            ),
6001            (
6002                &[
6003                    b"HGETEX".as_slice(),
6004                    b"h",
6005                    b"KEEPTTL",
6006                    b"FIELDS",
6007                    b"1",
6008                    b"a",
6009                ][..],
6010                "-ERR unknown argument: KEEPTTL",
6011            ),
6012            (
6013                &[
6014                    b"HGETEX".as_slice(),
6015                    b"h",
6016                    b"EX",
6017                    b"100",
6018                    b"PERSIST",
6019                    b"FIELDS",
6020                    b"1",
6021                    b"a",
6022                ][..],
6023                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
6024            ),
6025            (
6026                &[
6027                    b"HSETEX".as_slice(),
6028                    b"h",
6029                    b"EX",
6030                    b"1",
6031                    b"KEEPTTL",
6032                    b"FIELDS",
6033                    b"1",
6034                    b"a",
6035                    b"1",
6036                ][..],
6037                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
6038            ),
6039            (
6040                &[
6041                    b"HSETEX".as_slice(),
6042                    b"h",
6043                    b"FNX",
6044                    b"FXX",
6045                    b"FIELDS",
6046                    b"1",
6047                    b"a",
6048                    b"1",
6049                ][..],
6050                "-ERR Only one of FXX or FNX arguments can be specified",
6051            ),
6052            (
6053                &[
6054                    b"HSETEX".as_slice(),
6055                    b"h",
6056                    b"FIELDS",
6057                    b"2",
6058                    b"a",
6059                    b"1",
6060                    b"b",
6061                ][..],
6062                "-ERR wrong number of arguments",
6063            ),
6064            (
6065                &[
6066                    b"HGETEX".as_slice(),
6067                    b"h",
6068                    b"EX",
6069                    b"-1",
6070                    b"FIELDS",
6071                    b"1",
6072                    b"a",
6073                ][..],
6074                "-ERR invalid expire time, must be >= 0",
6075            ),
6076            (
6077                &[
6078                    b"HGETEX".as_slice(),
6079                    b"h",
6080                    b"PXAT",
6081                    b"99999999999999",
6082                    b"FIELDS",
6083                    b"1",
6084                    b"a",
6085                ][..],
6086                "-ERR invalid expire time in 'hgetex' command",
6087            ),
6088            (
6089                &[
6090                    b"HSETEX".as_slice(),
6091                    b"h",
6092                    b"EX",
6093                    b"abc",
6094                    b"FIELDS",
6095                    b"1",
6096                    b"a",
6097                    b"1",
6098                ][..],
6099                "-ERR value is not an integer or out of range",
6100            ),
6101        ] {
6102            let reply = f.run(bad);
6103            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
6104            assert!(!reply.contains('*'), "an array header went out in front");
6105        }
6106        assert_eq!(
6107            f.run(&[b"HGET", b"h", b"a"]),
6108            "$1\r\n1\r\n",
6109            "and not one of them wrote anything"
6110        );
6111        assert_eq!(
6112            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6113            "*1\r\n:-1\r\n"
6114        );
6115    }
6116
6117    #[test]
6118    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
6119        let mut f = Fixture::new();
6120        f.run(&[b"SET", b"str", b"v"]);
6121        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6122        for cmd in [
6123            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6124            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6125            &[
6126                b"HGETEX".as_slice(),
6127                b"str",
6128                b"EX",
6129                b"100",
6130                b"FIELDS",
6131                b"1",
6132                b"f",
6133            ][..],
6134            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
6135        ] {
6136            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
6137        }
6138        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
6139    }
6140
6141    /// The two orders `HIMPORT` juggles, which are not the same order.
6142    ///
6143    /// Values arrive in the order the fields were declared in and the hash is
6144    /// built in sorted order, so the first value is not generally the first
6145    /// field. And the sort is by length before bytes, which nothing else here
6146    /// sorts names with: `b` comes before `aa` where a plain byte comparison
6147    /// would put `aa` first. Both read off 8.10.1.
6148    #[test]
6149    fn himport_writes_declared_values_into_sorted_fields() {
6150        let mut f = Fixture::new();
6151        assert_eq!(
6152            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
6153            "+OK\r\n"
6154        );
6155        assert_eq!(
6156            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
6157            "+OK\r\n"
6158        );
6159        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
6160        assert_eq!(
6161            f.run(&[b"HGETALL", b"k"]),
6162            bulks(&["a", "3", "b", "1", "aa", "2"])
6163        );
6164    }
6165
6166    /// It replaces the key rather than writing over it, so a field the fieldset
6167    /// does not name is gone afterwards and so is the deadline.
6168    #[test]
6169    fn himport_set_replaces_the_whole_key() {
6170        let mut f = Fixture::new();
6171        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
6172        f.run(&[b"EXPIRE", b"k", b"100"]);
6173        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6174        assert_eq!(
6175            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6176            "+OK\r\n"
6177        );
6178        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6179        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
6180    }
6181
6182    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
6183    /// throws them away, and a key built from one outlives it.
6184    #[test]
6185    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
6186        let mut f = Fixture::new();
6187        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
6188        f.run(&[b"SELECT", b"1"]);
6189        assert_eq!(
6190            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6191            "+OK\r\n"
6192        );
6193        f.run(&[b"SELECT", b"0"]);
6194        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
6195        assert_eq!(
6196            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
6197            "-ERR no such fieldset\r\n"
6198        );
6199    }
6200
6201    /// Which complaint wins when a line is wrong in more than one place.
6202    ///
6203    /// The type of the key beats both of the others, so a `HIMPORT SET` against
6204    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
6205    /// the ordering a real server has and not the one the argument order
6206    /// suggests.
6207    #[test]
6208    fn himport_complains_in_the_order_a_real_server_does() {
6209        let mut f = Fixture::new();
6210        f.run(&[b"SET", b"str", b"v"]);
6211        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6212        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6213        assert_eq!(
6214            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
6215            wrong,
6216            "the type beats a missing fieldset"
6217        );
6218        assert_eq!(
6219            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
6220            wrong,
6221            "and it beats a value count that does not fit"
6222        );
6223        assert_eq!(
6224            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
6225            "-ERR no such fieldset\r\n"
6226        );
6227        // One sentence for too few and for too many alike.
6228        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
6229            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
6230            line.extend_from_slice(values);
6231            assert_eq!(
6232                f.run(&line),
6233                "-ERR value count does not match fieldset field count\r\n",
6234                "{} values into two fields",
6235                values.len()
6236            );
6237        }
6238        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6239    }
6240
6241    /// The arity of each subcommand, and the unknown one.
6242    #[test]
6243    fn himport_checks_each_subcommand_count_under_its_own_name() {
6244        let mut f = Fixture::new();
6245        assert_eq!(
6246            f.run(&[b"HIMPORT"]),
6247            "-ERR wrong number of arguments for 'himport' command\r\n"
6248        );
6249        for (rest, name) in [
6250            (&["PREPARE"][..], "prepare"),
6251            (&["PREPARE", "fs"][..], "prepare"),
6252            (&["SET"][..], "set"),
6253            (&["SET", "k"][..], "set"),
6254            (&["SET", "k", "fs"][..], "set"),
6255            (&["DISCARD"][..], "discard"),
6256            (&["DISCARD", "a", "b"][..], "discard"),
6257            (&["DISCARDALL", "x"][..], "discardall"),
6258        ] {
6259            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
6260            line.extend(rest.iter().map(|a| a.as_bytes()));
6261            assert_eq!(
6262                f.run(&line),
6263                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
6264                "HIMPORT {}",
6265                rest.join(" ")
6266            );
6267        }
6268        assert_eq!(
6269            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
6270            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
6271        );
6272    }
6273
6274    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
6275    /// is the answer of the two that could not be guessed from outside.
6276    #[test]
6277    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
6278        let mut f = Fixture::new();
6279        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6280        assert_eq!(
6281            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
6282            "-ERR duplicate field name in fieldset\r\n"
6283        );
6284        assert_eq!(
6285            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6286            "+OK\r\n"
6287        );
6288        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6289    }
6290
6291    /// Preparing the same name twice replaces it, and the two discards count
6292    /// what they took rather than answering OK.
6293    #[test]
6294    fn himport_prepare_replaces_and_the_discards_count() {
6295        let mut f = Fixture::new();
6296        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6297        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
6298        assert_eq!(
6299            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6300            "+OK\r\n"
6301        );
6302        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
6303
6304        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
6305        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
6306        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
6307        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
6308        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
6309        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
6310    }
6311
6312    /// The one integer of a single element array reply.
6313    /// The number out of a plain integer reply.
6314    ///
6315    /// [`int_reply`] is the same thing wrapped in a one element array, which is
6316    /// the shape every hash field command answers in.
6317    fn int(reply: &str) -> i64 {
6318        let body = reply
6319            .strip_prefix(':')
6320            .and_then(|s| s.strip_suffix("\r\n"))
6321            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
6322        body.parse().expect("an integer")
6323    }
6324
6325    fn int_reply(reply: &str) -> i64 {
6326        let body = reply
6327            .strip_prefix("*1\r\n:")
6328            .and_then(|s| s.strip_suffix("\r\n"))
6329            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
6330        body.parse().expect("an integer")
6331    }
6332
6333    /// The cursor and the flat items of a scan reply.
6334    fn scan_reply(reply: &str) -> (String, Vec<String>) {
6335        let mut lines = reply.split("\r\n");
6336        assert_eq!(lines.next(), Some("*2"), "got {reply}");
6337        lines.next().expect("the cursor header");
6338        let cursor = lines.next().expect("a cursor").to_owned();
6339        let header = lines.next().expect("an item count");
6340        let n: usize = header[1..].parse().expect("a count");
6341        let mut items = Vec::with_capacity(n);
6342        for _ in 0..n {
6343            lines.next().expect("an item header");
6344            items.push(lines.next().expect("an item").to_owned());
6345        }
6346        (cursor, items)
6347    }
6348
6349    /// The members of a set reply, sorted, since none of these promise an
6350    /// order and a test that asserted one would be asserting an accident.
6351    fn sorted(reply: &str) -> Vec<String> {
6352        let mut lines = reply.split("\r\n");
6353        let header = lines.next().expect("a header");
6354        assert!(
6355            header.starts_with('*') || header.starts_with('~'),
6356            "got {reply}"
6357        );
6358        let n: usize = header[1..].parse().expect("a member count");
6359        let mut got = Vec::with_capacity(n);
6360        for _ in 0..n {
6361            lines.next().expect("a member header");
6362            got.push(lines.next().expect("a member").to_owned());
6363        }
6364        got.sort();
6365        got
6366    }
6367
6368    #[test]
6369    fn the_algebra_answers_what_the_sets_share_and_do_not() {
6370        let mut f = Fixture::new();
6371        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6372        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6373        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
6374
6375        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
6376        assert_eq!(
6377            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
6378            ["1", "2", "3", "4", "5"]
6379        );
6380        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
6381        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
6382
6383        // A key that is not there is an empty set, which empties an
6384        // intersection and does nothing at all to a union.
6385        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
6386        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
6387        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
6388        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
6389    }
6390
6391    #[test]
6392    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
6393        let mut f = Fixture::new();
6394        f.run(&[b"SADD", b"a", b"x"]);
6395        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
6396        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
6397        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
6398
6399        f.run(&[b"HELLO", b"3"]);
6400        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
6401        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
6402        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
6403        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
6404    }
6405
6406    #[test]
6407    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
6408        let mut f = Fixture::new();
6409        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6410        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6411
6412        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
6413        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
6414        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
6415        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
6416        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
6417        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
6418
6419        // An empty answer deletes the destination rather than leaving an empty
6420        // set behind, and the destination may be one of the sources.
6421        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
6422        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6423        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
6424        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
6425
6426        // And a destination holding something else is overwritten, the same way
6427        // SET overwrites, rather than refused.
6428        f.run(&[b"SET", b"str", b"v"]);
6429        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
6430        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
6431    }
6432
6433    #[test]
6434    fn sintercard_counts_without_building_and_stops_at_a_limit() {
6435        let mut f = Fixture::new();
6436        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6437        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
6438
6439        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
6440        assert_eq!(
6441            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6442            ":2\r\n"
6443        );
6444        assert_eq!(
6445            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6446            ":3\r\n",
6447            "a limit of zero is no limit"
6448        );
6449        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
6450        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
6451
6452        // The counted keys are what make its three error messages its own.
6453        assert_eq!(
6454            f.run(&[b"SINTERCARD", b"0", b"a"]),
6455            "-ERR numkeys should be greater than 0\r\n"
6456        );
6457        assert_eq!(
6458            f.run(&[b"SINTERCARD", b"abc", b"a"]),
6459            "-ERR numkeys should be greater than 0\r\n"
6460        );
6461        assert_eq!(
6462            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
6463            "-ERR Number of keys can't be greater than number of args\r\n"
6464        );
6465        assert_eq!(
6466            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
6467            "-ERR LIMIT can't be negative\r\n"
6468        );
6469        assert_eq!(
6470            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
6471            "-ERR syntax error\r\n"
6472        );
6473        // A key really can be called LIMIT, which is why the count exists.
6474        f.run(&[b"SADD", b"LIMIT", b"2"]);
6475        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
6476    }
6477
6478    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
6479    /// over a difference. Every number here was read off 8.10.1 first.
6480    #[test]
6481    fn sunioncard_and_sdiffcard_count_without_building() {
6482        let mut f = Fixture::new();
6483        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6484        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6485
6486        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6487        assert_eq!(
6488            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6489            ":2\r\n"
6490        );
6491        assert_eq!(
6492            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6493            ":6\r\n",
6494            "a limit of zero is no limit"
6495        );
6496        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6497        assert_eq!(
6498            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6499            ":4\r\n",
6500            "a missing key adds nothing to a union"
6501        );
6502
6503        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6504        assert_eq!(
6505            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6506            ":1\r\n"
6507        );
6508        assert_eq!(
6509            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6510            ":2\r\n",
6511            "a difference is not symmetric"
6512        );
6513        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6514        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6515        assert_eq!(
6516            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6517            ":0\r\n",
6518            "nothing taken away from nothing"
6519        );
6520
6521        // The same three messages SINTERCARD has, because the line is the same
6522        // line and is parsed once for all three.
6523        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6524            assert_eq!(
6525                f.run(&[name, b"0", b"a"]),
6526                "-ERR numkeys should be greater than 0\r\n"
6527            );
6528            assert_eq!(
6529                f.run(&[name, b"abc", b"a"]),
6530                "-ERR numkeys should be greater than 0\r\n"
6531            );
6532            assert_eq!(
6533                f.run(&[name, b"-1", b"a"]),
6534                "-ERR numkeys should be greater than 0\r\n"
6535            );
6536            assert_eq!(
6537                f.run(&[name, b"3", b"a", b"b"]),
6538                "-ERR Number of keys can't be greater than number of args\r\n"
6539            );
6540            assert_eq!(
6541                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6542                "-ERR LIMIT can't be negative\r\n"
6543            );
6544            assert_eq!(
6545                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6546                "-ERR LIMIT can't be negative\r\n",
6547                "a LIMIT that is not a number gets the negative message too"
6548            );
6549            assert_eq!(
6550                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6551                "-ERR syntax error\r\n"
6552            );
6553            assert_eq!(
6554                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6555                "-ERR syntax error\r\n"
6556            );
6557            assert_eq!(
6558                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6559                "-ERR syntax error\r\n"
6560            );
6561        }
6562
6563        // And a key called LIMIT is a key, here as much as on SINTERCARD.
6564        f.run(&[b"SADD", b"LIMIT", b"2"]);
6565        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6566        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6567    }
6568
6569    #[test]
6570    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6571        let mut f = Fixture::new();
6572        f.run(&[b"SADD", b"a", b"1"]);
6573        f.run(&[b"SADD", b"d", b"old"]);
6574        f.run(&[b"SET", b"str", b"v"]);
6575
6576        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6577        for bad in [
6578            &[b"SINTER".as_slice(), b"a", b"str"][..],
6579            &[b"SUNION".as_slice(), b"str"][..],
6580            &[b"SDIFF".as_slice(), b"a", b"str"][..],
6581            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6582            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6583            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6584            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6585        ] {
6586            let reply = f.run(bad);
6587            assert_eq!(reply, wrong, "for {:?}", bad[0]);
6588        }
6589        assert_eq!(
6590            f.run(&[b"SMEMBERS", b"d"]),
6591            "*1\r\n$3\r\nold\r\n",
6592            "and the destination was left alone every time"
6593        );
6594    }
6595
6596    /// The leak a set can spring that nothing on the wire would ever show: the
6597    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
6598    #[test]
6599    fn churning_sets_does_not_grow_the_server() {
6600        let mut f = Fixture::new();
6601        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6602        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6603            .chain(std::iter::once(&b"s"[..]))
6604            .chain(members.iter().map(Vec::as_slice))
6605            .collect();
6606
6607        f.run(&args);
6608        f.run(&[b"DEL", b"s"]);
6609        f.server.compact_step();
6610        let after_first = f.server.memory_bytes();
6611
6612        for _ in 0..200 {
6613            f.run(&args);
6614            f.run(&[b"DEL", b"s"]);
6615            f.server.compact_step();
6616        }
6617        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6618        assert!(
6619            f.server.memory_bytes() <= after_first * 2,
6620            "held {} after two hundred passes against {after_first} after one",
6621            f.server.memory_bytes()
6622        );
6623    }
6624
6625    // --------------------------------------------------------------- bitmaps
6626
6627    /// The two single bit commands, and the encoding rule underneath them.
6628    ///
6629    /// A write always leaves the value `raw` and a read never re-encodes, which
6630    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
6631    /// with its first digit changed after a `SETBIT`.
6632    #[test]
6633    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6634        let mut f = Fixture::new();
6635        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6636        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6637        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6638        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6639        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6640        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6641
6642        // Writing a nought past the end still creates the key and still pads.
6643        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6644        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6645        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6646
6647        f.run(&[b"SET", b"num", b"12345"]);
6648        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6649        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6650        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6651        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6652        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6653    }
6654
6655    /// Counting, in bytes and in bits.
6656    ///
6657    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
6658    /// says 22 for it. The server is the thing being copied here.
6659    #[test]
6660    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6661        let mut f = Fixture::new();
6662        f.run(&[b"SET", b"mykey", b"foobar"]);
6663        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6664        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6665        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6666        assert_eq!(
6667            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6668            ":6\r\n"
6669        );
6670        assert_eq!(
6671            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6672            ":25\r\n"
6673        );
6674        assert_eq!(
6675            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6676            ":17\r\n"
6677        );
6678        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6679
6680        // A start past the end is left where it is and the end is pulled back,
6681        // so the range comes out backwards and counts nothing.
6682        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6683
6684        // A lone start is a syntax error here, where BITPOS allows it.
6685        assert_eq!(
6686            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6687            "-ERR syntax error\r\n"
6688        );
6689        assert_eq!(
6690            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6691            "-ERR syntax error\r\n"
6692        );
6693    }
6694
6695    /// Searching, and the one place a miss is not minus one.
6696    ///
6697    /// A search for a nought that runs to the end of the string answers the
6698    /// length in bits, because the string is treated as if it had noughts after
6699    /// it forever. Give it an explicit end and it answers minus one instead.
6700    #[test]
6701    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6702        let mut f = Fixture::new();
6703        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6704        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6705        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6706        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6707        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6708        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6709
6710        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6711        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6712        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6713        assert_eq!(
6714            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6715            ":8\r\n"
6716        );
6717
6718        // A missing key is all noughts, so a one is never found and a nought is
6719        // at position zero.
6720        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6721        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6722    }
6723
6724    /// The eight operations, with the answers a real server gives for them.
6725    #[test]
6726    fn the_eight_combinations_write_what_a_real_server_writes() {
6727        let mut f = Fixture::new();
6728        f.run(&[b"SET", b"a", b"abc"]);
6729        f.run(&[b"SET", b"b", b"abd"]);
6730        let cases: &[(&[u8], &str)] = &[
6731            (b"AND", "ab`"),
6732            (b"OR", "abg"),
6733            (b"XOR", "\u{0}\u{0}\u{7}"),
6734            (b"DIFF", "\u{0}\u{0}\u{3}"),
6735            (b"DIFF1", "\u{0}\u{0}\u{4}"),
6736            (b"ANDOR", "ab`"),
6737            (b"ONE", "\u{0}\u{0}\u{7}"),
6738        ];
6739        for (op, want) in cases {
6740            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6741            assert_eq!(
6742                f.run(&[b"GET", b"d"]),
6743                format!("$3\r\n{want}\r\n"),
6744                "{op:?}"
6745            );
6746        }
6747        // The one whose answer is not text, so it is compared as bytes.
6748        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6749        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6750
6751        // A missing source is a string of noughts as long as it needs to be, so
6752        // an AND against one writes three zero bytes rather than nothing.
6753        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6754        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6755
6756        // Every source missing is an empty result, and an empty result takes
6757        // the destination with it.
6758        f.run(&[b"SET", b"dest", b"x"]);
6759        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6760        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6761    }
6762
6763    /// What `BITOP` says when it is asked for something it cannot do.
6764    #[test]
6765    fn bitop_names_the_operation_in_its_own_complaints() {
6766        let mut f = Fixture::new();
6767        f.run(&[b"SET", b"a", b"abc"]);
6768        assert_eq!(
6769            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6770            "-ERR syntax error\r\n"
6771        );
6772        assert_eq!(
6773            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6774            "-ERR BITOP NOT must be called with a single source key.\r\n"
6775        );
6776        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6777            assert_eq!(
6778                f.run(&[b"BITOP", op, b"d", b"a"]),
6779                format!(
6780                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
6781                    String::from_utf8_lossy(op)
6782                )
6783            );
6784        }
6785        f.run(&[b"LPUSH", b"l", b"x"]);
6786        assert_eq!(
6787            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6788            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6789        );
6790    }
6791
6792    /// Packed fields, the three overflow policies and the `#` offset.
6793    #[test]
6794    fn bitfield_reads_and_writes_packed_fields() {
6795        let mut f = Fixture::new();
6796        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6797        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6798
6799        assert_eq!(
6800            f.run(&[
6801                b"BITFIELD",
6802                b"bf",
6803                b"INCRBY",
6804                b"u2",
6805                b"100",
6806                b"1",
6807                b"GET",
6808                b"u4",
6809                b"0"
6810            ]),
6811            "*2\r\n:1\r\n:0\r\n"
6812        );
6813        // The field at bit 100 is two bits wide, so it ends in the thirteenth
6814        // byte and the value grew to thirteen bytes to hold it.
6815        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6816
6817        // A `#` offset counts in fields rather than in bits.
6818        assert_eq!(
6819            f.run(&[
6820                b"BITFIELD",
6821                b"bf",
6822                b"SET",
6823                b"u8",
6824                b"#0",
6825                b"255",
6826                b"GET",
6827                b"u8",
6828                b"#0"
6829            ]),
6830            "*2\r\n:0\r\n:255\r\n"
6831        );
6832
6833        assert_eq!(
6834            f.run(&[
6835                b"BITFIELD",
6836                b"bf",
6837                b"OVERFLOW",
6838                b"SAT",
6839                b"INCRBY",
6840                b"i8",
6841                b"0",
6842                b"120",
6843                b"INCRBY",
6844                b"i8",
6845                b"0",
6846                b"120"
6847            ]),
6848            "*2\r\n:119\r\n:127\r\n"
6849        );
6850        assert_eq!(
6851            f.run(&[
6852                b"BITFIELD",
6853                b"bf2",
6854                b"OVERFLOW",
6855                b"FAIL",
6856                b"INCRBY",
6857                b"u2",
6858                b"0",
6859                b"5"
6860            ]),
6861            "*1\r\n$-1\r\n"
6862        );
6863        assert_eq!(
6864            f.run(&[
6865                b"BITFIELD",
6866                b"bf3",
6867                b"OVERFLOW",
6868                b"WRAP",
6869                b"INCRBY",
6870                b"u2",
6871                b"0",
6872                b"5"
6873            ]),
6874            "*1\r\n:1\r\n"
6875        );
6876        assert_eq!(
6877            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6878            "*1\r\n:4611686018427387904\r\n"
6879        );
6880    }
6881
6882    /// A bad subcommand anywhere in the line stops all of it.
6883    ///
6884    /// Redis checks the whole argument list before it runs any of it, so the
6885    /// `SET` in front of the bad type here never happens and the key it would
6886    /// have created is not there afterwards.
6887    #[test]
6888    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6889        let mut f = Fixture::new();
6890        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6891        assert_eq!(
6892            f.run(&[
6893                b"BITFIELD",
6894                b"bad",
6895                b"SET",
6896                b"u8",
6897                b"0",
6898                b"1",
6899                b"GET",
6900                b"u99",
6901                b"0"
6902            ]),
6903            bad_type
6904        );
6905        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6906        assert_eq!(
6907            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6908            bad_type
6909        );
6910        assert_eq!(
6911            f.run(&[b"BITFIELD", b"bad", b"GET"]),
6912            "-ERR syntax error\r\n"
6913        );
6914        assert_eq!(
6915            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6916            "-ERR syntax error\r\n"
6917        );
6918        assert_eq!(
6919            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6920            "-ERR syntax error\r\n"
6921        );
6922        assert_eq!(
6923            f.run(&[
6924                b"BITFIELD",
6925                b"bad",
6926                b"OVERFLOW",
6927                b"NOPE",
6928                b"GET",
6929                b"u8",
6930                b"0"
6931            ]),
6932            "-ERR Invalid OVERFLOW type specified\r\n"
6933        );
6934        assert_eq!(
6935            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6936            "-ERR value is not an integer or out of range\r\n"
6937        );
6938        for at in [&b"#-1"[..], b"abc"] {
6939            assert_eq!(
6940                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6941                "-ERR bit offset is not an integer or out of range\r\n"
6942            );
6943        }
6944    }
6945
6946    /// The read only twin reads, refuses to write, and creates nothing.
6947    #[test]
6948    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6949        let mut f = Fixture::new();
6950        f.run(&[b"SET", b"n", b"123"]);
6951        assert_eq!(
6952            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6953            "*1\r\n:49\r\n"
6954        );
6955        // A read does not unpack an int the way a write does.
6956        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6957
6958        // An OVERFLOW word is allowed even though nothing here can overflow.
6959        assert_eq!(
6960            f.run(&[
6961                b"BITFIELD_RO",
6962                b"n",
6963                b"OVERFLOW",
6964                b"SAT",
6965                b"GET",
6966                b"u8",
6967                b"0"
6968            ]),
6969            "*1\r\n:49\r\n"
6970        );
6971        for sub in [&b"SET"[..], b"INCRBY"] {
6972            assert_eq!(
6973                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6974                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6975            );
6976        }
6977
6978        assert_eq!(
6979            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6980            "*1\r\n:0\r\n"
6981        );
6982        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6983    }
6984
6985    /// The offsets a bitmap command will not take.
6986    #[test]
6987    fn an_offset_off_the_end_of_the_world_is_refused() {
6988        let mut f = Fixture::new();
6989        let bad = "-ERR bit offset is not an integer or out of range\r\n";
6990        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6991            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6992            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6993        }
6994        for arg in [&b"2"[..], b"-1"] {
6995            assert_eq!(
6996                f.run(&[b"BITPOS", b"k", arg]),
6997                "-ERR The bit argument must be 1 or 0.\r\n"
6998            );
6999        }
7000        assert_eq!(
7001            f.run(&[b"BITPOS", b"k", b"abc"]),
7002            "-ERR value is not an integer or out of range\r\n"
7003        );
7004        assert_eq!(
7005            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
7006            "-ERR value is not an integer or out of range\r\n"
7007        );
7008        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
7009        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
7010        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
7011    }
7012
7013    /// Every one of the seven refuses a key that is not a string.
7014    #[test]
7015    fn every_bitmap_command_says_wrongtype() {
7016        let mut f = Fixture::new();
7017        f.run(&[b"LPUSH", b"l", b"x"]);
7018        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7019        let cases: &[&[&[u8]]] = &[
7020            &[b"SETBIT", b"l", b"0", b"1"],
7021            &[b"GETBIT", b"l", b"0"],
7022            &[b"BITCOUNT", b"l"],
7023            &[b"BITPOS", b"l", b"1"],
7024            &[b"BITOP", b"AND", b"d", b"l"],
7025            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
7026            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
7027        ];
7028        for case in cases {
7029            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
7030        }
7031    }
7032
7033    // --------------------------------------------------------- hyperloglogs
7034
7035    #[test]
7036    fn a_sketch_is_added_to_and_counted() {
7037        let mut f = Fixture::new();
7038        // Creating the key counts as a change, even with nothing to add.
7039        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
7040        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
7041        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
7042        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
7043        // And it is a string, which is not an implementation detail: a client
7044        // can `GET` a sketch out of one server and `SET` it into another.
7045        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
7046        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
7047
7048        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
7049        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
7050        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7051    }
7052
7053    #[test]
7054    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
7055        let mut f = Fixture::new();
7056        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7057        // Not text, so it is compared as bytes.
7058        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";
7059        let mut reply = b"$27\r\n".to_vec();
7060        reply.extend_from_slice(want);
7061        reply.extend_from_slice(b"\r\n");
7062        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
7063    }
7064
7065    #[test]
7066    fn counting_several_keys_counts_their_union() {
7067        let mut f = Fixture::new();
7068        f.run(&[b"PFADD", b"a", b"x", b"y"]);
7069        f.run(&[b"PFADD", b"b", b"y", b"z"]);
7070        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
7071        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
7072        // A key that is not there is an empty sketch, not an error and not
7073        // something that gets created by being counted.
7074        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
7075        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
7076        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
7077    }
7078
7079    #[test]
7080    fn a_merge_keeps_what_the_destination_had() {
7081        let mut f = Fixture::new();
7082        f.run(&[b"PFADD", b"a", b"x", b"y"]);
7083        f.run(&[b"PFADD", b"b", b"z"]);
7084        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
7085        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
7086        // The destination is one of the sources, so a second merge adds to it.
7087        f.run(&[b"PFADD", b"c", b"w"]);
7088        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
7089        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
7090        // And with no sources it is a no-op that still answers OK and still
7091        // creates a destination that was not there.
7092        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
7093        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
7094    }
7095
7096    #[test]
7097    fn the_debug_forms_answer_four_different_shapes() {
7098        let mut f = Fixture::new();
7099        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7100        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
7101        assert_eq!(
7102            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7103            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
7104        );
7105        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
7106        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
7107        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
7108        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
7109        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7110        // A dense sketch has no opcodes left to print.
7111        assert_eq!(
7112            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7113            "-ERR HLL encoding is not sparse\r\n"
7114        );
7115
7116        // All 16384 registers, of which three are not nought.
7117        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
7118        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
7119        assert_eq!(reply.matches(":0\r\n").count(), 16381);
7120        assert_eq!(reply.matches(":1\r\n").count(), 2);
7121        assert_eq!(reply.matches(":2\r\n").count(), 1);
7122
7123        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
7124    }
7125
7126    #[test]
7127    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
7128        let mut f = Fixture::new();
7129        f.run(&[b"SET", b"plain", b"not a sketch"]);
7130        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
7131        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
7132        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
7133        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
7134        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
7135
7136        // A key that is not a string at all gets the ordinary sentence, and a
7137        // destination that would have been written is not created.
7138        f.run(&[b"RPUSH", b"l", b"x"]);
7139        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7140        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
7141        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
7142        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
7143        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
7144        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
7145    }
7146
7147    #[test]
7148    fn pfdebug_has_its_own_complaints() {
7149        let mut f = Fixture::new();
7150        f.run(&[b"PFADD", b"h", b"a"]);
7151        // The word is quoted exactly as the client spelled it, and this is not
7152        // the "Try X HELP." sentence every other container command uses.
7153        assert_eq!(
7154            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
7155            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
7156        );
7157        // Where all three of the real commands take a missing key as empty.
7158        let gone = "-ERR The specified key does not exist\r\n";
7159        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
7160        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
7161        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
7162        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
7163        assert_eq!(
7164            f.run(&[b"PFDEBUG"]),
7165            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
7166        );
7167        assert_eq!(
7168            f.run(&[b"PFSELFTEST", b"x"]),
7169            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
7170        );
7171    }
7172
7173    #[test]
7174    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
7175        let mut f = Fixture::new();
7176        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7177        // The sketch with its last byte cut off, which is still a header and a
7178        // magic and is a run length encoding that stops short of register 16384.
7179        let reply = f.raw(&[b"GET", b"h"]);
7180        let short = reply[5..reply.len() - 3].to_vec();
7181        f.run(&[b"SET", b"h", &short]);
7182        assert_eq!(
7183            f.run(&[b"PFCOUNT", b"h"]),
7184            "-INVALIDOBJ Corrupted HLL object detected\r\n"
7185        );
7186    }
7187
7188    #[test]
7189    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
7190        let mut f = Fixture::new();
7191        // One that stays sparse and one that has gone dense, since the payload
7192        // carries the bytes and the two encodings are different lengths.
7193        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
7194        for i in 0..10_000u32 {
7195            let ele = format!("e{i}");
7196            f.run(&[b"PFADD", b"big", ele.as_bytes()]);
7197        }
7198        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
7199        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
7200
7201        for key in [&b"small"[..], b"big"] {
7202            let mut copy = key.to_vec();
7203            copy.push(b'2');
7204            let bytes = payload(&f.raw(&[b"DUMP", key]));
7205            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
7206            // The bytes, the encoding and the estimate all come back, which is
7207            // the whole of what byte compatibility across a round trip means.
7208            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
7209            assert_eq!(
7210                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
7211                f.run(&[b"PFDEBUG", b"ENCODING", key])
7212            );
7213            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
7214        }
7215        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
7216        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
7217    }
7218
7219    /// One RESP2 bulk string. The JSON replies are almost all one of these and
7220    /// the text inside them has quotes in it, so writing the frame out by hand
7221    /// buries the part of the assertion that matters.
7222    fn bulk(s: &str) -> String {
7223        format!("${}\r\n{s}\r\n", s.len())
7224    }
7225
7226    /// A RESP2 array of bulk strings, which is what most of the list replies
7227    /// are and what writing them out by hand in every assertion looks like.
7228    fn bulks(parts: &[&str]) -> String {
7229        let mut s = format!("*{}\r\n", parts.len());
7230        for p in parts {
7231            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
7232        }
7233        s
7234    }
7235
7236    #[test]
7237    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
7238        let mut f = Fixture::new();
7239        // Each element in turn goes at the head, so the last one sent is at the
7240        // front when it is over. That reads like a bug in the client and it is
7241        // what every Redis has always done.
7242        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
7243        assert_eq!(
7244            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7245            bulks(&["c", "b", "a"])
7246        );
7247        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
7248        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
7249        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
7250        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
7251        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
7252        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
7253    }
7254
7255    #[test]
7256    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
7257        let mut f = Fixture::new();
7258        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
7259        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
7260        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7261        f.run(&[b"RPUSH", b"k", b"a"]);
7262        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
7263        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
7264        assert_eq!(
7265            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7266            bulks(&["z", "a", "y"])
7267        );
7268    }
7269
7270    /// The four ways a pop can come back with nothing, which are three
7271    /// different replies and a RESP2 client can tell all of them apart.
7272    #[test]
7273    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
7274        let mut f = Fixture::new();
7275        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
7276        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
7277        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
7278        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
7279        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7280        // A count of zero against a list that is there is an empty array and
7281        // not a null array, which is the fourth answer.
7282        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
7283        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
7284        // More than there is takes what there is and the key goes with it.
7285        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
7286        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7287    }
7288
7289    #[test]
7290    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
7291        let mut f = Fixture::new();
7292        f.run(&[b"RPUSH", b"k", b"a"]);
7293        let range = "-ERR value is out of range, must be positive\r\n";
7294        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
7295        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
7296        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
7297        // Redis calls this an arity error and not a syntax error, which is a
7298        // distinction it does not always make.
7299        assert_eq!(
7300            f.run(&[b"LPOP", b"k", b"1", b"2"]),
7301            "-ERR wrong number of arguments for 'lpop' command\r\n"
7302        );
7303        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7304    }
7305
7306    #[test]
7307    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
7308        let mut f = Fixture::new();
7309        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7310        assert_eq!(
7311            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7312            bulks(&["a", "b", "c"])
7313        );
7314        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
7315        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
7316        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
7317        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
7318        assert_eq!(
7319            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
7320            bulks(&["a", "b", "c"])
7321        );
7322        // A key that is not there is an empty range and not a nil, which is the
7323        // one place a list disagrees with a set.
7324        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
7325        assert_eq!(
7326            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
7327            "-ERR value is not an integer or out of range\r\n"
7328        );
7329    }
7330
7331    #[test]
7332    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
7333        let mut f = Fixture::new();
7334        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7335        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
7336        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
7337        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
7338        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
7339        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
7340        assert_eq!(
7341            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7342            bulks(&["a", "b", "z"])
7343        );
7344        // Both ways of missing are errors here rather than a nil, because a
7345        // list is never empty and there is nothing else the reply could be.
7346        assert_eq!(
7347            f.run(&[b"LSET", b"k", b"99", b"z"]),
7348            "-ERR index out of range\r\n"
7349        );
7350        assert_eq!(
7351            f.run(&[b"LSET", b"nope", b"0", b"z"]),
7352            "-ERR no such key\r\n"
7353        );
7354    }
7355
7356    #[test]
7357    fn linsert_says_three_things_with_one_signed_number() {
7358        let mut f = Fixture::new();
7359        // Zero for a key that is not there, which is not the same as minus one
7360        // for a pivot that is not in a list that is.
7361        assert_eq!(
7362            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
7363            ":0\r\n"
7364        );
7365        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7366        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
7367        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
7368        assert_eq!(
7369            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7370            bulks(&["X", "a", "b", "Y"])
7371        );
7372        assert_eq!(
7373            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
7374            ":-1\r\n"
7375        );
7376        assert_eq!(
7377            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
7378            "-ERR syntax error\r\n"
7379        );
7380    }
7381
7382    #[test]
7383    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
7384        let mut f = Fixture::new();
7385        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
7386        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
7387        assert_eq!(
7388            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7389            bulks(&["b", "c", "a"])
7390        );
7391        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
7392        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7393        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
7394        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
7395        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7396        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
7397    }
7398
7399    #[test]
7400    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
7401        let mut f = Fixture::new();
7402        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
7403        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
7404        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7405        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
7406        // leave `EXISTS` answering zero rather than leaving an empty one.
7407        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
7408        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7409        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
7410    }
7411
7412    #[test]
7413    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
7414        let mut f = Fixture::new();
7415        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
7416        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
7417        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
7418        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
7419        assert_eq!(
7420            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
7421            "*2\r\n:0\r\n:3\r\n"
7422        );
7423        assert_eq!(
7424            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
7425            "*3\r\n:6\r\n:3\r\n:0\r\n"
7426        );
7427        // MAXLEN counts elements looked at and not matches found, so three
7428        // stops after `a b c` and finds the one match in it.
7429        assert_eq!(
7430            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
7431            "*1\r\n:0\r\n"
7432        );
7433        // Nothing found is three different replies depending on how it was
7434        // asked and whether the key is there at all.
7435        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
7436        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
7437        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
7438        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
7439    }
7440
7441    #[test]
7442    fn lpos_words_its_three_mistakes_the_way_redis_does() {
7443        let mut f = Fixture::new();
7444        f.run(&[b"RPUSH", b"p", b"a"]);
7445        // The whole sentence and not a prefix, because the older wording of it
7446        // is still all over the internet and clients match on the text.
7447        assert_eq!(
7448            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
7449            "-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"
7450        );
7451        assert_eq!(
7452            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
7453            "-ERR COUNT can't be negative\r\n"
7454        );
7455        assert_eq!(
7456            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
7457            "-ERR MAXLEN can't be negative\r\n"
7458        );
7459        assert_eq!(
7460            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
7461            "-ERR syntax error\r\n"
7462        );
7463        assert_eq!(
7464            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
7465            "-ERR syntax error\r\n"
7466        );
7467    }
7468
7469    #[test]
7470    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
7471        let mut f = Fixture::new();
7472        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7473        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
7474        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7475        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
7476        assert_eq!(
7477            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
7478            "$1\r\na\r\n"
7479        );
7480        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7481        // The same key twice is the documented way to rotate a list and falls
7482        // out of taking the element before deciding where to put it.
7483        f.run(&[b"DEL", b"r"]);
7484        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7485        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7486        assert_eq!(
7487            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7488            bulks(&["3", "1", "2"])
7489        );
7490        assert_eq!(
7491            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7492            "$-1\r\n"
7493        );
7494        assert_eq!(
7495            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7496            "-ERR syntax error\r\n"
7497        );
7498    }
7499
7500    #[test]
7501    fn a_move_checks_the_destination_before_it_takes_anything() {
7502        let mut f = Fixture::new();
7503        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7504        f.run(&[b"SET", b"str", b"v"]);
7505        assert_eq!(
7506            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7507            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7508        );
7509        // The element is still where it was, rather than having gone nowhere.
7510        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7511    }
7512
7513    #[test]
7514    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7515        // OBO is what you get from sending LMOVE that many times, BULK keeps
7516        // the source order. The two only differ when both ends are the same,
7517        // which is the whole reason the word exists.
7518        for (from, to, order, want) in [
7519            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7520            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7521            ("LEFT", "LEFT", "OBO", ["b", "a"]),
7522            ("LEFT", "LEFT", "BULK", ["a", "b"]),
7523            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7524            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7525            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7526            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7527        ] {
7528            let mut f = Fixture::new();
7529            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7530            let how = format!("{from} {to} {order}");
7531            let reply = f.run(&[
7532                b"LMOVEM",
7533                b"s",
7534                b"d",
7535                from.as_bytes(),
7536                to.as_bytes(),
7537                b"COUNT",
7538                b"2",
7539                order.as_bytes(),
7540            ]);
7541            assert_eq!(reply, bulks(&want), "the reply for {how}");
7542            assert_eq!(
7543                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7544                bulks(&want),
7545                "the destination for {how}"
7546            );
7547        }
7548    }
7549
7550    #[test]
7551    fn a_block_move_of_one_needs_no_count_at_all() {
7552        let mut f = Fixture::new();
7553        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7554        assert_eq!(
7555            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7556            bulks(&["a"])
7557        );
7558        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7559        // Six and seven arguments are neither of the two forms, so the
7560        // reference calls both of them a syntax error rather than guessing.
7561        assert_eq!(
7562            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7563            "-ERR syntax error\r\n"
7564        );
7565        assert_eq!(
7566            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7567            "-ERR syntax error\r\n"
7568        );
7569    }
7570
7571    #[test]
7572    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7573        let mut f = Fixture::new();
7574        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7575        // A null array and not a null bulk string, which `redis-cli` prints as
7576        // `(nil)` either way and only the raw wire tells apart. What it would
7577        // have sent is an array, so its nothing is an array's nothing.
7578        assert_eq!(
7579            f.run(&[
7580                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7581            ]),
7582            "*-1\r\n"
7583        );
7584        assert_eq!(
7585            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7586            bulks(&["a", "b", "c"])
7587        );
7588        // COUNT takes what there is, and an emptied source goes away.
7589        assert_eq!(
7590            f.run(&[
7591                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7592            ]),
7593            bulks(&["a", "b", "c"])
7594        );
7595        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7596        assert_eq!(
7597            f.run(&[
7598                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7599            ]),
7600            "*-1\r\n"
7601        );
7602    }
7603
7604    #[test]
7605    fn a_block_move_onto_itself_rotates_by_the_count() {
7606        let mut f = Fixture::new();
7607        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7608        assert_eq!(
7609            f.run(&[
7610                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7611            ]),
7612            bulks(&["a", "b"])
7613        );
7614        assert_eq!(
7615            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7616            bulks(&["c", "a", "b"])
7617        );
7618    }
7619
7620    #[test]
7621    fn a_block_move_reads_the_count_before_the_ordering_word() {
7622        let mut f = Fixture::new();
7623        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7624        f.run(&[b"SET", b"str", b"v"]);
7625        let count = "-ERR count should be greater than 0\r\n";
7626        assert_eq!(
7627            f.run(&[
7628                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7629            ]),
7630            count
7631        );
7632        assert_eq!(
7633            f.run(&[
7634                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7635            ]),
7636            count
7637        );
7638        assert_eq!(
7639            f.run(&[
7640                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7641            ]),
7642            "-ERR syntax error\r\n"
7643        );
7644        assert_eq!(
7645            f.run(&[
7646                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7647            ]),
7648            "-ERR syntax error\r\n"
7649        );
7650        // Every argument is read before the keys are looked at, so a bad count
7651        // beats a wrong type even when the type is wrong on the source.
7652        assert_eq!(
7653            f.run(&[
7654                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7655            ]),
7656            count
7657        );
7658        assert_eq!(
7659            f.run(&[
7660                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7661            ]),
7662            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7663        );
7664        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7665    }
7666
7667    #[test]
7668    fn lmpop_answers_from_the_first_key_that_has_anything() {
7669        let mut f = Fixture::new();
7670        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7671        // The name of the key that answered comes back with the elements,
7672        // because the client cannot work out which one it was.
7673        assert_eq!(
7674            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7675            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7676        );
7677        assert_eq!(
7678            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7679            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7680        );
7681        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7682        // A null array and not a null, even though what it stands in for is an
7683        // array holding a key name and then another array.
7684        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7685    }
7686
7687    #[test]
7688    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7689        let mut f = Fixture::new();
7690        f.run(&[b"RPUSH", b"k", b"a"]);
7691        assert_eq!(
7692            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7693            "-ERR numkeys should be greater than 0\r\n"
7694        );
7695        assert_eq!(
7696            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7697            "-ERR numkeys should be greater than 0\r\n"
7698        );
7699        assert_eq!(
7700            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7701            "-ERR count should be greater than 0\r\n"
7702        );
7703        // A key count that eats the direction is a syntax error and not a
7704        // sentence about key counts, because the direction is simply not there.
7705        assert_eq!(
7706            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7707            "-ERR syntax error\r\n"
7708        );
7709        assert_eq!(
7710            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7711            "-ERR syntax error\r\n"
7712        );
7713        assert_eq!(
7714            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7715            "-ERR syntax error\r\n"
7716        );
7717        assert_eq!(
7718            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7719            "-ERR syntax error\r\n"
7720        );
7721        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7722    }
7723
7724    #[test]
7725    fn every_list_command_says_wrongtype_and_writes_nothing() {
7726        let mut f = Fixture::new();
7727        f.run(&[b"SET", b"str", b"v"]);
7728        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7729        for cmd in [
7730            &[b"LPUSH".as_slice(), b"str", b"a"][..],
7731            &[b"RPUSH", b"str", b"a"],
7732            &[b"LPUSHX", b"str", b"a"],
7733            &[b"RPUSHX", b"str", b"a"],
7734            &[b"LPOP", b"str"],
7735            &[b"LPOP", b"str", b"2"],
7736            &[b"RPOP", b"str"],
7737            &[b"LLEN", b"str"],
7738            &[b"LRANGE", b"str", b"0", b"-1"],
7739            &[b"LINDEX", b"str", b"0"],
7740            &[b"LSET", b"str", b"0", b"a"],
7741            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7742            &[b"LREM", b"str", b"0", b"a"],
7743            &[b"LTRIM", b"str", b"0", b"-1"],
7744            &[b"LPOS", b"str", b"a"],
7745            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7746            &[b"RPOPLPUSH", b"str", b"d"],
7747            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7748            &[b"LMPOP", b"1", b"str", b"LEFT"],
7749        ] {
7750            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7751        }
7752        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7753        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7754    }
7755
7756    /// A timeout is not an integer and it is not an ordinary float either: the
7757    /// three sentences it can answer with are its own, and which one a given
7758    /// argument gets is not what reading the code would suggest.
7759    #[test]
7760    fn a_timeout_has_three_ways_of_being_wrong() {
7761        let mut f = Fixture::new();
7762        let not_float = "-ERR timeout is not a float or out of range\r\n";
7763        let range = "-ERR timeout is out of range\r\n";
7764        for (bad, want) in [
7765            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7766            (&[b"BLPOP", b"k", b"nan"], not_float),
7767            (&[b"BLPOP", b"k", b""], not_float),
7768            // Whitespace on either side, which `strtold` would take and Redis
7769            // does not.
7770            (&[b"BLPOP", b"k", b" 1"], not_float),
7771            (&[b"BLPOP", b"k", b"1 "], not_float),
7772            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7773            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7774            // These three parse, so they are not the not-a-float error, and all
7775            // three are further off than an i64 of milliseconds reaches.
7776            (&[b"BLPOP", b"k", b"1e400"], range),
7777            (&[b"BLPOP", b"k", b"inf"], range),
7778            (&[b"BLPOP", b"k", b"9999999999999999"], range),
7779            (&[b"BRPOP", b"k", b"abc"], not_float),
7780            (
7781                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7782                not_float,
7783            ),
7784            (
7785                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7786                "-ERR timeout is negative\r\n",
7787            ),
7788            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7789        ] {
7790            assert_eq!(f.run(bad), want, "for {bad:?}");
7791        }
7792    }
7793
7794    /// A timeout of exactly zero means no timeout, and there are two ways of
7795    /// writing exactly zero.
7796    #[test]
7797    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7798        let mut f = Fixture::new();
7799        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7800            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7801            assert_eq!(flow, Flow::Block, "for {timeout:?}");
7802            assert!(out.is_empty(), "for {timeout:?}");
7803        }
7804        // Positive, so it is a real deadline, and the deadline is this
7805        // millisecond. Nothing is written here either: the reply comes from the
7806        // sweep, which is the engine's and not this layer's.
7807        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7808        assert_eq!(flow, Flow::Block);
7809        assert!(out.is_empty());
7810    }
7811
7812    #[test]
7813    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7814        let mut f = Fixture::new();
7815        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7816
7817        // The one difference from LPOP: the reply names the key that answered,
7818        // which is what makes BLPOP over several keys usable.
7819        assert_eq!(
7820            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7821            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7822        );
7823        assert_eq!(
7824            f.run(&[b"BRPOP", b"L", b"0"]),
7825            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7826        );
7827        assert_eq!(
7828            f.run(&[
7829                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7830            ]),
7831            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7832        );
7833        assert_eq!(
7834            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7835            "$1\r\nd\r\n"
7836        );
7837        assert_eq!(
7838            f.run(&[b"EXISTS", b"L"]),
7839            ":0\r\n",
7840            "and the key went with it"
7841        );
7842        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7843        // Onto itself, which is how a list is rotated and is a real thing to ask
7844        // a blocking move for.
7845        f.run(&[b"RPUSH", b"D", b"x"]);
7846        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7847        assert_eq!(
7848            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7849            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7850        );
7851    }
7852
7853    #[test]
7854    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7855        let mut f = Fixture::new();
7856        f.run(&[b"RPUSH", b"k", b"a"]);
7857        for (bad, want) in [
7858            (
7859                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7860                "-ERR numkeys should be greater than 0\r\n",
7861            ),
7862            (
7863                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7864                "-ERR numkeys should be greater than 0\r\n",
7865            ),
7866            // Two keys named and one given, so the word that should have been
7867            // the direction is a key and there is no direction left.
7868            (
7869                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7870                "-ERR syntax error\r\n",
7871            ),
7872            (
7873                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7874                "-ERR syntax error\r\n",
7875            ),
7876            (
7877                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7878                "-ERR syntax error\r\n",
7879            ),
7880            (
7881                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7882                "-ERR syntax error\r\n",
7883            ),
7884            // A count that is not a number at all gets the same sentence a zero
7885            // or a negative one gets, rather than the usual one about integers.
7886            (
7887                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7888                "-ERR count should be greater than 0\r\n",
7889            ),
7890            (
7891                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7892                "-ERR count should be greater than 0\r\n",
7893            ),
7894        ] {
7895            assert_eq!(f.run(bad), want, "for {bad:?}");
7896        }
7897        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7898    }
7899
7900    #[test]
7901    fn a_blocking_move_reads_its_directions_before_its_timeout() {
7902        let mut f = Fixture::new();
7903        // Both are wrong. Redis checks the directions first, so this is the
7904        // syntax error and not a complaint about the timeout.
7905        assert_eq!(
7906            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7907            "-ERR syntax error\r\n"
7908        );
7909        assert_eq!(
7910            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7911            "-ERR syntax error\r\n"
7912        );
7913    }
7914
7915    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
7916    /// wait, which is the same relationship every other command in this file has
7917    /// with the one it wraps.
7918    #[test]
7919    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7920        let mut f = Fixture::new();
7921        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7922        assert_eq!(
7923            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7924            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7925        );
7926        assert_eq!(
7927            f.run(&[
7928                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7929            ]),
7930            bulks(&["e", "d"])
7931        );
7932        assert_eq!(
7933            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7934            bulks(&["a", "e", "d"])
7935        );
7936        // `EXACTLY` with enough there does not wait either.
7937        assert_eq!(
7938            f.run(&[
7939                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7940            ]),
7941            bulks(&["b", "c"])
7942        );
7943        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7944    }
7945
7946    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
7947    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
7948    /// whole block has arrived.
7949    #[test]
7950    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7951        let mut f = Fixture::new();
7952        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7953        // Two there and three asked for. `COUNT` takes the two.
7954        assert_eq!(
7955            f.flow(&[
7956                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7957            ]),
7958            (Flow::Continue, bulks(&["a", "b"]))
7959        );
7960
7961        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7962        // The same line with `EXACTLY` parks instead, and takes nothing on the
7963        // way past.
7964        assert_eq!(
7965            f.flow(&[
7966                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7967            ])
7968            .0,
7969            Flow::Block
7970        );
7971        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7972    }
7973
7974    #[test]
7975    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7976        let mut f = Fixture::new();
7977        let syntax = "-ERR syntax error\r\n";
7978        // All three are wrong and the directions are read first.
7979        assert_eq!(
7980            f.run(&[
7981                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7982            ]),
7983            syntax
7984        );
7985        // Directions fine, timeout and count both wrong, so the timeout wins.
7986        assert_eq!(
7987            f.run(&[
7988                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7989            ]),
7990            "-ERR timeout is not a float or out of range\r\n"
7991        );
7992        assert_eq!(
7993            f.run(&[
7994                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7995            ]),
7996            "-ERR timeout is negative\r\n"
7997        );
7998        // And with the timeout fine, the count before the ordering word.
7999        assert_eq!(
8000            f.run(&[
8001                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
8002            ]),
8003            "-ERR count should be greater than 0\r\n"
8004        );
8005        assert_eq!(
8006            f.run(&[
8007                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
8008            ]),
8009            syntax
8010        );
8011        // Seven and eight arguments are neither of the two forms, the same way
8012        // six and seven are for `LMOVEM`.
8013        assert_eq!(
8014            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
8015            syntax
8016        );
8017        assert_eq!(
8018            f.run(&[
8019                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
8020            ]),
8021            syntax
8022        );
8023    }
8024
8025    /// The four ways a blocking command sees a key of another type, and the one
8026    /// way it does not.
8027    #[test]
8028    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
8029        let mut f = Fixture::new();
8030        f.run(&[b"SET", b"S", b"v"]);
8031        f.run(&[b"RPUSH", b"D", b"x"]);
8032        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8033
8034        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
8035        // Every key is checked even when an earlier one would have blocked, so
8036        // an empty key in front of a string does not hide it.
8037        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
8038        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
8039        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
8040        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
8041        // The destination, which is only reached because the source has
8042        // something in it.
8043        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
8044        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
8045        assert_eq!(
8046            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
8047            wrong
8048        );
8049        assert_eq!(
8050            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
8051            wrong
8052        );
8053
8054        // And the one that does not: an empty source means the destination is
8055        // never looked at, so this waits rather than erroring, and on a real
8056        // server it times out.
8057        assert_eq!(
8058            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
8059                .0,
8060            Flow::Block
8061        );
8062        // `BLMOVEM` has a second way of not being ready, and it hides the
8063        // destination just as well: the source is a list with two elements in it
8064        // and `EXACTLY` wants three, so the string never gets looked at.
8065        assert_eq!(
8066            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
8067                .0,
8068            Flow::Block
8069        );
8070        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
8071        assert_eq!(
8072            f.flow(&[
8073                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
8074            ])
8075            .0,
8076            Flow::Block
8077        );
8078    }
8079
8080    /// The same churn the set and the string get, because a list that leaks a
8081    /// chunk per push looks exactly like one that does not until it has run for
8082    /// an afternoon.
8083    #[test]
8084    fn churning_lists_does_not_grow_the_server() {
8085        let mut f = Fixture::new();
8086        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
8087        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
8088            .into_iter()
8089            .chain(vals.iter().map(Vec::as_slice))
8090            .collect();
8091
8092        f.run(&args);
8093        f.run(&[b"DEL", b"k"]);
8094        f.server.compact_step();
8095        let after_first = f.server.memory_bytes();
8096
8097        for _ in 0..200 {
8098            f.run(&args);
8099            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
8100            f.server.compact_step();
8101        }
8102        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8103        assert!(
8104            f.server.memory_bytes() <= after_first * 2,
8105            "held {} after two hundred passes against {after_first} after one",
8106            f.server.memory_bytes()
8107        );
8108    }
8109
8110    // ------------------------------------------------------------ sorted set
8111
8112    #[test]
8113    fn a_sorted_set_takes_scores_and_gives_them_back() {
8114        let mut f = Fixture::new();
8115        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
8116        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
8117        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
8118        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
8119        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
8120        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
8121        assert_eq!(
8122            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
8123            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
8124        );
8125        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
8126        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
8127        // The key goes when the last member does.
8128        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
8129        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8130    }
8131
8132    #[test]
8133    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
8134        let mut f = Fixture::new();
8135        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
8136        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
8137        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
8138        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
8139
8140        f.out = Out::new(Proto::Resp3);
8141        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
8142        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
8143        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
8144        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
8145    }
8146
8147    #[test]
8148    fn the_zadd_options_gate_what_gets_written() {
8149        let mut f = Fixture::new();
8150        f.run(&[b"ZADD", b"z", b"5", b"a"]);
8151        // NX leaves a member that is there alone, XX will not create one.
8152        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
8153        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
8154        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
8155        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
8156        // GT and LT only move a score one way.
8157        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
8158        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
8159        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
8160        // CH counts a moved score and plain ZADD does not.
8161        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
8162        assert_eq!(
8163            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
8164            ":2\r\n"
8165        );
8166    }
8167
8168    #[test]
8169    fn zadd_incr_answers_a_score_or_nothing_at_all() {
8170        let mut f = Fixture::new();
8171        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
8172        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
8173        // A gate that refuses is the string nil, because the reply it stands in
8174        // for is a score.
8175        assert_eq!(
8176            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
8177            "$-1\r\n"
8178        );
8179        assert_eq!(
8180            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
8181            "$-1\r\n"
8182        );
8183        assert_eq!(
8184            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
8185            "$-1\r\n"
8186        );
8187        assert_eq!(
8188            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
8189            "$1\r\n8\r\n"
8190        );
8191        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
8192        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
8193    }
8194
8195    #[test]
8196    fn the_two_infinities_will_not_be_added_together() {
8197        let mut f = Fixture::new();
8198        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
8199        let nan = "-ERR resulting score is not a number (NaN)\r\n";
8200        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
8201        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
8202        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
8203        // And a key made for an increment that then fails does not stay behind.
8204        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
8205    }
8206
8207    #[test]
8208    fn zadd_says_its_mistakes_the_way_redis_says_them() {
8209        let mut f = Fixture::new();
8210        // The pairs are counted before the options are looked at, so this is a
8211        // syntax error about having none and not a complaint about NX and XX.
8212        assert_eq!(
8213            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
8214            "-ERR syntax error\r\n"
8215        );
8216        assert_eq!(
8217            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
8218            "-ERR XX and NX options at the same time are not compatible\r\n"
8219        );
8220        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
8221        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
8222        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
8223        assert_eq!(
8224            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
8225            "-ERR INCR option supports a single increment-element pair\r\n"
8226        );
8227        // An odd number of arguments after the options.
8228        assert_eq!(
8229            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
8230            "-ERR syntax error\r\n"
8231        );
8232        // Every score is read before the first is stored.
8233        assert_eq!(
8234            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
8235            "-ERR value is not a valid float\r\n"
8236        );
8237        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8238    }
8239
8240    #[test]
8241    fn a_rank_says_where_a_member_sits_from_either_end() {
8242        let mut f = Fixture::new();
8243        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8244        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
8245        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
8246        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
8247        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
8248        // WITHSCORE changes both shapes: the answer and the nothing.
8249        assert_eq!(
8250            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
8251            "*2\r\n:1\r\n$1\r\n2\r\n"
8252        );
8253        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
8254        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
8255        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
8256        // A bad option is a syntax error and one argument too many is an arity
8257        // error, which is Redis's split.
8258        assert_eq!(
8259            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
8260            "-ERR syntax error\r\n"
8261        );
8262        assert_eq!(
8263            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
8264            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
8265        );
8266    }
8267
8268    #[test]
8269    fn the_two_counts_read_their_two_kinds_of_bound() {
8270        let mut f = Fixture::new();
8271        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8272        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
8273        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
8274        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
8275        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
8276        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
8277        assert_eq!(
8278            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
8279            "-ERR min or max is not a float\r\n"
8280        );
8281
8282        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
8283        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
8284        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
8285        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
8286        // A bare member is not a bound, because a member can start with any
8287        // byte and there would be no way to say the bracket if it were optional.
8288        assert_eq!(
8289            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
8290            "-ERR min or max not valid string range item\r\n"
8291        );
8292    }
8293
8294    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
8295    ///
8296    /// Every byte in here was read off a real 8.10.1 rather than worked out,
8297    /// because the interesting part of this command is not what it selects, it
8298    /// is which of the two ends the client is expected to name first.
8299    #[test]
8300    fn one_range_command_selects_by_rank_or_score_or_name() {
8301        let mut f = Fixture::new();
8302        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8303        assert_eq!(
8304            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8305            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8306        );
8307        assert_eq!(
8308            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
8309            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8310        );
8311        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
8312        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
8313        // REV over ranks reverses the walk and leaves the two arguments alone,
8314        // because a rank counts from the end the walk starts at.
8315        assert_eq!(
8316            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
8317            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8318        );
8319        assert_eq!(
8320            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
8321            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8322        );
8323        // And REV over scores does swap them, since a bound does not count from
8324        // anywhere. This is the one line of the parse that tells the two apart.
8325        assert_eq!(
8326            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
8327            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8328        );
8329        assert_eq!(
8330            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
8331            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8332        );
8333        assert_eq!(
8334            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
8335            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8336        );
8337    }
8338
8339    /// The older spellings, which are the same six windows with the mode in the
8340    /// name and the high end named first on the three that go backwards.
8341    #[test]
8342    fn the_older_range_spellings_name_their_high_end_first() {
8343        let mut f = Fixture::new();
8344        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8345        assert_eq!(
8346            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
8347            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8348        );
8349        assert_eq!(
8350            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
8351            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8352        );
8353        assert_eq!(
8354            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
8355            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8356        );
8357        assert_eq!(
8358            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
8359            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8360        );
8361        // The two arguments the wrong way round is an empty answer and not an
8362        // error, which is what the swap being in the parse rather than in the
8363        // window buys.
8364        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
8365        assert_eq!(
8366            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
8367            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8368        );
8369        assert_eq!(
8370            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
8371            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
8372        );
8373        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
8374        // way of spelling the mode, they are a syntax error.
8375        for cmd in [
8376            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
8377            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
8378            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
8379        ] {
8380            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
8381        }
8382    }
8383
8384    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
8385    /// only some of them accept.
8386    #[test]
8387    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
8388        let mut f = Fixture::new();
8389        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8390        assert_eq!(
8391            f.run(&[
8392                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
8393            ]),
8394            "*1\r\n$1\r\nb\r\n"
8395        );
8396        // A negative offset skips past everything, a negative count is no bound.
8397        assert_eq!(
8398            f.run(&[
8399                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
8400            ]),
8401            "*0\r\n"
8402        );
8403        assert_eq!(
8404            f.run(&[
8405                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
8406            ]),
8407            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8408        );
8409        // The two options in either order, which falls out of the parse loop.
8410        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";
8411        assert_eq!(
8412            f.run(&[
8413                b"ZRANGEBYSCORE",
8414                b"z",
8415                b"1",
8416                b"3",
8417                b"WITHSCORES",
8418                b"LIMIT",
8419                b"0",
8420                b"2"
8421            ]),
8422            both
8423        );
8424        assert_eq!(
8425            f.run(&[
8426                b"ZRANGEBYSCORE",
8427                b"z",
8428                b"1",
8429                b"3",
8430                b"LIMIT",
8431                b"0",
8432                b"2",
8433                b"WITHSCORES"
8434            ]),
8435            both
8436        );
8437        // LIMIT on a range by rank is refused after the whole option list has
8438        // been read, so this complains about LIMIT and not about WITHSCORES.
8439        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
8440        assert_eq!(
8441            f.run(&[
8442                b"ZREVRANGE",
8443                b"z",
8444                b"0",
8445                b"-1",
8446                b"WITHSCORES",
8447                b"LIMIT",
8448                b"0",
8449                b"1"
8450            ]),
8451            needs_by
8452        );
8453        assert_eq!(
8454            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
8455            needs_by
8456        );
8457        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
8458        assert_eq!(
8459            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
8460            not_bylex
8461        );
8462        assert_eq!(
8463            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
8464            not_bylex
8465        );
8466        // Two modes at once, an option nobody knows, a LIMIT missing its count,
8467        // and the three number errors, which are three different sentences.
8468        for cmd in [
8469            &[
8470                b"ZRANGE".as_slice(),
8471                b"z",
8472                b"0",
8473                b"-1",
8474                b"BYSCORE",
8475                b"BYLEX",
8476            ][..],
8477            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
8478            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
8479        ] {
8480            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8481        }
8482        assert_eq!(
8483            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8484            "-ERR min or max is not a float\r\n"
8485        );
8486        assert_eq!(
8487            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8488            "-ERR min or max not valid string range item\r\n"
8489        );
8490        assert_eq!(
8491            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8492            "-ERR value is not an integer or out of range\r\n"
8493        );
8494    }
8495
8496    /// `WITHSCORES` is the one place in this group where the two protocols
8497    /// disagree about the shape of the reply and not just the type of a value.
8498    #[test]
8499    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8500        let mut f = Fixture::new();
8501        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8502        assert_eq!(
8503            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8504            "*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"
8505        );
8506        f.out = Out::new(Proto::Resp3);
8507        assert_eq!(
8508            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8509            "*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"
8510        );
8511        assert_eq!(
8512            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8513            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8514        );
8515    }
8516
8517    /// The store form, which is the same parse with the destination in front.
8518    #[test]
8519    fn a_range_store_writes_the_window_into_another_key() {
8520        let mut f = Fixture::new();
8521        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8522        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8523        // A window that selects nothing deletes the destination rather than
8524        // leaving an empty sorted set, because an empty one does not exist.
8525        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8526        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8527        assert_eq!(
8528            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8529            ":2\r\n"
8530        );
8531        assert_eq!(
8532            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8533            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8534        );
8535        // The destination is allowed to be the source, because the result is
8536        // built whole before anything is written over.
8537        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8538        assert_eq!(
8539            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8540            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8541        );
8542        // It takes every option ZRANGE takes except WITHSCORES, which is a
8543        // plain syntax error here and not the sentence about BYLEX.
8544        assert_eq!(
8545            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8546            "-ERR syntax error\r\n"
8547        );
8548    }
8549
8550    /// The three removals, which are the read side's window with the walk
8551    /// turned into a removal and no options at all.
8552    #[test]
8553    fn the_three_removals_share_their_window_with_the_reads() {
8554        let mut f = Fixture::new();
8555        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8556        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8557        assert_eq!(
8558            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8559            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8560        );
8561        assert_eq!(
8562            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8563            ":1\r\n"
8564        );
8565        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8566        // The last member going takes the key with it.
8567        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8568        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8569        assert_eq!(
8570            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8571            ":0\r\n"
8572        );
8573        assert_eq!(
8574            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8575            "-ERR value is not an integer or out of range\r\n"
8576        );
8577    }
8578
8579    /// The algebra, which is one gather and three names for it.
8580    #[test]
8581    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8582        let mut f = Fixture::new();
8583        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8584        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8585        assert_eq!(
8586            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8587            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8588        );
8589        // The scores are added where a member is in both, and the answer comes
8590        // out in the order those combined scores put it in.
8591        assert_eq!(
8592            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8593            "*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"
8594        );
8595        assert_eq!(
8596            f.run(&[
8597                b"ZUNION",
8598                b"2",
8599                b"z",
8600                b"y",
8601                b"WEIGHTS",
8602                b"2",
8603                b"3",
8604                b"WITHSCORES"
8605            ]),
8606            "*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"
8607        );
8608        assert_eq!(
8609            f.run(&[
8610                b"ZUNION",
8611                b"2",
8612                b"z",
8613                b"y",
8614                b"AGGREGATE",
8615                b"MIN",
8616                b"WITHSCORES"
8617            ]),
8618            "*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"
8619        );
8620        assert_eq!(
8621            f.run(&[
8622                b"ZUNION",
8623                b"2",
8624                b"z",
8625                b"y",
8626                b"AGGREGATE",
8627                b"MAX",
8628                b"WITHSCORES"
8629            ]),
8630            "*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"
8631        );
8632        assert_eq!(
8633            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8634            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8635        );
8636        assert_eq!(
8637            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8638            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8639        );
8640        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8641        // A plain set is an input, and it behaves as a sorted set in which
8642        // every member scores one.
8643        f.run(&[b"SADD", b"p", b"a", b"d"]);
8644        assert_eq!(
8645            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8646            "*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"
8647        );
8648        // A difference never combines two scores, so it has nothing for either
8649        // of the two options to do and refuses both.
8650        for cmd in [
8651            &[
8652                b"ZDIFF".as_slice(),
8653                b"2",
8654                b"z",
8655                b"y",
8656                b"WEIGHTS",
8657                b"1",
8658                b"1",
8659            ][..],
8660            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8661        ] {
8662            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8663        }
8664    }
8665
8666    /// The count of keys, which is what lets a key be named `WEIGHTS`.
8667    #[test]
8668    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8669        let mut f = Fixture::new();
8670        f.run(&[b"ZADD", b"z", b"1", b"a"]);
8671        f.run(&[b"ZADD", b"y", b"2", b"b"]);
8672        // Redis names the command in this one, so each spelling says its own.
8673        assert_eq!(
8674            f.run(&[b"ZUNION", b"0", b"z"]),
8675            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8676        );
8677        assert_eq!(
8678            f.run(&[b"ZUNION", b"-1", b"z"]),
8679            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8680        );
8681        assert_eq!(
8682            f.run(&[b"ZINTERCARD", b"0", b"z"]),
8683            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8684        );
8685        // A count bigger than the line is a plain syntax error, which reads
8686        // oddly and is what Redis says.
8687        assert_eq!(
8688            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8689            "-ERR syntax error\r\n"
8690        );
8691        assert_eq!(
8692            f.run(&[b"ZUNION", b"x", b"z"]),
8693            "-ERR value is not an integer or out of range\r\n"
8694        );
8695        // A WEIGHTS list that is not one per key is a syntax error, and a
8696        // weight that is not a number gets a sentence of its own.
8697        assert_eq!(
8698            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8699            "-ERR syntax error\r\n"
8700        );
8701        assert_eq!(
8702            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8703            "-ERR weight value is not a float\r\n"
8704        );
8705        assert_eq!(
8706            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8707            "-ERR syntax error\r\n"
8708        );
8709    }
8710
8711    /// The three store forms, which answer a count and take no WITHSCORES.
8712    #[test]
8713    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8714        let mut f = Fixture::new();
8715        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8716        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8717        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8718        assert_eq!(
8719            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8720            "*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"
8721        );
8722        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8723        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8724        // An empty result deletes the destination rather than leaving an empty
8725        // sorted set, because an empty one does not exist.
8726        assert_eq!(
8727            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8728            ":0\r\n"
8729        );
8730        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8731        // The destination is allowed to name its own source.
8732        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8733        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8734        for cmd in [
8735            &[
8736                b"ZUNIONSTORE".as_slice(),
8737                b"d",
8738                b"2",
8739                b"z",
8740                b"y",
8741                b"WITHSCORES",
8742            ][..],
8743            &[
8744                b"ZDIFFSTORE",
8745                b"d",
8746                b"2",
8747                b"z",
8748                b"y",
8749                b"WEIGHTS",
8750                b"1",
8751                b"1",
8752            ],
8753        ] {
8754            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8755        }
8756    }
8757
8758    /// `ZINTERCARD`, which counts without building anything.
8759    #[test]
8760    fn intercard_counts_and_stops_at_its_limit() {
8761        let mut f = Fixture::new();
8762        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8763        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8764        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8765        // A limit of zero is no limit, which is Redis's reading of it.
8766        assert_eq!(
8767            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8768            ":2\r\n"
8769        );
8770        assert_eq!(
8771            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8772            ":1\r\n"
8773        );
8774        // A negative limit and a limit that is not a number at all get the same
8775        // sentence, which looks like a mistake in Redis and is copied as one.
8776        let bad = "-ERR LIMIT can't be negative\r\n";
8777        assert_eq!(
8778            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8779            bad
8780        );
8781        assert_eq!(
8782            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8783            bad
8784        );
8785        for cmd in [
8786            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8787            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8788            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8789        ] {
8790            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8791        }
8792    }
8793
8794    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
8795    #[test]
8796    fn a_draw_answers_one_member_or_an_array_of_them() {
8797        let mut f = Fixture::new();
8798        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8799        // No count is one member or a nil, a count is an array that may be
8800        // empty, and those are two reply types the client has to tell apart.
8801        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8802        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8803        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8804        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8805        // A positive count draws without replacement, so a count over the size
8806        // answers the whole set and never a member twice.
8807        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8808        assert!(all.starts_with("*3\r\n"), "{all}");
8809        for m in ["a", "b", "c"] {
8810            assert!(all.contains(m), "{all}");
8811        }
8812        // A negative one draws with replacement and answers exactly as many as
8813        // it was asked for, whatever the size of the set.
8814        assert!(
8815            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8816            "five draws with replacement"
8817        );
8818        assert!(
8819            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8820                .starts_with("*4\r\n"),
8821            "two pairs, flat on RESP2"
8822        );
8823        f.out = Out::new(Proto::Resp3);
8824        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8825        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8826        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8827        f.out = Out::new(Proto::Resp2);
8828        assert_eq!(
8829            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8830            "-ERR syntax error\r\n"
8831        );
8832        assert_eq!(
8833            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8834            "-ERR value is not an integer or out of range\r\n"
8835        );
8836    }
8837
8838    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
8839    #[test]
8840    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8841        let mut f = Fixture::new();
8842        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8843        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";
8844        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8845        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8846        assert_eq!(
8847            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8848            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8849        );
8850        assert_eq!(
8851            f.run(&[b"ZSCAN", b"nokey", b"0"]),
8852            "*2\r\n$1\r\n0\r\n*0\r\n"
8853        );
8854        // A score stays a bulk string on RESP3, which is the one place the two
8855        // protocols agree about a score and everywhere else they do not.
8856        f.out = Out::new(Proto::Resp3);
8857        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8858        f.out = Out::new(Proto::Resp2);
8859        assert_eq!(
8860            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8861            "-ERR NOVALUES option can only be used in HSCAN\r\n"
8862        );
8863        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8864        assert_eq!(
8865            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8866            "-ERR syntax error\r\n"
8867        );
8868    }
8869
8870    /// The count is what decides the shape, and its value is not.
8871    #[test]
8872    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8873        let mut f = Fixture::new();
8874        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8875        // No count, so one flat pair, and the score is a bulk string on RESP2.
8876        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8877        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8878        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8879        // A count, so pairs, and on RESP2 they are flattened into one run.
8880        assert_eq!(
8881            f.run(&[b"ZPOPMIN", b"z", b"2"]),
8882            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8883        );
8884        // An empty array rather than a null, which is where a sorted set pop and
8885        // a list pop part company, and the same answer a count of zero gives.
8886        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8887        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8888        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8889        // The last member takes the key with it.
8890        assert_eq!(
8891            f.run(&[b"ZPOPMIN", b"z", b"9"]),
8892            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8893        );
8894        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8895
8896        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8897        f.out = Out::new(Proto::Resp3);
8898        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8899        assert_eq!(
8900            f.run(&[b"ZPOPMIN", b"z", b"1"]),
8901            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8902        );
8903        f.out = Out::new(Proto::Resp2);
8904        // Both of these are the range error rather than the usual sentence about
8905        // integers, which is the odd answer and so the one worth copying.
8906        let bad = "-ERR value is out of range, must be positive\r\n";
8907        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8908        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8909        assert_eq!(
8910            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8911            "-ERR syntax error\r\n"
8912        );
8913    }
8914
8915    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
8916    #[test]
8917    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8918        let mut f = Fixture::new();
8919        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8920        assert_eq!(
8921            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8922            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8923        );
8924        // Nested on RESP2 as well, because the key name is already in front of
8925        // the pairs and there is nothing left to flatten into.
8926        assert_eq!(
8927            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8928            "*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"
8929        );
8930        // A null array and not a null, the same as LMPOP.
8931        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8932        f.out = Out::new(Proto::Resp3);
8933        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8934        f.out = Out::new(Proto::Resp2);
8935        let numkeys = "-ERR numkeys should be greater than 0\r\n";
8936        for bad in [
8937            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8938            &[b"ZMPOP", b"-1", b"z", b"MIN"],
8939            &[b"ZMPOP", b"x", b"z", b"MIN"],
8940        ] {
8941            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8942        }
8943        let count = "-ERR count should be greater than 0\r\n";
8944        for bad in [
8945            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8946            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8947            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8948        ] {
8949            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8950        }
8951        let syntax = "-ERR syntax error\r\n";
8952        for bad in [
8953            // Two keys named and one given, so the word that should have been
8954            // the direction is a key and there is no direction left.
8955            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8956            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8957            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8958            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8959        ] {
8960            assert_eq!(f.run(bad), syntax, "{bad:?}");
8961        }
8962    }
8963
8964    /// The three that wait, when there is something there and they do not have
8965    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
8966    #[test]
8967    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8968        let mut f = Fixture::new();
8969        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8970        assert_eq!(
8971            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8972            (
8973                Flow::Continue,
8974                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8975            )
8976        );
8977        assert_eq!(
8978            f.run(&[b"BZPOPMAX", b"z", b"0"]),
8979            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8980        );
8981        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8982        assert_eq!(
8983            f.run(&[
8984                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8985            ]),
8986            "*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"
8987        );
8988        f.out = Out::new(Proto::Resp3);
8989        assert_eq!(
8990            f.run(&[b"BZPOPMIN", b"z", b"0"]),
8991            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8992        );
8993        f.out = Out::new(Proto::Resp2);
8994        // Nothing to take, so the client is parked and nothing was written.
8995        assert_eq!(
8996            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8997            (Flow::Block, String::new())
8998        );
8999        assert_eq!(
9000            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
9001            (Flow::Block, String::new())
9002        );
9003        // The timeout is read before the key count, so this complains about the
9004        // timeout and not about the count.
9005        assert_eq!(
9006            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
9007            "-ERR timeout is not a float or out of range\r\n"
9008        );
9009        assert_eq!(
9010            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
9011            "-ERR numkeys should be greater than 0\r\n"
9012        );
9013        assert_eq!(
9014            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
9015            "-ERR timeout is negative\r\n"
9016        );
9017    }
9018
9019    /// A parked sorted set client is served by whatever puts a member under one
9020    /// of its keys, and is not served by something of another type landing
9021    /// there.
9022    #[test]
9023    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
9024        let mut f = Fixture::new();
9025        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
9026        assert_eq!(f.server.parked(), 1);
9027        // A string under the key is not what it asked for, so it stays parked
9028        // rather than being handed a WRONGTYPE on a command that was accepted.
9029        f.run(&[b"SET", b"z", b"v"]);
9030        let mut out = Out::new(Proto::Resp2);
9031        assert!(!f.server.serve_waiter(0, 0, &mut out));
9032        assert!(out.as_slice().is_empty());
9033        f.run(&[b"DEL", b"z"]);
9034        f.run(&[b"ZADD", b"z", b"5", b"m"]);
9035        assert!(f.server.serve_waiter(0, 0, &mut out));
9036        assert_eq!(
9037            core::str::from_utf8(out.as_slice()).expect("ascii"),
9038            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
9039        );
9040        // And the member is gone, which is what makes a queue of workers on a
9041        // sorted set work at all.
9042        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
9043    }
9044
9045    #[test]
9046    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
9047        let mut f = Fixture::new();
9048        f.run(&[b"SET", b"s", b"v"]);
9049        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9050        for cmd in [
9051            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
9052            &[b"ZINCRBY", b"s", b"1", b"a"],
9053            &[b"ZCARD", b"s"],
9054            &[b"ZSCORE", b"s", b"a"],
9055            &[b"ZMSCORE", b"s", b"a"],
9056            &[b"ZREM", b"s", b"a"],
9057            &[b"ZRANK", b"s", b"a"],
9058            &[b"ZREVRANK", b"s", b"a"],
9059            &[b"ZCOUNT", b"s", b"1", b"2"],
9060            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
9061            &[b"ZRANGE", b"s", b"0", b"-1"],
9062            &[b"ZREVRANGE", b"s", b"0", b"-1"],
9063            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
9064            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
9065            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
9066            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
9067            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
9068            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
9069            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
9070            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
9071            &[b"ZUNION", b"1", b"s"],
9072            &[b"ZINTER", b"1", b"s"],
9073            &[b"ZDIFF", b"1", b"s"],
9074            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
9075            &[b"ZINTERSTORE", b"d", b"1", b"s"],
9076            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
9077            &[b"ZINTERCARD", b"1", b"s"],
9078            &[b"ZRANDMEMBER", b"s"],
9079            &[b"ZSCAN", b"s", b"0"],
9080            &[b"ZPOPMIN", b"s"],
9081            &[b"ZPOPMAX", b"s", b"2"],
9082            &[b"ZMPOP", b"1", b"s", b"MIN"],
9083            &[b"BZPOPMIN", b"s", b"0"],
9084            &[b"BZPOPMAX", b"s", b"0"],
9085            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
9086        ] {
9087            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
9088        }
9089        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
9090    }
9091
9092    /// The same churn the set, the string and the list get, because a sorted
9093    /// set that leaks a tree node per add looks exactly like one that does not
9094    /// until it has run for an afternoon.
9095    #[test]
9096    fn churning_sorted_sets_does_not_grow_the_server() {
9097        let mut f = Fixture::new();
9098        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
9099        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
9100        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
9101        for i in 0..200 {
9102            args.push(&scores[i]);
9103            args.push(&members[i]);
9104        }
9105
9106        f.run(&args);
9107        f.run(&[b"DEL", b"z"]);
9108        f.server.compact_step();
9109        let after_first = f.server.memory_bytes();
9110
9111        for _ in 0..200 {
9112            f.run(&args);
9113            f.run(&[b"DEL", b"z"]);
9114            f.server.compact_step();
9115        }
9116        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9117        assert!(
9118            f.server.memory_bytes() <= after_first * 2,
9119            "held {} after two hundred passes against {after_first} after one",
9120            f.server.memory_bytes()
9121        );
9122    }
9123
9124    // ------------------------------------------------------------------- geo
9125
9126    /// The three places every Redis geo example uses, and one more.
9127    ///
9128    /// Every reply this section asserts on came off a running 8.10.1 with these
9129    /// three loaded, byte for byte, including the number of digits in a
9130    /// coordinate and the four places on a distance.
9131    fn sicily(f: &mut Fixture) {
9132        f.run(&[
9133            b"GEOADD",
9134            b"Sicily",
9135            b"13.361389",
9136            b"38.115556",
9137            b"Palermo",
9138            b"15.087269",
9139            b"37.502669",
9140            b"Catania",
9141        ]);
9142        f.run(&[
9143            b"GEOADD",
9144            b"Sicily",
9145            b"13.583333",
9146            b"37.316667",
9147            b"Agrigento",
9148        ]);
9149    }
9150
9151    #[test]
9152    fn places_go_in_as_scores_and_come_back_as_positions() {
9153        let mut f = Fixture::new();
9154        assert_eq!(
9155            f.run(&[
9156                b"GEOADD",
9157                b"Sicily",
9158                b"13.361389",
9159                b"38.115556",
9160                b"Palermo",
9161                b"15.087269",
9162                b"37.502669",
9163                b"Catania"
9164            ]),
9165            ":2\r\n"
9166        );
9167        // A geo key is a sorted set and says so, which is not an implementation
9168        // detail either: a client removes a place with ZREM and counts them
9169        // with ZCARD, and the score is the number a real server stores.
9170        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
9171        assert_eq!(
9172            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
9173            "$16\r\n3479099956230698\r\n"
9174        );
9175        assert_eq!(
9176            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
9177            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
9178        );
9179        assert_eq!(
9180            f.run(&[
9181                b"GEOHASH",
9182                b"Sicily",
9183                b"Palermo",
9184                b"Catania",
9185                b"NonExisting"
9186            ]),
9187            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
9188        );
9189        // A key that is not there is an empty one, and the two nulls are not
9190        // the same null: GEOPOS answers the array one and GEOHASH the string
9191        // one, which a RESP2 client can tell apart.
9192        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
9193        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
9194    }
9195
9196    #[test]
9197    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
9198        let mut f = Fixture::new();
9199        sicily(&mut f);
9200        assert_eq!(
9201            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
9202            "$11\r\n166274.1516\r\n"
9203        );
9204        assert_eq!(
9205            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
9206            "$8\r\n166.2742\r\n"
9207        );
9208        assert_eq!(
9209            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
9210            "$8\r\n103.3182\r\n"
9211        );
9212        // A member that is not there and a key that is not there are the same
9213        // nil, and the unit is read before the key is looked up, so a bad unit
9214        // on a missing key is still an error.
9215        assert_eq!(
9216            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
9217            "$-1\r\n"
9218        );
9219        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
9220        assert_eq!(
9221            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
9222            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
9223        );
9224        assert_eq!(
9225            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
9226            "-ERR syntax error\r\n"
9227        );
9228    }
9229
9230    #[test]
9231    fn a_search_finds_what_is_inside_it_nearest_first() {
9232        let mut f = Fixture::new();
9233        sicily(&mut f);
9234        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
9235        assert_eq!(
9236            f.run(&[
9237                b"GEOSEARCH",
9238                b"Sicily",
9239                b"FROMLONLAT",
9240                b"15",
9241                b"37",
9242                b"BYRADIUS",
9243                b"200",
9244                b"km",
9245                b"ASC"
9246            ]),
9247            all
9248        );
9249        // The older spelling of the same search, which is the same nine boxes
9250        // and the same order.
9251        assert_eq!(
9252            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
9253            all
9254        );
9255        assert_eq!(
9256            f.run(&[
9257                b"GEORADIUS_RO",
9258                b"Sicily",
9259                b"15",
9260                b"37",
9261                b"200",
9262                b"km",
9263                b"ASC"
9264            ]),
9265            all
9266        );
9267        // A count with no ordering means the nearest ones, so DESC has to be
9268        // asked for to get the far end.
9269        assert_eq!(
9270            f.run(&[
9271                b"GEORADIUS",
9272                b"Sicily",
9273                b"15",
9274                b"37",
9275                b"200",
9276                b"km",
9277                b"DESC",
9278                b"COUNT",
9279                b"1"
9280            ]),
9281            "*1\r\n$7\r\nPalermo\r\n"
9282        );
9283        assert_eq!(
9284            f.run(&[
9285                b"GEORADIUS",
9286                b"Sicily",
9287                b"15",
9288                b"37",
9289                b"200",
9290                b"km",
9291                b"COUNT",
9292                b"1"
9293            ]),
9294            "*1\r\n$7\r\nCatania\r\n"
9295        );
9296        // Nothing inside a kilometre of that point, and nothing in a key that
9297        // is not there, and both are the empty array rather than an error.
9298        let empty = "*0\r\n";
9299        assert_eq!(
9300            f.run(&[
9301                b"GEOSEARCH",
9302                b"Sicily",
9303                b"FROMLONLAT",
9304                b"15",
9305                b"37",
9306                b"BYRADIUS",
9307                b"1",
9308                b"km"
9309            ]),
9310            empty
9311        );
9312        assert_eq!(
9313            f.run(&[
9314                b"GEOSEARCH",
9315                b"nokey",
9316                b"FROMLONLAT",
9317                b"15",
9318                b"37",
9319                b"BYRADIUS",
9320                b"1",
9321                b"km"
9322            ]),
9323            empty
9324        );
9325        assert_eq!(
9326            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
9327            empty
9328        );
9329    }
9330
9331    #[test]
9332    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
9333        let mut f = Fixture::new();
9334        sicily(&mut f);
9335        assert_eq!(
9336            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
9337            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9338        );
9339        // The member itself is nothing away from itself, which is where the
9340        // fixed point writer's zero shows up on the wire.
9341        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";
9342        assert_eq!(
9343            f.run(&[
9344                b"GEORADIUSBYMEMBER_RO",
9345                b"Sicily",
9346                b"Agrigento",
9347                b"100",
9348                b"km",
9349                b"WITHDIST"
9350            ]),
9351            with_dist
9352        );
9353        assert_eq!(
9354            f.run(&[
9355                b"GEOSEARCH",
9356                b"Sicily",
9357                b"FROMMEMBER",
9358                b"Agrigento",
9359                b"BYRADIUS",
9360                b"100",
9361                b"km",
9362                b"ASC",
9363                b"WITHDIST"
9364            ]),
9365            with_dist
9366        );
9367        assert_eq!(
9368            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
9369            "-ERR could not decode requested zset member\r\n"
9370        );
9371    }
9372
9373    #[test]
9374    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
9375        let mut f = Fixture::new();
9376        sicily(&mut f);
9377        // Three options asked for, so each result is a four element array of
9378        // the member, the distance, the hash and a pair. The order of the three
9379        // is Redis's and not the order they were written in the command.
9380        assert_eq!(
9381            f.run(&[
9382                b"GEOSEARCH",
9383                b"Sicily",
9384                b"FROMLONLAT",
9385                b"15",
9386                b"37",
9387                b"BYBOX",
9388                b"400",
9389                b"400",
9390                b"km",
9391                b"ASC",
9392                b"WITHCOORD",
9393                b"WITHDIST",
9394                b"WITHHASH"
9395            ]),
9396            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
9397             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
9398             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
9399             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
9400             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
9401             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
9402        );
9403    }
9404
9405    #[test]
9406    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
9407        let mut f = Fixture::new();
9408        sicily(&mut f);
9409        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
9410                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
9411                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
9412        assert_eq!(
9413            f.run(&[
9414                b"GEOSEARCHSTORE",
9415                b"dst",
9416                b"Sicily",
9417                b"FROMLONLAT",
9418                b"15",
9419                b"37",
9420                b"BYRADIUS",
9421                b"200",
9422                b"km",
9423                b"ASC"
9424            ]),
9425            ":3\r\n"
9426        );
9427        assert_eq!(
9428            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
9429            hashes
9430        );
9431        // The same again through the older spelling, which stores the same
9432        // scores, so a key written by either is a geo key.
9433        assert_eq!(
9434            f.run(&[
9435                b"GEORADIUS",
9436                b"Sicily",
9437                b"15",
9438                b"37",
9439                b"200",
9440                b"km",
9441                b"STORE",
9442                b"dst3"
9443            ]),
9444            ":3\r\n"
9445        );
9446        assert_eq!(
9447            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
9448            hashes
9449        );
9450        // STOREDIST stores the distance in the search unit instead, and those
9451        // are full doubles rather than the four places WITHDIST writes. The
9452        // numbers on the right are what 8.10.1 stored for this search, and they
9453        // are compared with a tolerance rather than byte for byte because the
9454        // last bit of a haversine is the platform's sin, cos and asin: this
9455        // machine and that one disagree in the sixteenth digit, and so do two
9456        // Redis builds. Everything a client actually reads back is four places
9457        // and is asserted exactly above.
9458        assert_eq!(
9459            f.run(&[
9460                b"GEOSEARCHSTORE",
9461                b"dst2",
9462                b"Sicily",
9463                b"FROMLONLAT",
9464                b"15",
9465                b"37",
9466                b"BYRADIUS",
9467                b"200",
9468                b"km",
9469                b"ASC",
9470                b"STOREDIST"
9471            ]),
9472            ":3\r\n"
9473        );
9474        for (member, want) in [
9475            ("Catania", 56.441_257_870_158_19),
9476            ("Agrigento", 130.423_487_067_147_14),
9477            ("Palermo", 190.442_429_847_757_92),
9478        ] {
9479            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
9480            let got: f64 = reply
9481                .trim_start_matches(|c: char| c != '\n')
9482                .trim()
9483                .parse()
9484                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9485            assert!(
9486                (got - want).abs() < 1e-9,
9487                "{member} scored {got} not {want}"
9488            );
9489        }
9490        // The order they went in is the order the scores put them in, which is
9491        // the point of storing the distance rather than the hash.
9492        assert_eq!(
9493            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9494            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9495        );
9496        // A search that finds nothing takes the destination with it rather than
9497        // leaving what was there, and a source key that is not there is a
9498        // search that finds nothing.
9499        assert_eq!(
9500            f.run(&[
9501                b"GEOSEARCHSTORE",
9502                b"dst",
9503                b"nokey",
9504                b"FROMLONLAT",
9505                b"15",
9506                b"37",
9507                b"BYRADIUS",
9508                b"200",
9509                b"km"
9510            ]),
9511            ":0\r\n"
9512        );
9513        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9514    }
9515
9516    #[test]
9517    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9518        let mut f = Fixture::new();
9519        sicily(&mut f);
9520        // XX on a member that is already where it is changes nothing, and NX on
9521        // one that is there refuses to move it.
9522        assert_eq!(
9523            f.run(&[
9524                b"GEOADD",
9525                b"Sicily",
9526                b"XX",
9527                b"CH",
9528                b"13.361389",
9529                b"38.115556",
9530                b"Palermo"
9531            ]),
9532            ":0\r\n"
9533        );
9534        assert_eq!(
9535            f.run(&[
9536                b"GEOADD",
9537                b"Sicily",
9538                b"NX",
9539                b"13.361389",
9540                b"38.9",
9541                b"Palermo"
9542            ]),
9543            ":0\r\n"
9544        );
9545        assert_eq!(
9546            f.run(&[
9547                b"GEOADD",
9548                b"Sicily",
9549                b"CH",
9550                b"13.361389",
9551                b"38.9",
9552                b"Palermo"
9553            ]),
9554            ":1\r\n"
9555        );
9556        // Out of range, and nothing is stored: the whole call is refused rather
9557        // than the good pairs going in and the bad one stopping it.
9558        assert_eq!(
9559            f.run(&[
9560                b"GEOADD",
9561                b"new",
9562                b"13.361389",
9563                b"38.115556",
9564                b"here",
9565                b"181",
9566                b"38",
9567                b"there"
9568            ]),
9569            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9570        );
9571        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9572        assert_eq!(
9573            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9574            "-ERR value is not a valid float\r\n"
9575        );
9576        // The count of triples is checked before the two gates are, and a call
9577        // with no triples at all reaches the same sentence.
9578        assert_eq!(
9579            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9580            "-ERR syntax error\r\n"
9581        );
9582        assert_eq!(
9583            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9584            "-ERR syntax error\r\n"
9585        );
9586        assert_eq!(
9587            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9588            "-ERR syntax error\r\n"
9589        );
9590        assert_eq!(
9591            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9592            "-ERR wrong number of arguments for 'geoadd' command\r\n"
9593        );
9594    }
9595
9596    /// The sentences a search answers, which are its contract as much as the
9597    /// results are.
9598    #[test]
9599    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9600        let mut f = Fixture::new();
9601        sicily(&mut f);
9602        let cases: &[(&[&[u8]], &str)] = &[
9603            (
9604                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9605                "-ERR need numeric radius\r\n",
9606            ),
9607            (
9608                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9609                "-ERR radius cannot be negative\r\n",
9610            ),
9611            (
9612                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9613                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9614            ),
9615            (
9616                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9617                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9618            ),
9619            (
9620                &[
9621                    b"GEOSEARCH",
9622                    b"Sicily",
9623                    b"FROMLONLAT",
9624                    b"15",
9625                    b"37",
9626                    b"BYBOX",
9627                    b"x",
9628                    b"1",
9629                    b"km",
9630                ],
9631                "-ERR need numeric width\r\n",
9632            ),
9633            (
9634                &[
9635                    b"GEOSEARCH",
9636                    b"Sicily",
9637                    b"FROMLONLAT",
9638                    b"15",
9639                    b"37",
9640                    b"BYBOX",
9641                    b"1",
9642                    b"y",
9643                    b"km",
9644                ],
9645                "-ERR need numeric height\r\n",
9646            ),
9647            (
9648                &[
9649                    b"GEOSEARCH",
9650                    b"Sicily",
9651                    b"FROMLONLAT",
9652                    b"15",
9653                    b"37",
9654                    b"BYBOX",
9655                    b"-1",
9656                    b"1",
9657                    b"km",
9658                ],
9659                "-ERR height or width cannot be negative\r\n",
9660            ),
9661            (
9662                &[
9663                    b"GEOSEARCH",
9664                    b"Sicily",
9665                    b"FROMLONLAT",
9666                    b"15",
9667                    b"37",
9668                    b"BYRADIUS",
9669                    b"1",
9670                    b"km",
9671                    b"ANY",
9672                ],
9673                "-ERR the ANY argument requires COUNT argument\r\n",
9674            ),
9675            (
9676                &[
9677                    b"GEOSEARCH",
9678                    b"Sicily",
9679                    b"FROMLONLAT",
9680                    b"15",
9681                    b"37",
9682                    b"BYRADIUS",
9683                    b"1",
9684                    b"km",
9685                    b"COUNT",
9686                    b"0",
9687                ],
9688                "-ERR COUNT must be > 0\r\n",
9689            ),
9690            (
9691                &[
9692                    b"GEOSEARCH",
9693                    b"Sicily",
9694                    b"BYRADIUS",
9695                    b"1",
9696                    b"km",
9697                    b"BYBOX",
9698                    b"1",
9699                    b"1",
9700                    b"km",
9701                ],
9702                "-ERR syntax error\r\n",
9703            ),
9704            (
9705                &[
9706                    b"GEOSEARCH",
9707                    b"Sicily",
9708                    b"FROMMEMBER",
9709                    b"Palermo",
9710                    b"FROMLONLAT",
9711                    b"1",
9712                    b"2",
9713                    b"BYRADIUS",
9714                    b"1",
9715                    b"km",
9716                ],
9717                "-ERR syntax error\r\n",
9718            ),
9719            // The two options a GEOSEARCH cannot leave out, each with its own
9720            // sentence, and the command quoted the way the client spelled it.
9721            (
9722                &[
9723                    b"geosearch",
9724                    b"Sicily",
9725                    b"BYRADIUS",
9726                    b"1",
9727                    b"km",
9728                    b"ASC",
9729                    b"WITHDIST",
9730                ],
9731                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9732            ),
9733            (
9734                &[
9735                    b"GEOSEARCH",
9736                    b"Sicily",
9737                    b"FROMLONLAT",
9738                    b"15",
9739                    b"37",
9740                    b"ASC",
9741                    b"WITHDIST",
9742                ],
9743                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9744            ),
9745            // A store cannot also be asked for the distance, and the two
9746            // families name themselves differently in the same sentence.
9747            (
9748                &[
9749                    b"GEOSEARCHSTORE",
9750                    b"d",
9751                    b"Sicily",
9752                    b"FROMLONLAT",
9753                    b"15",
9754                    b"37",
9755                    b"BYRADIUS",
9756                    b"1",
9757                    b"km",
9758                    b"WITHCOORD",
9759                ],
9760                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9761            ),
9762            (
9763                &[
9764                    b"GEORADIUS",
9765                    b"Sicily",
9766                    b"15",
9767                    b"37",
9768                    b"1",
9769                    b"km",
9770                    b"WITHDIST",
9771                    b"STORE",
9772                    b"d",
9773                ],
9774                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9775            ),
9776            // The read only forms have no store at all, so the word is a stray
9777            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
9778            (
9779                &[
9780                    b"GEORADIUS_RO",
9781                    b"Sicily",
9782                    b"15",
9783                    b"37",
9784                    b"1",
9785                    b"km",
9786                    b"STORE",
9787                    b"d",
9788                ],
9789                "-ERR syntax error\r\n",
9790            ),
9791            (
9792                &[
9793                    b"GEOSEARCH",
9794                    b"Sicily",
9795                    b"FROMLONLAT",
9796                    b"15",
9797                    b"37",
9798                    b"BYRADIUS",
9799                    b"1",
9800                    b"km",
9801                    b"STOREDIST",
9802                ],
9803                "-ERR syntax error\r\n",
9804            ),
9805        ];
9806        for (parts, want) in cases {
9807            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9808        }
9809    }
9810
9811    /// A wrong type wins over a bad argument, because the key is looked up
9812    /// first, and every one of the ten says the same thing about it.
9813    #[test]
9814    fn every_geo_command_says_wrongtype() {
9815        let mut f = Fixture::new();
9816        f.run(&[b"SET", b"s", b"v"]);
9817        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9818        let cases: &[&[&[u8]]] = &[
9819            &[b"GEOADD", b"s", b"13", b"38", b"m"],
9820            &[b"GEOPOS", b"s", b"m"],
9821            &[b"GEOHASH", b"s", b"m"],
9822            &[b"GEODIST", b"s", b"a", b"b"],
9823            &[
9824                b"GEOSEARCH",
9825                b"s",
9826                b"FROMLONLAT",
9827                b"15",
9828                b"37",
9829                b"BYRADIUS",
9830                b"1",
9831                b"km",
9832            ],
9833            &[
9834                b"GEOSEARCHSTORE",
9835                b"d",
9836                b"s",
9837                b"FROMLONLAT",
9838                b"15",
9839                b"37",
9840                b"BYRADIUS",
9841                b"1",
9842                b"km",
9843            ],
9844            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9845            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9846            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9847            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9848        ];
9849        for case in cases {
9850            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9851        }
9852        // And it wins over an argument that will not parse, which is the whole
9853        // reason the lookup comes first.
9854        assert_eq!(
9855            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9856            wrong
9857        );
9858    }
9859
9860    // ----------------------------------------------------------------- array
9861
9862    #[test]
9863    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9864        let mut f = Fixture::new();
9865        // Three consecutive positions from a high index, and the reply is how
9866        // many of them were empty before rather than how many were written.
9867        assert_eq!(
9868            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9869            ":3\r\n"
9870        );
9871        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9872        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9873        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9874        // A hole and a key that is not there are the same answer.
9875        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9876        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9877        assert_eq!(
9878            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9879            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9880        );
9881        // Scattered pairs in one command, last write wins within it.
9882        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9883        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9884    }
9885
9886    /// The two numbers an array reports are not the same number, and one of
9887    /// them does not fit a signed integer.
9888    #[test]
9889    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9890        let mut f = Fixture::new();
9891        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9892        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9893        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9894        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9895        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9896        // Deleting in the middle leaves the high water mark where it was.
9897        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9898        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9899        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9900
9901        // The top of the space is addressable, and its length is a number with
9902        // bit sixty three set, so the reply has to be unsigned or it comes back
9903        // negative.
9904        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9905        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9906        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9907        // And one past it does not exist, so a write that would reach it fails
9908        // before any of it lands.
9909        assert_eq!(
9910            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9911            "-ERR array index overflow\r\n"
9912        );
9913        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9914    }
9915
9916    /// One reply per position and not one per element, which is the whole
9917    /// reason the range is capped.
9918    #[test]
9919    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9920        let mut f = Fixture::new();
9921        f.run(&[b"ARSET", b"a", b"1", b"x"]);
9922        assert_eq!(
9923            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9924            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9925        );
9926        // The two ends may come in either order, and the answer is reversed
9927        // rather than empty.
9928        assert_eq!(
9929            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9930            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9931        );
9932        // A key that is not there reads like an array of nothing but holes.
9933        assert_eq!(
9934            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9935            "*2\r\n$-1\r\n$-1\r\n"
9936        );
9937        // A range wider than a million positions is refused and not trimmed,
9938        // because against a missing key it is a request for as many nulls as
9939        // the range is wide.
9940        assert_eq!(
9941            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9942            "-ERR range exceeds maximum of 1000000 items\r\n"
9943        );
9944    }
9945
9946    /// Every index in the argument list is read before the key is touched, so
9947    /// a bad one at the end leaves nothing half written.
9948    #[test]
9949    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9950        let mut f = Fixture::new();
9951        assert_eq!(
9952            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9953            "-ERR invalid array index\r\n"
9954        );
9955        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9956        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9957        assert_eq!(
9958            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9959            "-ERR invalid array index\r\n"
9960        );
9961        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9962        // An index is unsigned here, so the numbers a list would take are not
9963        // the last element, they are errors.
9964        assert_eq!(
9965            f.run(&[b"ARGET", b"a", b"-1"]),
9966            "-ERR invalid array index\r\n"
9967        );
9968        // And a pair list with an odd tail is an arity error rather than a
9969        // syntax one.
9970        assert_eq!(
9971            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9972            "-ERR wrong number of arguments for 'armset' command\r\n"
9973        );
9974        assert_eq!(
9975            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9976            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9977        );
9978    }
9979
9980    #[test]
9981    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9982        let mut f = Fixture::new();
9983        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9984        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9985        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9986        // Two ranges in one command, and the second one covers the whole space
9987        // without walking it.
9988        assert_eq!(
9989            f.run(&[
9990                b"ARDELRANGE",
9991                b"a",
9992                b"100",
9993                b"200",
9994                b"0",
9995                b"18446744073709551614"
9996            ]),
9997            ":2\r\n"
9998        );
9999        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
10000        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
10001        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
10002    }
10003
10004    /// A value goes out as the bytes it came in as, whichever of the three ways
10005    /// the array found to store it.
10006    #[test]
10007    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
10008        let mut f = Fixture::new();
10009        let long = vec![b'v'; 200];
10010        f.run(&[
10011            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
10012            b"short", b"5", &long, b"6", b"-0",
10013        ]);
10014        // 42 is an integer, 007 is not one because it does not print back the
10015        // same, 3.5 survives a double and 3.14 does not, and the last two are a
10016        // word packed string and a blob.
10017        assert_eq!(
10018            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
10019            format!(
10020                "*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",
10021                String::from_utf8_lossy(&long)
10022            )
10023        );
10024    }
10025
10026    #[test]
10027    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
10028        let mut f = Fixture::new();
10029        f.run(&[b"ARSET", b"a", b"0", b"x"]);
10030        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
10031        assert_eq!(
10032            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
10033            "$12\r\nsliced-array\r\n"
10034        );
10035        // And it is a body like any other, so the key commands work on it.
10036        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
10037        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
10038        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
10039        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
10040        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
10041        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
10042    }
10043
10044    #[test]
10045    fn every_array_command_refuses_a_key_holding_something_else() {
10046        let mut f = Fixture::new();
10047        f.run(&[b"SET", b"s", b"v"]);
10048        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10049        for cmd in [
10050            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
10051            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
10052            &[b"ARGET".as_ref(), b"s", b"0"][..],
10053            &[b"ARMGET".as_ref(), b"s", b"0"][..],
10054            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
10055            &[b"ARLEN".as_ref(), b"s"][..],
10056            &[b"ARCOUNT".as_ref(), b"s"][..],
10057            &[b"ARDEL".as_ref(), b"s", b"0"][..],
10058            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
10059            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
10060            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
10061            &[b"ARNEXT".as_ref(), b"s"][..],
10062            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
10063            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
10064            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
10065            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
10066            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
10067            &[b"ARINFO".as_ref(), b"s"][..],
10068        ] {
10069            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
10070        }
10071    }
10072
10073    /// Two of the array commands look the key up before they read the index and
10074    /// the rest read the index first, so the same broken argument gets two
10075    /// different errors depending on which command it went to.
10076    #[test]
10077    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
10078        let mut f = Fixture::new();
10079        f.run(&[b"SET", b"s", b"v"]);
10080        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10081        let bad = "-ERR invalid array index\r\n";
10082        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
10083        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
10084        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
10085        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
10086        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
10087        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
10088        // And on a key that is an array the index is just an index.
10089        f.run(&[b"ARSET", b"a", b"0", b"x"]);
10090        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
10091        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
10092    }
10093
10094    #[test]
10095    fn an_append_follows_a_cursor_the_client_can_move() {
10096        let mut f = Fixture::new();
10097        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
10098        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
10099        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
10100        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
10101        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
10102
10103        // A seek says where the next one goes, and a missing key has no cursor
10104        // to move and is not created by the asking.
10105        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
10106        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
10107        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
10108        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
10109        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
10110        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
10111        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
10112
10113        // The top of the space is the one index only ARSEEK will take, and it
10114        // leaves the cursor with nowhere to go.
10115        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
10116        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
10117        assert_eq!(
10118            f.run(&[b"ARINSERT", b"a", b"x"]),
10119            "-ERR insert index overflow\r\n"
10120        );
10121        assert_eq!(
10122            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
10123            "-ERR invalid array index\r\n"
10124        );
10125    }
10126
10127    #[test]
10128    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
10129        let mut f = Fixture::new();
10130        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
10131        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
10132        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
10133        assert_eq!(
10134            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
10135            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
10136        );
10137        // Growing it after it has wrapped puts the survivors back in the order
10138        // they arrived, which is the whole point of paying for the rebuild.
10139        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
10140        assert_eq!(
10141            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
10142            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
10143        );
10144        // The size is read before the key, so a bad one is a bad size wherever
10145        // it is sent.
10146        assert_eq!(
10147            f.run(&[b"ARRING", b"r", b"0", b"x"]),
10148            "-ERR size must be positive\r\n"
10149        );
10150        assert_eq!(
10151            f.run(&[b"ARRING", b"r", b"big", b"x"]),
10152            "-ERR invalid size\r\n"
10153        );
10154    }
10155
10156    #[test]
10157    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
10158        let mut f = Fixture::new();
10159        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
10160        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
10161        assert_eq!(
10162            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
10163            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
10164        );
10165        assert_eq!(
10166            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
10167            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
10168        );
10169        assert_eq!(
10170            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
10171            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
10172            "more than there is gets what there is"
10173        );
10174        // Nothing asked for is an empty reply, and Redis answers that before it
10175        // has read the option or looked at the key.
10176        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
10177        assert_eq!(
10178            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
10179            "-ERR syntax error\r\n"
10180        );
10181        assert_eq!(
10182            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
10183            "-ERR invalid COUNT\r\n"
10184        );
10185
10186        // With no cursor the tail of the array is the anchor, and a hole inside
10187        // the window is reported as one.
10188        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
10189        assert_eq!(
10190            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
10191            "*2\r\n$-1\r\n$1\r\nz\r\n"
10192        );
10193    }
10194
10195    #[test]
10196    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
10197        let mut f = Fixture::new();
10198        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
10199        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
10200        // The whole index space, which ARGETRANGE refuses and this one answers
10201        // in three visits because holes cost nothing.
10202        assert_eq!(
10203            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
10204            "*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"
10205        );
10206        assert_eq!(
10207            f.run(&[
10208                b"ARSCAN",
10209                b"a",
10210                b"18446744073709551614",
10211                b"0",
10212                b"LIMIT",
10213                b"1"
10214            ]),
10215            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
10216        );
10217        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
10218        assert_eq!(
10219            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
10220            "-ERR LIMIT must be positive\r\n"
10221        );
10222        assert_eq!(
10223            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
10224            "-ERR syntax error\r\n"
10225        );
10226        assert_eq!(
10227            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
10228            "-ERR wrong number of arguments for 'arscan' command\r\n"
10229        );
10230    }
10231
10232    #[test]
10233    fn a_grep_answers_the_indexes_whose_elements_match() {
10234        let mut f = Fixture::new();
10235        assert_eq!(
10236            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
10237            "*0\r\n"
10238        );
10239        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
10240
10241        // The two bounds take the ends of the array as well as an index, and a
10242        // reversed range is walked backwards the way ARSCAN walks one.
10243        assert_eq!(
10244            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
10245            "*3\r\n:0\r\n:1\r\n:2\r\n"
10246        );
10247        assert_eq!(
10248            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
10249            "*3\r\n:2\r\n:1\r\n:0\r\n"
10250        );
10251        assert_eq!(
10252            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
10253            "*2\r\n:1\r\n:2\r\n"
10254        );
10255
10256        // One test each. NOCASE reaches all four of them and it may be written
10257        // after the pattern it applies to.
10258        assert_eq!(
10259            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
10260            "*1\r\n:0\r\n"
10261        );
10262        assert_eq!(
10263            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
10264            "*2\r\n:0\r\n:3\r\n"
10265        );
10266        assert_eq!(
10267            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
10268            "*1\r\n:2\r\n"
10269        );
10270        assert_eq!(
10271            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
10272            "*2\r\n:1\r\n:2\r\n"
10273        );
10274
10275        // OR is the default and AND has to be asked for, and either way the
10276        // last of a repeated option wins.
10277        let both: &[&[u8]] = &[
10278            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
10279        ];
10280        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
10281        assert_eq!(
10282            f.run(&[
10283                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
10284            ]),
10285            "*0\r\n"
10286        );
10287        assert_eq!(
10288            f.run(&[
10289                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
10290            ]),
10291            "*2\r\n:0\r\n:1\r\n"
10292        );
10293
10294        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
10295        // not the positions it had to look at.
10296        assert_eq!(
10297            f.run(&[
10298                b"ARGREP",
10299                b"a",
10300                b"-",
10301                b"+",
10302                b"MATCH",
10303                b"a",
10304                b"WITHVALUES",
10305                b"LIMIT",
10306                b"2"
10307            ]),
10308            "*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"
10309        );
10310        assert_eq!(
10311            f.run(&[
10312                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
10313            ]),
10314            "*1\r\n:3\r\n"
10315        );
10316    }
10317
10318    /// Everything ARGREP refuses, in the order it refuses it.
10319    #[test]
10320    fn a_grep_reports_a_broken_command_the_way_redis_does() {
10321        let mut f = Fixture::new();
10322        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
10323        let syntax = "-ERR syntax error\r\n";
10324
10325        // The bounds are read before the plan, so a bad index beats a bad
10326        // predicate whichever way round the two are written.
10327        assert_eq!(
10328            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
10329            "-ERR invalid array index\r\n"
10330        );
10331        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
10332        // A keyword with nothing after it, and a command that asks for nothing.
10333        assert_eq!(
10334            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
10335            syntax
10336        );
10337        assert_eq!(
10338            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
10339            syntax
10340        );
10341        assert_eq!(
10342            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
10343            syntax,
10344            "a command with no predicate in it at all"
10345        );
10346        assert_eq!(
10347            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
10348            "-ERR LIMIT must be positive\r\n"
10349        );
10350        assert_eq!(
10351            f.run(&[
10352                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
10353            ]),
10354            "-ERR value is not an integer or out of range\r\n"
10355        );
10356        assert_eq!(
10357            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
10358            "-ERR regular expression is empty\r\n"
10359        );
10360        assert_eq!(
10361            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
10362            "-ERR invalid regular expression: Missing ')'\r\n"
10363        );
10364        assert_eq!(
10365            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
10366            "-ERR regular expression backreferences are not supported\r\n"
10367        );
10368        // The arity is minus six, so a predicate keyword with no pattern after
10369        // it is short by one and never reaches the parser.
10370        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
10371        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
10372        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
10373    }
10374
10375    #[test]
10376    fn an_op_reduces_a_range_to_one_number() {
10377        let mut f = Fixture::new();
10378        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
10379        assert_eq!(
10380            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
10381            "$4\r\n-0.5\r\n"
10382        );
10383        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
10384        assert_eq!(
10385            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
10386            "$3\r\n2.5\r\n"
10387        );
10388        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
10389        assert_eq!(
10390            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
10391            ":1\r\n"
10392        );
10393        // An aggregate is written with seventeen significant digits, which is
10394        // Redis's own choice and not what a score comes back as.
10395        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
10396        assert_eq!(
10397            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
10398            "$19\r\n0.30000000000000004\r\n"
10399        );
10400        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
10401        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
10402
10403        // Nothing to work with is a null, and a missing key is a null for the
10404        // aggregates and a zero for the two that count.
10405        f.run(&[b"ARSET", b"w", b"0", b"word"]);
10406        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
10407        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
10408        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
10409
10410        assert_eq!(
10411            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
10412            "-ERR unknown operation\r\n"
10413        );
10414        assert_eq!(
10415            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
10416            "-ERR MATCH requires a value argument\r\n"
10417        );
10418        assert_eq!(
10419            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
10420            "-ERR wrong number of arguments for 'arop' command\r\n"
10421        );
10422    }
10423
10424    #[test]
10425    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
10426        let mut f = Fixture::new();
10427        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
10428        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
10429        let short = f.run(&[b"ARINFO", b"a"]);
10430        assert!(
10431            short.starts_with("*14\r\n"),
10432            "seven pairs on RESP2: {short}"
10433        );
10434        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
10435        assert!(
10436            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
10437            "{short}"
10438        );
10439        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
10440        let full = f.run(&[b"ARINFO", b"a", b"full"]);
10441        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
10442        // Two values one apart are held sparsely, so the dense count is zero and
10443        // the two dense averages have nothing to average.
10444        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
10445        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
10446        assert!(
10447            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
10448            "{full}"
10449        );
10450        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
10451
10452        // On RESP3 the same reply is a map and the averages are doubles.
10453        let mut g = Fixture::new();
10454        g.run(&[b"HELLO", b"3"]);
10455        g.run(&[b"ARINSERT", b"a", b"x"]);
10456        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
10457        assert!(map.starts_with("%12\r\n"), "{map}");
10458        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
10459        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
10460    }
10461
10462    #[test]
10463    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
10464        let mut f = Fixture::new();
10465        // Whole numbers up to two to the sixty second come back as integers,
10466        // and past that the digit generator takes over and uses an exponent.
10467        for (score, want) in [
10468            ("3", "3"),
10469            ("3.5", "3.5"),
10470            ("0.3", "0.3"),
10471            ("1e30", "1e+30"),
10472            ("1e19", "1e+19"),
10473            ("1e-7", "1e-7"),
10474            ("0.000001", "0.000001"),
10475            ("4611686018427387904", "4611686018427387904"),
10476            ("-0", "-0"),
10477        ] {
10478            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
10479            assert_eq!(
10480                f.run(&[b"ZSCORE", b"z", b"m"]),
10481                format!("${}\r\n{want}\r\n", want.len()),
10482                "score {score}"
10483            );
10484        }
10485
10486        // The same bytes on RESP3, where the reply is a double rather than a
10487        // bulk string.
10488        let mut g = Fixture::new();
10489        g.run(&[b"HELLO", b"3"]);
10490        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10491        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10492        // The two float increments are not this printer. They go through
10493        // ld2string in its human mode, which is a fixed point conversion with
10494        // the trailing zeros taken off, so they never write an exponent, and
10495        // they reply with a bulk string on both protocols.
10496        assert_eq!(
10497            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10498            "$31\r\n1000000000000000000000000000000\r\n"
10499        );
10500        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10501        assert_eq!(
10502            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10503            "$20\r\n10000000000000000000\r\n"
10504        );
10505    }
10506
10507    // ----------------------------------------------------------------- graph
10508
10509    #[test]
10510    fn a_node_comes_back_with_the_fields_it_went_in_with() {
10511        let mut f = Fixture::new();
10512        assert_eq!(
10513            f.run(&[
10514                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10515            ]),
10516            ":1\r\n"
10517        );
10518        // The year comes back as the four bytes that were sent and not as a
10519        // number, because every property is text and there is nothing on the
10520        // wire that says which of `1815` and `"1815"` the client meant. The
10521        // fields are in the document's order, which is sorted by name, because
10522        // that is what makes a field lookup a binary search.
10523        assert_eq!(
10524            f.run(&[b"G.NGET", b"social", b"ada"]),
10525            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10526        );
10527        // A second write to the same id replaces the document and says so with
10528        // a zero, so an ingest can count what it created.
10529        assert_eq!(
10530            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10531            ":0\r\n"
10532        );
10533        assert_eq!(
10534            f.run(&[b"G.NGET", b"social", b"ada"]),
10535            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10536        );
10537        // A node with no properties is an empty map and not a null, which is
10538        // how a client tells an isolated node from one that is not there.
10539        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10540        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10541        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10542        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10543
10544        // A field with no value creates nothing, because the pairs are checked
10545        // before the key is touched.
10546        assert_eq!(
10547            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10548            "-ERR syntax error\r\n"
10549        );
10550        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10551
10552        // On RESP3 the same reply is a map.
10553        let mut g = Fixture::new();
10554        g.run(&[b"HELLO", b"3"]);
10555        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10556        assert_eq!(
10557            g.run(&[b"G.NGET", b"social", b"ada"]),
10558            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10559        );
10560    }
10561
10562    #[test]
10563    fn an_edge_creates_the_ends_it_needs() {
10564        let mut f = Fixture::new();
10565        assert_eq!(
10566            f.run(&[
10567                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10568            ]),
10569            ":1\r\n"
10570        );
10571        // Neither end was written first and both are there, as empty nodes.
10572        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10573        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10574        assert_eq!(
10575            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10576            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10577        );
10578        assert_eq!(
10579            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10580            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10581        );
10582        // The same pair under the same label again updates the edge rather than
10583        // making a second one.
10584        assert_eq!(
10585            f.run(&[
10586                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10587            ]),
10588            ":0\r\n"
10589        );
10590        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10591        // A different label between the same pair is a different edge.
10592        assert_eq!(
10593            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10594            ":1\r\n"
10595        );
10596        assert_eq!(
10597            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10598            ":1\r\n"
10599        );
10600
10601        assert_eq!(
10602            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10603            ":1\r\n"
10604        );
10605        assert_eq!(
10606            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10607            ":0\r\n"
10608        );
10609        // A label nothing has used, an end that is not there, and a key that is
10610        // not there are all a zero rather than an error.
10611        assert_eq!(
10612            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10613            ":0\r\n"
10614        );
10615        assert_eq!(
10616            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10617            ":0\r\n"
10618        );
10619        assert_eq!(
10620            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10621            ":0\r\n"
10622        );
10623    }
10624
10625    /// A run is paged the way `SCAN` is paged, so a client that can walk one
10626    /// can walk the other.
10627    #[test]
10628    fn a_hop_answers_a_cursor_and_a_page() {
10629        let mut f = Fixture::new();
10630        for i in 0..25u32 {
10631            let dst = format!("n{i}");
10632            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10633        }
10634        // Ten without being asked, and the cursor is where to carry on from.
10635        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10636        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10637
10638        let mut seen = 0;
10639        let mut cursor = String::from("0");
10640        loop {
10641            let page = f.run(&[
10642                b"G.OUT",
10643                b"social",
10644                b"hub",
10645                b"FOLLOWS",
10646                b"COUNT",
10647                b"7",
10648                b"CURSOR",
10649                cursor.as_bytes(),
10650            ]);
10651            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10652            cursor = head
10653                .rsplit("\r\n")
10654                .next()
10655                .expect("the cursor line")
10656                .to_string();
10657            seen += rest
10658                .split_once("\r\n")
10659                .expect("the page length")
10660                .0
10661                .parse::<usize>()
10662                .expect("a length");
10663            if cursor == "0" {
10664                break;
10665            }
10666        }
10667        assert_eq!(seen, 25, "every neighbour once across the pages");
10668
10669        // A cursor past the end is an empty page and not an error, and so is a
10670        // key or a label that is not there.
10671        assert_eq!(
10672            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10673            "*2\r\n$1\r\n0\r\n*0\r\n"
10674        );
10675        assert_eq!(
10676            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10677            "*2\r\n$1\r\n0\r\n*0\r\n"
10678        );
10679        assert_eq!(
10680            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10681            "*2\r\n$1\r\n0\r\n*0\r\n"
10682        );
10683        assert_eq!(
10684            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10685            "-ERR COUNT must be a positive integer\r\n"
10686        );
10687        assert_eq!(
10688            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10689            "-ERR syntax error\r\n"
10690        );
10691    }
10692
10693    #[test]
10694    fn a_degree_counts_one_way_or_both() {
10695        let mut f = Fixture::new();
10696        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10697        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10698        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10699        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10700        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10701        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10702        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10703        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10704        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10705        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10706        assert_eq!(
10707            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10708            "-ERR syntax error\r\n"
10709        );
10710    }
10711
10712    /// A walk answers which nodes it can reach and not by how many routes, so a
10713    /// node two ways out is in the frontier once.
10714    #[test]
10715    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10716        let mut f = Fixture::new();
10717        for (src, dst) in [
10718            ("ada", "grace"),
10719            ("ada", "alan"),
10720            ("grace", "edsger"),
10721            ("alan", "edsger"),
10722            ("edsger", "barbara"),
10723        ] {
10724            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10725        }
10726        // Two hops without being asked, the start left out, and edsger once
10727        // even though both of the first hop's nodes point at it.
10728        assert_eq!(
10729            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10730            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10731        );
10732        assert_eq!(
10733            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10734            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10735        );
10736        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10737        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10738        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10739        // COUNT stops the walk rather than trimming what it found.
10740        assert_eq!(
10741            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10742            "*1\r\n$5\r\ngrace\r\n"
10743        );
10744        // A node nothing leaves is an empty array and not an error.
10745        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10746        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10747        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10748        assert_eq!(
10749            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10750            "-ERR DEPTH must be a positive integer\r\n"
10751        );
10752        assert_eq!(
10753            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10754            "-ERR syntax error\r\n"
10755        );
10756    }
10757
10758    /// The two sided search, which is the whole reason `G.PATH` is a command
10759    /// and not something a client builds out of `G.OUT`.
10760    #[test]
10761    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10762        let mut f = Fixture::new();
10763        // A chain of six, and a shortcut that makes a shorter way round under a
10764        // second label so the search has to take either kind of hop.
10765        for i in 0..6u32 {
10766            let src = format!("n{i}");
10767            let dst = format!("n{}", i + 1);
10768            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10769        }
10770        assert_eq!(
10771            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10772            "*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"
10773        );
10774        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10775        assert_eq!(
10776            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10777            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10778        );
10779        // A node to itself is a path of one, and a depth too short to reach is
10780        // no path at all.
10781        assert_eq!(
10782            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10783            "*1\r\n$2\r\nn2\r\n"
10784        );
10785        assert_eq!(
10786            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10787            "*0\r\n"
10788        );
10789        // Direction counts: the chain only goes one way.
10790        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10791        // An unreachable node, a node that is not there, and a key that is not
10792        // there are the same empty answer.
10793        f.run(&[b"G.NADD", b"road", b"island"]);
10794        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10795        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10796        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10797        assert_eq!(
10798            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10799            "-ERR syntax error\r\n"
10800        );
10801    }
10802
10803    /// The point of the escape in the record tag: the keyspace owns a graph key
10804    /// the way it owns every other key, and none of these commands know a graph
10805    /// exists.
10806    #[test]
10807    fn the_keyspace_sees_a_graph_key_like_any_other() {
10808        let mut f = Fixture::new();
10809        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10810        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10811        assert_eq!(
10812            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10813            "$9\r\nadjacency\r\n"
10814        );
10815        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10816        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10817        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10818        // A graph is counted against the server the way every other body is,
10819        // which is what `maxmemory` will read when this key is a million nodes.
10820        // There is no `MEMORY USAGE` command yet, so this asks the server.
10821        let held = f.server.memory_bytes();
10822        for i in 0..200u32 {
10823            let dst = format!("n{i}");
10824            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10825        }
10826        assert!(
10827            f.server.memory_bytes() > held,
10828            "two hundred edges cost something: {held} then {}",
10829            f.server.memory_bytes()
10830        );
10831        f.run(&[b"DEL", b"big"]);
10832
10833        // An expiry, then a rename, then a move to another database, all of
10834        // which are the keyspace moving a record it cannot look inside.
10835        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10836        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10837        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10838        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10839        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10840        f.run(&[b"SELECT", b"1"]);
10841        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10842
10843        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10844        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10845        f.run(&[b"G.NADD", b"g", b"n"]);
10846        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10847        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10848    }
10849
10850    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
10851    /// rather than answering the way they answer for a key that is not there.
10852    #[test]
10853    fn a_graph_cannot_be_copied_or_dumped() {
10854        let mut f = Fixture::new();
10855        f.run(&[b"G.NADD", b"social", b"ada"]);
10856        assert_eq!(
10857            f.run(&[b"COPY", b"social", b"other"]),
10858            "-ERR COPY is not supported for a graph\r\n"
10859        );
10860        assert_eq!(
10861            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10862            "-ERR COPY is not supported for a graph\r\n"
10863        );
10864        assert_eq!(
10865            f.run(&[b"DUMP", b"social"]),
10866            "-ERR DUMP is not supported for a graph\r\n"
10867        );
10868        // A refused copy leaves both keys exactly as they were.
10869        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10870    }
10871
10872    /// A graph key is a key, so the commands for the other types refuse it and
10873    /// the graph commands refuse theirs.
10874    #[test]
10875    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10876        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10877        let mut f = Fixture::new();
10878        f.run(&[b"G.NADD", b"social", b"ada"]);
10879        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10880        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10881        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10882
10883        f.run(&[b"SET", b"str", b"v"]);
10884        for cmd in [
10885            vec![b"G.NADD".as_ref(), b"str", b"n"],
10886            vec![b"G.NGET".as_ref(), b"str", b"n"],
10887            vec![b"G.NDEL".as_ref(), b"str", b"n"],
10888            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10889            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10890            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10891            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10892            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10893            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10894            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10895        ] {
10896            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10897        }
10898    }
10899
10900    /// Every other collection here takes its key with it when its last member
10901    /// goes, and a graph is no different.
10902    #[test]
10903    fn a_graph_goes_when_its_last_node_does() {
10904        let mut f = Fixture::new();
10905        f.run(&[
10906            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10907        ]);
10908        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10909        // The node and the edges that hung off it are both gone.
10910        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10911        assert_eq!(
10912            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10913            ":0\r\n"
10914        );
10915        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10916        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10917
10918        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10919        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10920        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10921        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10922
10923        // The id the removed node had is not handed out again, so a client
10924        // holding an id from an earlier reply cannot have it mean another node.
10925        f.run(&[b"G.NADD", b"social", b"first"]);
10926        f.run(&[b"G.NADD", b"social", b"second"]);
10927        f.run(&[b"G.NDEL", b"social", b"first"]);
10928        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10929        assert_eq!(
10930            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10931            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10932        );
10933    }
10934
10935    // ------------------------------------------------------------------ json
10936
10937    /// The two path syntaxes answer different shapes, which is the thing a
10938    /// client is most likely to be broken by and so the thing to pin first.
10939    #[test]
10940    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10941        let mut f = Fixture::new();
10942        let doc = br#"{"a":1,"b":{"c":true}}"#;
10943        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10944        // No path at all is the legacy root and not `$`, so the document comes
10945        // back as itself rather than wrapped.
10946        assert_eq!(
10947            f.run(&[b"JSON.GET", b"doc"]),
10948            bulk(r#"{"a":1,"b":{"c":true}}"#)
10949        );
10950        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10951        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10952        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10953        // A path that matched nothing is an empty set on one syntax and an
10954        // error on the other, and the error does not quote the path.
10955        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10956        assert_eq!(
10957            f.run(&[b"JSON.GET", b"doc", b".nope"]),
10958            "-ERR Path does not exist\r\n"
10959        );
10960        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10961        // The key is a document to the rest of the keyspace, under the name
10962        // RedisJSON registers, and every generic command works on it.
10963        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10964        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10965        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10966        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10967        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10968    }
10969
10970    /// The two error lines RedisJSON sends without a prefix in front of them.
10971    ///
10972    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
10973    /// two do not, on a real server, and a differential harness compares the
10974    /// whole line.
10975    #[test]
10976    fn the_two_json_errors_that_carry_no_prefix() {
10977        let mut f = Fixture::new();
10978        f.run(&[b"SET", b"plain", b"x"]);
10979        let wrong = "-Existing key has wrong Redis type\r\n";
10980        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10981        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10982        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10983        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10984        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10985
10986        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10987        // A wildcard that matched something writes to all of it. A wildcard
10988        // that matched nothing would have to invent a place, and that is the
10989        // other unprefixed line.
10990        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10991        assert_eq!(
10992            f.run(&[b"JSON.GET", b"doc"]),
10993            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10994        );
10995        assert_eq!(
10996            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10997            "-Err wrong static path\r\n"
10998        );
10999    }
11000
11001    /// What `JSON.SET` does with a path that named nowhere.
11002    #[test]
11003    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
11004        let mut f = Fixture::new();
11005        // A key that is not there can only be written whole.
11006        assert_eq!(
11007            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
11008            "-ERR new objects must be created at the root\r\n"
11009        );
11010        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
11011        // The root check comes before NX and XX, which is the order a real
11012        // server checks them in.
11013        assert_eq!(
11014            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
11015            "-ERR new objects must be created at the root\r\n"
11016        );
11017        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
11018        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
11019
11020        f.run(&[
11021            b"JSON.SET",
11022            b"doc",
11023            b"$",
11024            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
11025        ]);
11026        // One step past a container that is there is a place to write.
11027        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
11028        // One step past something that is not, or past something that is not an
11029        // object, is not an error and is not a write either.
11030        assert_eq!(
11031            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
11032            "$-1\r\n"
11033        );
11034        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
11035        // An index past the end does not append. JSON.ARRAPPEND appends.
11036        assert_eq!(
11037            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
11038            "-ERR array index out of range\r\n"
11039        );
11040        assert_eq!(
11041            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
11042            "-ERR array index out of range\r\n"
11043        );
11044        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
11045        // NX on a path that is there and XX on a path that is not are both a
11046        // nil and neither changes anything.
11047        assert_eq!(
11048            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
11049            "$-1\r\n"
11050        );
11051        assert_eq!(
11052            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
11053            "$-1\r\n"
11054        );
11055        assert_eq!(
11056            f.run(&[b"JSON.GET", b"doc"]),
11057            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
11058        );
11059        // Text that is not JSON is refused before the key is touched. The
11060        // line has no `ERR` in front of it, which is this command's and not
11061        // every command's, and is in D-37.
11062        assert!(
11063            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
11064                .starts_with("-this is not the start of a value")
11065        );
11066        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
11067    }
11068
11069    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
11070    /// answers a count or a word rather than text.
11071    #[test]
11072    fn the_json_commands_that_do_not_answer_text() {
11073        let mut f = Fixture::new();
11074        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
11075        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11076
11077        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
11078        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
11079        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
11080        assert_eq!(
11081            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
11082            format!("*1\r\n{}", bulk("integer"))
11083        );
11084        // The one place a legacy path that matched nothing is a nil rather than
11085        // an error, which lines up with a key that is not there.
11086        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
11087        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
11088
11089        // A boolean flips and answers the value it now has, as an integer on
11090        // one syntax and as the word on the other.
11091        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
11092        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
11093        // Something that is not a boolean is a hole on one syntax and one
11094        // sentence covering both cases on the other.
11095        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
11096        assert_eq!(
11097            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
11098            "-ERR Path does not exist or not a bool\r\n"
11099        );
11100        assert_eq!(
11101            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
11102            "-ERR Path does not exist or not a bool\r\n"
11103        );
11104        assert_eq!(
11105            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
11106            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11107        );
11108
11109        // Clearing empties containers and zeroes numbers and leaves everything
11110        // else alone, and counts only what it changed.
11111        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
11112        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
11113        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
11114        assert_eq!(
11115            f.run(&[b"JSON.GET", b"doc"]),
11116            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
11117        );
11118
11119        // Deleting counts what it removed, and deleting the root is deleting
11120        // the key.
11121        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
11122        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
11123        // Deleting the last member of the root container deletes the key, the
11124        // same way popping the last element off a list does. It is a rule about
11125        // deleting and not about shape: a document written as an empty object
11126        // by JSON.SET stays, because nothing was removed from it.
11127        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
11128        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
11129        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
11130        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
11131        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
11132        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
11133        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
11134        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
11135    }
11136
11137    /// `JSON.GET` with more than one path, and with a layout.
11138    ///
11139    /// The wrapper the reply is built in is laid out too, so what a path
11140    /// matched starts one level in for a single JSONPath and two for one of
11141    /// several, and getting that wrong is the kind of thing only a byte for
11142    /// byte comparison catches.
11143    #[test]
11144    fn json_get_lays_out_the_wrapper_it_builds() {
11145        let mut f = Fixture::new();
11146        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
11147
11148        assert_eq!(
11149            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
11150            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
11151        );
11152        // Legacy paths are not wrapped, even when there are several of them.
11153        assert_eq!(
11154            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
11155            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
11156        );
11157        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
11158        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
11159        one.extend_from_slice(fmt);
11160        one.push(b"$.b");
11161        assert_eq!(
11162            f.run(&one),
11163            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
11164        );
11165        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
11166        two.extend_from_slice(fmt);
11167        two.push(b"$.a");
11168        two.push(b"$.nope");
11169        assert_eq!(
11170            f.run(&two),
11171            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
11172        );
11173        // The options are read before the paths and in any order, and a
11174        // document with nothing to lay out is the same either way.
11175        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
11176        root.push(b".a");
11177        assert_eq!(f.run(&root), bulk("1"));
11178    }
11179
11180    /// `JSON.MGET`, which is the only command here that reads more than one key
11181    /// and so the only one whose answer has holes in it.
11182    #[test]
11183    fn json_mget_answers_once_per_key_whatever_is_under_them() {
11184        let mut f = Fixture::new();
11185        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
11186        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
11187        f.run(&[b"SET", b"plain", b"x"]);
11188        assert_eq!(
11189            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
11190            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
11191        );
11192        // A key that is not there and a key holding something else are both a
11193        // hole rather than an error, the way MGET treats a hash.
11194        assert_eq!(
11195            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
11196            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
11197        );
11198        // A legacy path that matched nothing is a hole too, because one bad
11199        // answer should not lose the others.
11200        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
11201    }
11202
11203    /// The four commands that ask how big something is, and the four different
11204    /// sets of answers they give for the same three failures.
11205    ///
11206    /// There is no pattern in this and there is no reading it off the
11207    /// documentation either. It was read off a running RedisJSON one line at a
11208    /// time, and it is written down here because the error text is what a client
11209    /// library branches on.
11210    #[test]
11211    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
11212        let mut f = Fixture::new();
11213        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
11214        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11215
11216        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
11217        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
11218        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
11219        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
11220        assert_eq!(
11221            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
11222            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
11223        );
11224        // A JSONPath answers one entry per match and a hole for a match of the
11225        // wrong kind, which is the one shape all four agree on.
11226        assert_eq!(
11227            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
11228            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
11229        );
11230
11231        // A legacy path that matched nothing. Two of them are an error and two
11232        // of them are a nil, and the two errors do not use the same sentence.
11233        assert_eq!(
11234            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
11235            "-ERR Path does not exist\r\n"
11236        );
11237        assert_eq!(
11238            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
11239            "-ERR Path does not exist\r\n"
11240        );
11241        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
11242        // A nil bulk and not an empty array, even though the answer would have
11243        // been an array, which is what RedisJSON sends here too.
11244        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
11245        // The JSONPath spelling of the same question is an empty array, since
11246        // no match is not a failure on that syntax.
11247        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
11248
11249        // A legacy path that matched the wrong kind of value. Now two of them
11250        // are an ERR and two of them are a WRONGTYPE, and it is not the same
11251        // two.
11252        assert_eq!(
11253            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
11254            "-ERR Path does not exist or not an array\r\n"
11255        );
11256        assert_eq!(
11257            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
11258            "-ERR Path does not exist or not an object\r\n"
11259        );
11260        assert_eq!(
11261            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
11262            "-WRONGTYPE wrong type of path value - expected object\r\n"
11263        );
11264        assert_eq!(
11265            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
11266            "-WRONGTYPE wrong type of path value - expected string\r\n"
11267        );
11268
11269        // A key that is not there, where the two syntaxes swap over: the legacy
11270        // path is the quiet answer and the JSONPath is the error.
11271        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
11272        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
11273        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
11274        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
11275        assert_eq!(
11276            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
11277            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11278        );
11279        // Except this one, which answers about the path instead.
11280        assert_eq!(
11281            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
11282            "-ERR Path does not exist or not an object\r\n"
11283        );
11284    }
11285
11286    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
11287    ///
11288    /// The four of them share one error line for a path that named something
11289    /// that is not an array, and they disagree about what an index outside the
11290    /// array means: insert refuses it and the other two clamp.
11291    #[test]
11292    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
11293        let mut f = Fixture::new();
11294        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
11295
11296        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
11297        assert_eq!(
11298            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
11299            "*1\r\n:6\r\n"
11300        );
11301        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
11302
11303        // A negative index counts back from the end, and the end itself is a
11304        // place to insert at, so an insert at the length is an append.
11305        assert_eq!(
11306            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
11307            ":7\r\n"
11308        );
11309        assert_eq!(
11310            f.run(&[b"JSON.GET", b"doc", b".a"]),
11311            bulk("[1,2,3,4,5,0,6]")
11312        );
11313        assert_eq!(
11314            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
11315            ":8\r\n"
11316        );
11317        // One past the end is not, and neither is one before the front.
11318        assert_eq!(
11319            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
11320            "-ERR index out of bounds\r\n"
11321        );
11322        assert_eq!(
11323            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
11324            "-ERR index out of bounds\r\n"
11325        );
11326
11327        // Trim takes both ends inclusive and clamps both of them, so a start
11328        // past the end leaves an empty array rather than an error.
11329        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
11330        assert_eq!(
11331            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
11332            ":3\r\n"
11333        );
11334        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
11335        assert_eq!(
11336            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
11337            ":2\r\n"
11338        );
11339        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
11340        assert_eq!(
11341            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
11342            ":0\r\n"
11343        );
11344        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11345
11346        // Pop clamps as well, its default is the last element, and an empty
11347        // array pops a nil rather than failing.
11348        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
11349        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
11350        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
11351        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
11352        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
11353
11354        // One sentence covers a path that matched nothing and a path that
11355        // matched the wrong kind of value, for all four of them.
11356        for call in [
11357            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
11358            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
11359            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
11360            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
11361        ] {
11362            for path in [&b".n"[..], &b".nope"[..]] {
11363                let args: Vec<&[u8]> = call
11364                    .iter()
11365                    .map(|a| if *a == b"PATH" { path } else { *a })
11366                    .collect();
11367                assert_eq!(
11368                    f.run(&args),
11369                    "-ERR Path does not exist or not an array\r\n",
11370                    "{} {}",
11371                    String::from_utf8_lossy(call[0]),
11372                    String::from_utf8_lossy(path)
11373                );
11374            }
11375        }
11376
11377        // A key that is not there is the same sentence for all four, on either
11378        // syntax, and it is about the key and not about the path.
11379        assert_eq!(
11380            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
11381            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11382        );
11383        assert_eq!(
11384            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
11385            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11386        );
11387
11388        // The values are parsed before the key is touched, so text that is not
11389        // JSON leaves the document alone.
11390        // Text that is not JSON is refused before the key is touched, and
11391        // the line has no `ERR` in front of it, which is D-37.
11392        assert!(
11393            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
11394                .starts_with("-this is not the start of a value")
11395        );
11396        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11397    }
11398
11399    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
11400    /// path matched cannot take the index, which is D-36.
11401    ///
11402    /// RedisJSON walks the matches, inserts into each one it can, and returns
11403    /// the error on the first one it cannot, leaving the earlier inserts in the
11404    /// document. A write here is one list of edits applied together, so either
11405    /// all of them happen or none of them do.
11406    #[test]
11407    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
11408        let mut f = Fixture::new();
11409        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
11410        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11411        assert_eq!(
11412            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
11413            "-ERR index out of bounds\r\n"
11414        );
11415        assert_eq!(
11416            f.run(&[b"JSON.GET", b"doc"]),
11417            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
11418        );
11419        // Every match can take the index, so every match gets it.
11420        assert_eq!(
11421            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
11422            "*3\r\n:4\r\n:3\r\n:2\r\n"
11423        );
11424        assert_eq!(
11425            f.run(&[b"JSON.GET", b"doc"]),
11426            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
11427        );
11428    }
11429
11430    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
11431    /// last element rather than to one past it.
11432    ///
11433    /// Both of those read like mistakes and both are what RedisJSON does. The
11434    /// start is the one that bites: a start of five into an array of four still
11435    /// looks at the fourth, so a search that should have run out of array comes
11436    /// back with an answer.
11437    #[test]
11438    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
11439        let mut f = Fixture::new();
11440        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
11441
11442        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
11443        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
11444        assert_eq!(
11445            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
11446            "*1\r\n:1\r\n"
11447        );
11448
11449        // Zero as the stop means the end rather than the front, so leaving it
11450        // off and passing it are the same thing.
11451        assert_eq!(
11452            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
11453            ":3\r\n"
11454        );
11455        // The stop is exclusive, so a stop of three does not look at index
11456        // three.
11457        assert_eq!(
11458            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
11459            ":-1\r\n"
11460        );
11461
11462        // The start clamps to the last element in both directions, which is why
11463        // a start of four, five or minus one all find the 1 at index three.
11464        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
11465            assert_eq!(
11466                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
11467                ":3\r\n",
11468                "{}",
11469                String::from_utf8_lossy(start)
11470            );
11471        }
11472        assert_eq!(
11473            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
11474            ":0\r\n"
11475        );
11476        // An empty array is the one case that comes back with nothing, since
11477        // the stop is zero and the loop never starts.
11478        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
11479        assert_eq!(
11480            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11481            ":-1\r\n"
11482        );
11483
11484        // The comparison is structural rather than one of the encoded bytes,
11485        // because an object in a stored document holds its keys as intern table
11486        // ids where one parsed off the wire holds them as bytes.
11487        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11488        assert_eq!(
11489            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11490            ":0\r\n"
11491        );
11492        assert_eq!(
11493            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11494            ":1\r\n"
11495        );
11496        assert_eq!(
11497            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11498            ":-1\r\n"
11499        );
11500
11501        // Its errors are a third set again: a missing legacy path is the short
11502        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
11503        // not there is about the path on either syntax.
11504        assert_eq!(
11505            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11506            "-ERR Path does not exist\r\n"
11507        );
11508        assert_eq!(
11509            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11510            "-WRONGTYPE wrong type of path value - expected array\r\n"
11511        );
11512        assert_eq!(
11513            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11514            "-ERR Path does not exist\r\n"
11515        );
11516        assert_eq!(
11517            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11518            "-ERR Path does not exist\r\n"
11519        );
11520    }
11521
11522    /// The number family answers text and keeps an integer an integer until
11523    /// something in the sum is not one.
11524    #[test]
11525    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11526        let mut f = Fixture::new();
11527        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11528        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11529
11530        // A legacy path answers the new value as JSON text in a bulk string,
11531        // not as a number, which is the shape all three of them use.
11532        assert_eq!(
11533            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11534            bulk("9").as_str()
11535        );
11536        // A JSONPath answers a bulk string holding a JSON array.
11537        assert_eq!(
11538            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11539            bulk("[11]").as_str()
11540        );
11541        // Two integers stay an integer and a double anywhere in it makes the
11542        // answer a double, which the document then holds.
11543        assert_eq!(
11544            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11545            bulk("13.0").as_str()
11546        );
11547        assert_eq!(
11548            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11549            bulk("number").as_str()
11550        );
11551        assert_eq!(
11552            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11553            bulk("3.0").as_str()
11554        );
11555        assert_eq!(
11556            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11557            bulk("-8").as_str()
11558        );
11559        // A power of a half is a square root, and the square root of a negative
11560        // number is the error that says the answer is not a number.
11561        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11562        assert_eq!(
11563            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11564            bulk("1.224744871391589").as_str()
11565        );
11566        assert_eq!(
11567            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11568            "-ERR result is not a number\r\n"
11569        );
11570        // An integer answer that does not fit is refused rather than promoted,
11571        // and a negative exponent lands in the same error because there is no
11572        // integer answer to two to the minus one.
11573        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11574        assert_eq!(
11575            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11576            "-ERR numeric overflow\r\n"
11577        );
11578        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11579        assert_eq!(
11580            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11581            "-ERR numeric overflow\r\n"
11582        );
11583        // A double that leaves the finite numbers is the other error.
11584        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11585        assert_eq!(
11586            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11587            "-ERR result is not a number\r\n"
11588        );
11589
11590        // A match that is not a number is a null inside the array on a
11591        // JSONPath, and a legacy path that found no number at all is the error
11592        // with the module's own typo in it.
11593        assert_eq!(
11594            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11595            bulk("[null]").as_str()
11596        );
11597        assert_eq!(
11598            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11599            bulk("[]").as_str()
11600        );
11601        assert_eq!(
11602            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11603            "-ERR Path does not exist or does not contains a number\r\n"
11604        );
11605        assert_eq!(
11606            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11607            "-ERR Path does not exist or does not contains a number\r\n"
11608        );
11609        // The operand is JSON and has to be a number. Valid JSON that is not
11610        // one is a line of its own, and it goes out without a prefix.
11611        assert_eq!(
11612            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11613            "-bad input number\r\n"
11614        );
11615        assert_eq!(
11616            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11617            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11618        );
11619        assert_eq!(
11620            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11621            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11622        );
11623    }
11624
11625    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
11626    /// which nothing else in the group does.
11627    #[test]
11628    fn json_strappend_reads_its_shape_off_the_argument_count() {
11629        let mut f = Fixture::new();
11630        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11631
11632        assert_eq!(
11633            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11634            ":3\r\n"
11635        );
11636        assert_eq!(
11637            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11638            "*1\r\n:4\r\n"
11639        );
11640        // The length is in bytes and not in characters, so one two byte letter
11641        // takes it up by two.
11642        assert_eq!(
11643            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11644            ":6\r\n"
11645        );
11646        // Three arguments means the value is the last one and the path is the
11647        // root, so this appends to a document that is a string on its own.
11648        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11649        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11650        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11651
11652        // The value is JSON and has to be a JSON string. A number is a
11653        // WRONGTYPE about a path value even though it was the value that was
11654        // wrong, which is the module's wording and not a slip here.
11655        assert_eq!(
11656            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11657            "-WRONGTYPE wrong type of path value - expected string\r\n"
11658        );
11659        assert_eq!(
11660            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11661            "*1\r\n$-1\r\n"
11662        );
11663        assert_eq!(
11664            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11665            "-ERR Path does not exist or not a string\r\n"
11666        );
11667        assert_eq!(
11668            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11669            "*0\r\n"
11670        );
11671        assert_eq!(
11672            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11673            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11674        );
11675    }
11676
11677    /// A legacy path can match more than one value, and which of them the one
11678    /// answer comes from is not the same choice twice.
11679    #[test]
11680    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11681        let mut f = Fixture::new();
11682        // Three arrays of one, two and three elements, which tells the first
11683        // match and the last match apart in a single command.
11684        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11685
11686        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11687        assert_eq!(
11688            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11689            ":4\r\n"
11690        );
11691        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11692        assert_eq!(
11693            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11694            ":2\r\n"
11695        );
11696        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11697        assert_eq!(
11698            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11699            ":1\r\n"
11700        );
11701        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11702        assert_eq!(
11703            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11704            bulk("1").as_str()
11705        );
11706        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11707        assert_eq!(
11708            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11709            bulk("13").as_str()
11710        );
11711        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11712        assert_eq!(
11713            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11714            ":4\r\n"
11715        );
11716        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11717        assert_eq!(
11718            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11719            bulk("false").as_str()
11720        );
11721        // Every one of them wrote to all three matches, whichever one it chose
11722        // to answer about.
11723        assert_eq!(
11724            f.run(&[b"JSON.GET", b"doc", b".a"]),
11725            bulk("[false,true,false]").as_str()
11726        );
11727
11728        // A match of the wrong kind is skipped rather than being the answer, so
11729        // a path that found a string and then two arrays still answers.
11730        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11731        assert_eq!(
11732            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11733            ":3\r\n"
11734        );
11735        assert_eq!(
11736            f.run(&[b"JSON.GET", b"doc", b".a"]),
11737            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11738        );
11739        // Nothing of the right kind anywhere is the error, and that is the only
11740        // case that is.
11741        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11742        assert_eq!(
11743            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11744            "-ERR Path does not exist or not an array\r\n"
11745        );
11746        assert_eq!(
11747            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11748            "-ERR Path does not exist or not a bool\r\n"
11749        );
11750        // The one array that was there and had nothing in it is an answer and
11751        // not a skip, so the pop answers about it rather than about the array
11752        // after it.
11753        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11754        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11755        assert_eq!(
11756            f.run(&[b"JSON.GET", b"doc", b".a"]),
11757            bulk("[[],[2]]").as_str()
11758        );
11759    }
11760
11761    /// A path that matched a value and something inside that value writes to
11762    /// both, which is what `$..` and a nested wildcard are for.
11763    #[test]
11764    fn a_write_reaches_a_match_that_sits_inside_another_match() {
11765        let mut f = Fixture::new();
11766        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11767
11768        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11769        assert_eq!(
11770            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11771            "*3\r\n:3\r\n:2\r\n:3\r\n"
11772        );
11773        assert_eq!(
11774            f.run(&[b"JSON.GET", b"doc", b"$"]),
11775            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11776        );
11777
11778        // The same for a trim, where the outer array keeps the two elements the
11779        // inner writes landed in.
11780        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11781        assert_eq!(
11782            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11783            "*3\r\n:1\r\n:1\r\n:1\r\n"
11784        );
11785        assert_eq!(
11786            f.run(&[b"JSON.GET", b"doc", b"$"]),
11787            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11788        );
11789
11790        // And for a number, where the first match is the object the outer array
11791        // holds and only the two inside it are numbers.
11792        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11793        assert_eq!(
11794            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11795            bulk("[null,8,8]").as_str()
11796        );
11797    }
11798
11799    /// The value a write is given is looked at only once the path has found
11800    /// something of the right kind to use it on.
11801    #[test]
11802    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11803        let mut f = Fixture::new();
11804        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11805
11806        // A string is not a number, so the path answers first and the `"x"` is
11807        // never looked at. Same for the value that is not JSON at all.
11808        assert_eq!(
11809            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11810            bulk("[null]").as_str()
11811        );
11812        assert_eq!(
11813            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11814            bulk("[null]").as_str()
11815        );
11816        assert_eq!(
11817            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11818            bulk("[]").as_str()
11819        );
11820        assert_eq!(
11821            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11822            "-ERR Path does not exist or does not contains a number\r\n"
11823        );
11824        // A number match anywhere and the value is looked at after all.
11825        assert_eq!(
11826            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11827            "-bad input number\r\n"
11828        );
11829
11830        // JSON.STRAPPEND follows the same order with its own two answers.
11831        assert_eq!(
11832            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11833            "*1\r\n$-1\r\n"
11834        );
11835        assert_eq!(
11836            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11837            "-ERR Path does not exist or not a string\r\n"
11838        );
11839        assert_eq!(
11840            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11841            "-WRONGTYPE wrong type of path value - expected string\r\n"
11842        );
11843
11844        // A key that is not there still comes before either of them.
11845        assert_eq!(
11846            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11847            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11848        );
11849        assert_eq!(
11850            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11851            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11852        );
11853    }
11854
11855    /// RFC 7386 in one test: a null deletes, everything else merges, and a
11856    /// patch that is not an object replaces what it lands on.
11857    #[test]
11858    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11859        let mut f = Fixture::new();
11860
11861        // A key that is not there is created at the root, nulls and all,
11862        // because a deletion with nothing to delete is still what the client
11863        // sent.
11864        assert_eq!(
11865            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11866            "+OK\r\n"
11867        );
11868        assert_eq!(
11869            f.run(&[b"JSON.GET", b"doc", b"$"]),
11870            bulk(r#"[{"x":null,"y":1}]"#).as_str()
11871        );
11872
11873        // Onto something that is there, a null deletes the member of that name
11874        // and the rest is merged one level at a time.
11875        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11876        assert_eq!(
11877            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11878            "+OK\r\n"
11879        );
11880        assert_eq!(
11881            f.run(&[b"JSON.GET", b"doc", b"$"]),
11882            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11883        );
11884
11885        // A patch that is not an object replaces what it is merged onto.
11886        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11887        assert_eq!(
11888            f.run(&[b"JSON.GET", b"doc", b"$"]),
11889            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11890        );
11891
11892        // A patch object onto a value that is not an object starts from an
11893        // empty object, so this time the null has nothing to delete and is
11894        // dropped rather than stored.
11895        assert_eq!(
11896            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11897            "+OK\r\n"
11898        );
11899        assert_eq!(
11900            f.run(&[b"JSON.GET", b"doc", b"$"]),
11901            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11902        );
11903
11904        // A member one level past the end of the document is created and keeps
11905        // its nulls, two levels past it is a write that did not happen, and a
11906        // path that would have to invent where it goes is the unprefixed line.
11907        assert_eq!(
11908            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11909            "+OK\r\n"
11910        );
11911        assert_eq!(
11912            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11913            bulk(r#"[{"z":null}]"#).as_str()
11914        );
11915        assert_eq!(
11916            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11917            "$-1\r\n"
11918        );
11919        assert_eq!(
11920            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11921            "-Err wrong static path\r\n"
11922        );
11923
11924        // A wildcard merges every match.
11925        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11926        assert_eq!(
11927            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11928            "+OK\r\n"
11929        );
11930        assert_eq!(
11931            f.run(&[b"JSON.GET", b"doc", b"$"]),
11932            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11933        );
11934
11935        // The three ways to get it wrong.
11936        assert_eq!(
11937            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11938            "-ERR syntax error\r\n"
11939        );
11940        assert_eq!(
11941            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11942            "-ERR new objects must be created at the root\r\n"
11943        );
11944        f.run(&[b"SET", b"str", b"x"]);
11945        assert_eq!(
11946            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11947            "-Existing key has wrong Redis type\r\n"
11948        );
11949    }
11950
11951    /// A descent is the one path that matches a value and something inside that
11952    /// same value, and the inner merge has to survive the outer one.
11953    #[test]
11954    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11955        let mut f = Fixture::new();
11956        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11957        assert_eq!(
11958            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11959            "+OK\r\n"
11960        );
11961        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
11962        // merged onto the result, so the `{"m":1}` written into `a.b` is still
11963        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
11964        assert_eq!(
11965            f.run(&[b"JSON.GET", b"doc", b"$"]),
11966            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11967        );
11968
11969        // A deletion down the same path, which is the case where the inner
11970        // merge empties the object the outer one then copies.
11971        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11972        assert_eq!(
11973            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11974            "+OK\r\n"
11975        );
11976        assert_eq!(
11977            f.run(&[b"JSON.GET", b"doc", b"$"]),
11978            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11979        );
11980    }
11981
11982    /// A filter is a selector like any other, so every command that takes a path
11983    /// takes one, reads and writes alike.
11984    #[test]
11985    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11986        let mut f = Fixture::new();
11987        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11988        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11989
11990        assert_eq!(
11991            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11992            bulk(r#"["a","c"]"#).as_str()
11993        );
11994        // `$` inside the expression is the document, so a member can be measured
11995        // against something that is not inside it.
11996        assert_eq!(
11997            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11998            bulk(r#"["a","c"]"#).as_str()
11999        );
12000        // The legacy syntax takes one too, and answers the first match.
12001        assert_eq!(
12002            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
12003            bulk(r#""a""#).as_str()
12004        );
12005        assert_eq!(
12006            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
12007            "*1\r\n$6\r\nobject\r\n"
12008        );
12009
12010        // A write goes through it as far as a value that is already there. A
12011        // field that is not there yet has nowhere definite to go, which is the
12012        // same refusal a wildcard gets.
12013        assert_eq!(
12014            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
12015            bulk("[9,10]").as_str()
12016        );
12017        assert_eq!(
12018            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
12019            "+OK\r\n"
12020        );
12021        assert_eq!(
12022            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
12023            "-Err wrong static path\r\n"
12024        );
12025        assert_eq!(
12026            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
12027            ":2\r\n"
12028        );
12029        assert_eq!(
12030            f.run(&[b"JSON.GET", b"doc", b"$"]),
12031            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
12032        );
12033
12034        // A path that does not parse is refused before the document is read, so
12035        // a key that is not there answers the same way.
12036        assert!(
12037            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
12038                .starts_with("-ERR")
12039        );
12040        assert!(
12041            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
12042                .starts_with("-ERR")
12043        );
12044    }
12045
12046    /// The operators past the comparisons, over the wire rather than in the
12047    /// parser's own tests, so that a client can reach all of them.
12048    #[test]
12049    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
12050        let mut f = Fixture::new();
12051        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
12052        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
12053
12054        for (path, want) in [
12055            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
12056            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
12057            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
12058            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
12059            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
12060            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
12061            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
12062            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
12063            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
12064            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
12065            (b"$.box[?(@.n~)].t", "[]"),
12066            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
12067            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
12068            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
12069            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
12070        ] {
12071            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
12072        }
12073
12074        // A write goes through one of these the same way it goes through a
12075        // comparison.
12076        assert_eq!(
12077            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
12078            "+OK\r\n"
12079        );
12080        assert_eq!(
12081            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
12082            bulk(r#"["b"]"#).as_str()
12083        );
12084    }
12085
12086    /// D-41. RedisJSON refuses this one, and which document it refuses is
12087    /// decided by how it happens to hold an array of numbers.
12088    #[test]
12089    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
12090        let mut f = Fixture::new();
12091        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
12092        assert_eq!(
12093            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12094            "+OK\r\n"
12095        );
12096        assert_eq!(
12097            f.run(&[b"JSON.GET", b"doc", b"$"]),
12098            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
12099        );
12100        // The same document with one element that is not an integer is the one
12101        // RedisJSON is happy with, and it goes the same way here.
12102        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
12103        assert_eq!(
12104            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12105            "+OK\r\n"
12106        );
12107        assert_eq!(
12108            f.run(&[b"JSON.GET", b"doc", b"$"]),
12109            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
12110        );
12111    }
12112
12113    /// `JSON.MSET` checks what it can before it writes anything and skips the
12114    /// one thing it cannot, which is a path with nowhere to put its value.
12115    #[test]
12116    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
12117        let mut f = Fixture::new();
12118        assert_eq!(
12119            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
12120            "+OK\r\n"
12121        );
12122        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
12123        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
12124
12125        // A repeated key takes the last write.
12126        assert_eq!(
12127            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
12128            "+OK\r\n"
12129        );
12130        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
12131
12132        // A triple whose path names nowhere is skipped, the others are still
12133        // written and the reply turns into a nil. Both ways round, because a
12134        // loop that gave up at the first skip would agree with this on one
12135        // order and not on the other.
12136        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
12137        assert_eq!(
12138            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
12139            "$-1\r\n"
12140        );
12141        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
12142        assert_eq!(
12143            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
12144            "$-1\r\n"
12145        );
12146        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12147
12148        // A value that is not JSON, a key holding something else and a path
12149        // that would have to create a document below its own root are all
12150        // checked before anything is written, so the good triple next to them
12151        // does not happen either.
12152        f.run(&[b"SET", b"str", b"x"]);
12153        assert_eq!(
12154            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
12155            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
12156        );
12157        assert_eq!(
12158            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
12159            "-Existing key has wrong Redis type\r\n"
12160        );
12161        assert_eq!(
12162            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
12163            "-ERR new objects must be created at the root\r\n"
12164        );
12165        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
12166
12167        // The two errors a path can be are checked up front as well, so the
12168        // triple before them is not written either. A wildcard that matched
12169        // nothing has nowhere to invent, and an index that is not in the array
12170        // is out of range, and both of them stop the whole command.
12171        assert_eq!(
12172            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
12173            "-Err wrong static path\r\n"
12174        );
12175        assert_eq!(
12176            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
12177            "-ERR array index out of range\r\n"
12178        );
12179        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12180
12181        // Every triple is worked out against the keyspace as the command found
12182        // it, so a second triple on the same key does not see the first one and
12183        // the last write is the one that stays.
12184        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
12185        assert_eq!(
12186            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
12187            "+OK\r\n"
12188        );
12189        assert_eq!(
12190            f.run(&[b"JSON.GET", b"c", b"$"]),
12191            bulk(r#"[{"n":3}]"#).as_str()
12192        );
12193
12194        // An argument count that is not a run of key, path and value is the
12195        // arity error rather than a syntax one.
12196        assert_eq!(
12197            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
12198            "-ERR wrong number of arguments for 'json.mset' command\r\n"
12199        );
12200    }
12201
12202    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
12203    /// an empty array and an empty object apart.
12204    #[test]
12205    fn json_resp_answers_the_document_as_resp_types() {
12206        let mut f = Fixture::new();
12207        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
12208        assert_eq!(
12209            f.run(&[b"JSON.RESP", b"doc"]),
12210            "*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"
12211        );
12212        // A JSONPath wraps the same answer in one more array.
12213        assert_eq!(
12214            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
12215            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
12216        );
12217
12218        f.run(&[
12219            b"JSON.SET",
12220            b"doc",
12221            b"$",
12222            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
12223        ]);
12224        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
12225        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
12226        // A double goes out as its text, so a client reads the same digits
12227        // `JSON.GET` would have given it.
12228        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
12229        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
12230        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
12231
12232        // A missing legacy path is an error, a missing JSONPath is an empty
12233        // array, and a key that is not there is a nil on either.
12234        assert_eq!(
12235            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
12236            "-ERR Path does not exist\r\n"
12237        );
12238        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
12239        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
12240        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
12241    }
12242
12243    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
12244    /// pins the shapes and that the two syntaxes agree rather than a number
12245    /// read off another server. That is D-42.
12246    #[test]
12247    fn json_debug_answers_a_byte_count_and_its_own_help() {
12248        let mut f = Fixture::new();
12249        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
12250        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
12251        assert!(one.starts_with(':'), "{one}");
12252        assert_eq!(
12253            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
12254            format!("*1\r\n{one}")
12255        );
12256        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
12257        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
12258
12259        // A key that is not there is a zero on a legacy path and an empty set
12260        // on a JSONPath, which is the one reader here that does not answer nil
12261        // for it.
12262        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
12263        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
12264        assert_eq!(
12265            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
12266            "-ERR Path does not exist\r\n"
12267        );
12268        assert_eq!(
12269            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
12270            "*0\r\n"
12271        );
12272
12273        assert_eq!(
12274            f.run(&[b"JSON.DEBUG", b"HELP"]),
12275            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
12276             $34\r\nHELP                - this message\r\n"
12277        );
12278        assert_eq!(
12279            f.run(&[b"JSON.DEBUG", b"NOPE"]),
12280            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
12281        );
12282        assert_eq!(
12283            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
12284            "-ERR wrong number of arguments for 'json.debug' command\r\n"
12285        );
12286    }
12287
12288    // ---------------------------------------------------------------- vector
12289
12290    /// The first `VADD` fixes the dimension and every one after it has to
12291    /// agree, because there is no create command to say it earlier.
12292    #[test]
12293    fn the_first_vadd_decides_how_wide_the_set_is() {
12294        let mut f = Fixture::new();
12295        assert_eq!(
12296            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
12297            ":1\r\n"
12298        );
12299        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12300        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12301        // A second vector under the same name replaces it and says so with a
12302        // zero, so an ingest can count what it created.
12303        assert_eq!(
12304            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
12305            ":0\r\n"
12306        );
12307        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12308        // Three dimensions into a two dimensional set names both numbers, since
12309        // a client that gets this wrong needs to know which end is which.
12310        assert_eq!(
12311            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
12312            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
12313        );
12314        // A vector of zeros has no direction, and it is taken anyway and comes
12315        // back as the origin, because that is what a real server does with it.
12316        assert_eq!(
12317            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
12318            ":1\r\n"
12319        );
12320        assert_eq!(
12321            f.run(&[b"VEMB", b"v", b"nowhere"]),
12322            "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
12323        );
12324        // A set is made with one quantisation and keeps it, and a `VADD` that
12325        // names another is refused. Naming none names `Q8`, which is why this
12326        // set is a `Q8` one.
12327        assert_eq!(
12328            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
12329            "-ERR asked quantization mismatch with existing vector set\r\n"
12330        );
12331        // Nothing above created a key, and a set that never took a vector has
12332        // no dimension to report.
12333        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
12334        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
12335        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
12336    }
12337
12338    /// What a client sent comes back out, and what a client asked for is a
12339    /// similarity and not the distance underneath it.
12340    #[test]
12341    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
12342        let mut f = Fixture::new();
12343        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
12344        // The set stored the direction and the length is multiplied back on the
12345        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
12346        // either, because nobody named a quantisation and that means `Q8`: the
12347        // wider coordinate lands on a code exactly and the other one does not.
12348        // Both numbers are a real server's answers for the same input.
12349        assert_eq!(
12350            f.run(&[b"VEMB", b"v", b"a"]),
12351            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12352        );
12353        // NOQUANT is the way to ask for what went in to come back out.
12354        f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
12355        assert_eq!(
12356            f.run(&[b"VEMB", b"n", b"a"]),
12357            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12358        );
12359        // BIN keeps the signs and nothing else, and does not multiply the
12360        // length back on, since a sign has no length in it to scale.
12361        f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
12362        assert_eq!(
12363            f.run(&[b"VEMB", b"b", b"a"]),
12364            "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
12365        );
12366        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
12367        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
12368
12369        // On the axes, where the unit vector is exact and so is the dot
12370        // product, both ends of the scale come out exact: the same direction is
12371        // 1 and the opposite one is 0, with a right angle at a half.
12372        let mut f = Fixture::new();
12373        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
12374        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
12375        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
12376        assert_eq!(
12377            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
12378            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
12379             $8\r\nopposite\r\n$1\r\n0\r\n"
12380        );
12381        // A search from an element leaves that element out, since it is always
12382        // its own nearest neighbour.
12383        assert_eq!(
12384            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
12385            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12386        );
12387        // An element that is not there is an empty answer and not an error,
12388        // which is what a missing key gives too.
12389        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
12390        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
12391        // COUNT bounds it and TRUTH reads every vector rather than the codes,
12392        // which has to agree with the index on a set this small.
12393        assert_eq!(
12394            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
12395            "*1\r\n$6\r\nacross\r\n"
12396        );
12397        assert_eq!(
12398            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
12399            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12400        );
12401        // EF widens how much of the index is read and does not change how many
12402        // answers come back, so a wide search still returns what COUNT asked
12403        // for.
12404        assert_eq!(
12405            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
12406            "*1\r\n$6\r\nacross\r\n"
12407        );
12408
12409        // On RESP3 a scored search is a map, which is what the vector set
12410        // module replies and is not what ZRANGE does here.
12411        let mut g = Fixture::new();
12412        g.run(&[b"HELLO", b"3"]);
12413        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12414        assert_eq!(
12415            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
12416            "%1\r\n$4\r\neast\r\n,1\r\n"
12417        );
12418    }
12419
12420    /// The attribute pair, and the one reply that means two things.
12421    #[test]
12422    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
12423        let mut f = Fixture::new();
12424        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12425        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12426        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
12427        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
12428        // Not parsed as JSON, because nothing reads into it yet and refusing a
12429        // write for a rule nothing enforces would be the wrong trade.
12430        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
12431        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
12432        // An empty string clears it, which is Redis's spelling of the removal.
12433        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
12434        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12435        // An element that is not there answers zero rather than being created,
12436        // since an attribute with no vector under it is not a thing this holds.
12437        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
12438        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
12439        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
12440        // A null for an element with no attribute and a null for one that is
12441        // not there. VISMEMBER is how a client tells the two apart.
12442        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
12443        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
12444        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
12445        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
12446
12447        // WITHATTRIBS carries it alongside the answers.
12448        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12449        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12450        assert_eq!(
12451            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
12452            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
12453        );
12454    }
12455
12456    /// The slot a removed element had is reused, and nothing that was beside it
12457    /// comes back with the next element to get it.
12458    #[test]
12459    fn vrem_takes_the_attribute_with_it() {
12460        let mut f = Fixture::new();
12461        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12462        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12463        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
12464        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
12465        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
12466        // The key went with the last element, the way every other collection
12467        // here works.
12468        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12469
12470        // The next element is given the slot the removed one had, and it comes
12471        // with no attribute on it.
12472        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12473        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12474        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12475        f.run(&[b"VREM", b"v", b"east"]);
12476        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
12477        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
12478    }
12479
12480    /// `VINFO` says what the index is before it says anything a client could
12481    /// mistake for a graph.
12482    #[test]
12483    fn vinfo_says_partition_first() {
12484        let mut f = Fixture::new();
12485        f.run(&[
12486            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12487        ]);
12488        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12489        let info = f.run(&[b"VINFO", b"v"]);
12490        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12491        // What the client asked for and not what happened to the tuning, which
12492        // is `10` section 7: M is recorded and changes nothing.
12493        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12494        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12495        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12496        // Nobody named a quantisation, so this set is a `Q8` one and every
12497        // element in it is stored that way.
12498        assert!(
12499            info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12500            "{info}"
12501        );
12502        let mut f = Fixture::new();
12503        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12504        assert!(
12505            f.run(&[b"VINFO", b"v"])
12506                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12507        );
12508        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12509    }
12510
12511    /// A set to read ranges of names out of.
12512    fn named() -> Fixture {
12513        let mut f = Fixture::new();
12514        for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12515            .iter()
12516            .enumerate()
12517        {
12518            let x = (i + 1).to_string();
12519            f.run(&[
12520                b"VADD",
12521                b"r",
12522                b"VALUES",
12523                b"2",
12524                x.as_bytes(),
12525                b"1",
12526                name.as_bytes(),
12527            ]);
12528        }
12529        f
12530    }
12531
12532    /// `VRANGE` reads the names in the order bytes come in and pays no
12533    /// attention to where the vectors point.
12534    #[test]
12535    fn vrange_walks_the_names_and_not_the_vectors() {
12536        let mut f = named();
12537        assert_eq!(
12538            f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12539            "*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"
12540        );
12541        assert_eq!(
12542            f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12543            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12544            "the high end is a name and not a prefix, so delta is past it"
12545        );
12546        assert_eq!(
12547            f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12548            "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12549        );
12550        assert_eq!(
12551            f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12552            "*1\r\n$4\r\nbeta\r\n"
12553        );
12554        assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12555        // Bytes and not letters, so an upper case name sorts before every lower
12556        // case one rather than beside its own spelling.
12557        f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12558        assert_eq!(
12559            f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12560            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12561        );
12562        assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12563    }
12564
12565    /// The count cuts the answer after the range is decided, and zero is not
12566    /// the same as leaving it out.
12567    #[test]
12568    fn a_vrange_count_of_zero_asks_for_nothing() {
12569        let mut f = named();
12570        assert_eq!(
12571            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12572            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12573        );
12574        assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12575        assert!(
12576            f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12577                .starts_with("*5\r\n"),
12578            "a negative count is no limit at all"
12579        );
12580    }
12581
12582    /// Both ends are read before either is placed, and the count is read before
12583    /// either end.
12584    #[test]
12585    fn vrange_says_which_end_it_could_not_read() {
12586        let mut f = named();
12587        assert_eq!(
12588            f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12589            "-ERR invalid start range format\r\n"
12590        );
12591        assert_eq!(
12592            f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12593            "-ERR invalid end range format\r\n",
12594            "the high end is spelled wrong, which is worth saying before the \
12595             low end being on the wrong side"
12596        );
12597        assert_eq!(
12598            f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12599            "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12600        );
12601        // A bracket with nothing after it is not the empty name here, though an
12602        // element really can be called that.
12603        assert_eq!(
12604            f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12605            "-ERR invalid start range format\r\n"
12606        );
12607        assert_eq!(
12608            f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12609            "-ERR invalid COUNT value\r\n"
12610        );
12611        assert_eq!(
12612            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12613            "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12614        );
12615        f.run(&[b"SET", b"s", b"x"]);
12616        assert!(
12617            f.run(&[b"VRANGE", b"s", b"-", b"+"])
12618                .starts_with("-WRONGTYPE")
12619        );
12620    }
12621
12622    /// The option that asks for something this index does not have says so
12623    /// rather than doing something else quietly.
12624    #[test]
12625    fn reduce_is_refused_and_not_ignored() {
12626        let mut f = Fixture::new();
12627        let reduce = f.run(&[
12628            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12629        ]);
12630        assert!(
12631            reduce.starts_with("-ERR REDUCE is not supported."),
12632            "{reduce}"
12633        );
12634        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12635    }
12636
12637    /// A filtered search answers with the nearest elements that match, and an
12638    /// expression that is not one is an error before the key is looked at.
12639    #[test]
12640    fn vsim_filter_reads_the_attributes() {
12641        let mut f = Fixture::new();
12642        for (name, x, y, attr) in [
12643            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12644            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12645            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12646            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12647        ] {
12648            f.run(&[
12649                b"VADD",
12650                b"v",
12651                b"VALUES",
12652                b"2",
12653                x.as_bytes(),
12654                y.as_bytes(),
12655                name.as_bytes(),
12656                b"SETATTR",
12657                attr.as_bytes(),
12658            ]);
12659        }
12660        // `b` is the nearest to the query and is the one the filter drops, so
12661        // this is the answer a filter applied afterwards would have got wrong.
12662        assert_eq!(
12663            f.run(&[
12664                b"VSIM",
12665                b"v",
12666                b"VALUES",
12667                b"2",
12668                b"9",
12669                b"1",
12670                b"COUNT",
12671                b"2",
12672                b"FILTER",
12673                b".lang == \"en\"",
12674            ]),
12675            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12676        );
12677        // A number is compared as a number, and the two halves of an `and` both
12678        // have to hold.
12679        assert_eq!(
12680            f.run(&[
12681                b"VSIM",
12682                b"v",
12683                b"VALUES",
12684                b"2",
12685                b"9",
12686                b"1",
12687                b"FILTER",
12688                b".lang == 'en' and .year > 1980",
12689            ]),
12690            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12691        );
12692        // A list, and a field an element does not have.
12693        assert_eq!(
12694            f.run(&[
12695                b"VSIM",
12696                b"v",
12697                b"VALUES",
12698                b"2",
12699                b"9",
12700                b"1",
12701                b"FILTER",
12702                b".lang in ['fr', 'de']",
12703            ]),
12704            "*1\r\n$1\r\nb\r\n"
12705        );
12706        assert_eq!(
12707            f.run(&[
12708                b"VSIM",
12709                b"v",
12710                b"VALUES",
12711                b"2",
12712                b"9",
12713                b"1",
12714                b"FILTER",
12715                b".rating > 3"
12716            ]),
12717            "*0\r\n"
12718        );
12719        // TRUTH measures every vector, and the filter still decides which ones
12720        // are measured.
12721        assert_eq!(
12722            f.run(&[
12723                b"VSIM",
12724                b"v",
12725                b"VALUES",
12726                b"2",
12727                b"9",
12728                b"1",
12729                b"TRUTH",
12730                b"FILTER",
12731                b".year < 1980",
12732            ]),
12733            "*1\r\n$1\r\nc\r\n"
12734        );
12735        // VSETATTR moves an element in and out of a filter, which means the tag
12736        // beside its code was rewritten and not just the string.
12737        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12738        assert_eq!(
12739            f.run(&[
12740                b"VSIM",
12741                b"v",
12742                b"VALUES",
12743                b"2",
12744                b"9",
12745                b"1",
12746                b"COUNT",
12747                b"1",
12748                b"FILTER",
12749                b".lang == \"en\"",
12750            ]),
12751            "*1\r\n$1\r\nb\r\n"
12752        );
12753        // And a VADD that replaces the vector keeps the attribute and the tag,
12754        // which is the same rewrite from the other end.
12755        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12756        assert_eq!(
12757            f.run(&[
12758                b"VSIM",
12759                b"v",
12760                b"VALUES",
12761                b"2",
12762                b"9",
12763                b"1",
12764                b"COUNT",
12765                b"1",
12766                b"FILTER",
12767                b".lang == \"en\"",
12768            ]),
12769            "*1\r\n$1\r\nb\r\n"
12770        );
12771
12772        // The expression is parsed before the key is read, so a bad one is an
12773        // error whether or not the key is there.
12774        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12775        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12776        assert_eq!(
12777            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12778            "-ERR invalid FILTER expression\r\n"
12779        );
12780        // FILTER-EF raises the effort rather than capping it, and zero is
12781        // Redis's word for no limit, so neither is an error.
12782        assert_eq!(
12783            f.run(&[
12784                b"VSIM",
12785                b"v",
12786                b"VALUES",
12787                b"2",
12788                b"9",
12789                b"1",
12790                b"COUNT",
12791                b"1",
12792                b"FILTER-EF",
12793                b"500",
12794                b"FILTER",
12795                b".lang == 'en'",
12796            ]),
12797            "*1\r\n$1\r\nb\r\n"
12798        );
12799        assert_eq!(
12800            f.run(&[
12801                b"VSIM",
12802                b"v",
12803                b"VALUES",
12804                b"2",
12805                b"9",
12806                b"1",
12807                b"COUNT",
12808                b"1",
12809                b"FILTER-EF",
12810                b"0"
12811            ]),
12812            "*1\r\n$1\r\nb\r\n"
12813        );
12814        assert_eq!(
12815            f.run(&[
12816                b"VSIM",
12817                b"v",
12818                b"VALUES",
12819                b"2",
12820                b"9",
12821                b"1",
12822                b"FILTER-EF",
12823                b"lots"
12824            ]),
12825            "-ERR EF must be a positive integer\r\n"
12826        );
12827    }
12828
12829    /// A vector set key is a key, so the keyspace owns it the way it owns every
12830    /// other one and none of those commands know what is inside it.
12831    #[test]
12832    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12833        let mut f = Fixture::new();
12834        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12835        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12836        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12837        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12838        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12839        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12840        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12841        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12842        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12843        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12844        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12845
12846        // And the wrong type is the wrong type in both directions.
12847        f.run(&[b"SET", b"s", b"1"]);
12848        assert_eq!(
12849            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12850            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12851        );
12852        assert_eq!(
12853            f.run(&[b"VCARD", b"s"]),
12854            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12855        );
12856        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12857        assert_eq!(
12858            f.run(&[b"GET", b"v"]),
12859            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12860        );
12861        // A graph and a vector set share the escape in the record tag and are
12862        // still two different types, which is the case the tag alone cannot
12863        // decide.
12864        f.run(&[b"G.NADD", b"social", b"ada"]);
12865        assert_eq!(
12866            f.run(&[b"VCARD", b"social"]),
12867            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12868        );
12869        assert_eq!(
12870            f.run(&[b"G.NGET", b"v", b"ada"]),
12871            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12872        );
12873    }
12874
12875    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
12876    /// shapes, off the database's own generator.
12877    #[test]
12878    fn vrandmember_has_the_two_shapes_srandmember_has() {
12879        let mut f = Fixture::new();
12880        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12881            let x = (i + 1).to_string();
12882            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12883        }
12884        // One element is a bulk string and not an array of one.
12885        let one = f.run(&[b"VRANDMEMBER", b"v"]);
12886        assert!(one.starts_with("$1\r\n"), "{one}");
12887        // A positive count is distinct and stops at the size of the set.
12888        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12889        assert!(all.starts_with("*3\r\n"), "{all}");
12890        for name in ["a", "b", "c"] {
12891            assert!(all.contains(name), "{all} is missing {name}");
12892        }
12893        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12894        assert!(all.starts_with("*2\r\n"), "{all}");
12895        // A negative one draws that many and allows repeats.
12896        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12897        assert!(many.starts_with("*5\r\n"), "{many}");
12898        // A key that is not there answers the shape that was asked for.
12899        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12900        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12901    }
12902
12903    /// `VLINKS` answers about the index that is here rather than the graph that
12904    /// is not, which is D-2.
12905    #[test]
12906    fn vlinks_reports_one_layer_of_partition_neighbours() {
12907        let mut f = Fixture::new();
12908        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12909        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12910        // One layer deep, because the index is one layer deep, so a client
12911        // walking layers gets a short list and not a shape it cannot parse.
12912        assert_eq!(
12913            f.run(&[b"VLINKS", b"v", b"east"]),
12914            "*1\r\n*1\r\n$5\r\nnorth\r\n"
12915        );
12916        assert_eq!(
12917            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12918            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12919        );
12920        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12921        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12922    }
12923
12924    /// A vector arrives either as digits or as bytes, and the two have to mean
12925    /// the same thing.
12926    #[test]
12927    fn fp32_and_values_are_the_same_vector() {
12928        let mut f = Fixture::new();
12929        let mut blob = Vec::new();
12930        for x in [3.0f32, 4.0] {
12931            blob.extend_from_slice(&x.to_le_bytes());
12932        }
12933        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12934        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12935        assert_eq!(
12936            f.run(&[b"VEMB", b"v", b"a"]),
12937            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12938        );
12939        // RAW is the stored bytes and the numbers that turn them back into the
12940        // client's vector, which for `Q8` is a code a coordinate, the length the
12941        // vector arrived with and the scale the codes are measured against. The
12942        // name of the form is a simple string, which is a real server's shape,
12943        // and all four of these are a real server's answers.
12944        assert_eq!(
12945            f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12946            "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12947        );
12948        // A blob that is not a whole number of floats is not a vector.
12949        assert_eq!(
12950            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12951            "-ERR invalid vector specification\r\n"
12952        );
12953        // Neither is a count that promises more than arrived.
12954        assert_eq!(
12955            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12956            "-ERR syntax error\r\n"
12957        );
12958        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12959    }
12960
12961    // ----------------------------------------------------------------- bloom
12962
12963    /// The filter a client gets when it does not describe one, and the two
12964    /// answers an add can give.
12965    #[test]
12966    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12967        let mut f = Fixture::new();
12968        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12969        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12970        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12971        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12972        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12973        // The defaults are the module's configs and not anything the command
12974        // said, which is 100 entries at a hundredth and a growth of 2.
12975        assert_eq!(
12976            f.run(&[b"BF.INFO", b"b"]),
12977            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12978             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12979             +Expansion rate\r\n:2\r\n"
12980        );
12981        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12982        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12983        // A key that is not there has no filter to report on, and answers two
12984        // different ways about it depending on which command asked.
12985        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12986        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12987    }
12988
12989    /// `BF.EXISTS` on a key holding something else answers a miss, and
12990    /// everything else in the family answers `WRONGTYPE`.
12991    ///
12992    /// The two halves of a check and set disagree about what that key is, which
12993    /// is the module's behaviour and not a decision taken here.
12994    #[test]
12995    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12996        let mut f = Fixture::new();
12997        f.run(&[b"SET", b"s", b"text"]);
12998        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12999        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13000        for cmd in [
13001            vec![&b"BF.ADD"[..], b"s", b"x"],
13002            vec![&b"BF.MADD"[..], b"s", b"x"],
13003            vec![&b"BF.CARD"[..], b"s"],
13004            vec![&b"BF.INFO"[..], b"s"],
13005            vec![&b"BF.DEBUG"[..], b"s"],
13006            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
13007        ] {
13008            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13009            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13010        }
13011        // The arguments are read before the key is, so a reserve with a bad
13012        // error rate complains about the rate and never learns about the string.
13013        assert_eq!(
13014            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
13015            "-ERR bad error rate\r\n"
13016        );
13017        assert!(
13018            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
13019                .starts_with("-WRONGTYPE")
13020        );
13021    }
13022
13023    /// A chain grows by its expansion factor and each link is half as wrong as
13024    /// the one before, which is what makes the whole filter hold its rate.
13025    #[test]
13026    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
13027        let mut f = Fixture::new();
13028        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
13029        for i in 0..10u32 {
13030            assert_eq!(
13031                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
13032                ":1\r\n"
13033            );
13034        }
13035        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
13036        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
13037        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
13038        // Capacity is the sum of every link and not the number that was asked
13039        // for, so it is 10 and then 10 plus 20.
13040        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
13041        assert_eq!(
13042            f.run(&[b"BF.DEBUG", b"g"]),
13043            "*3\r\n$7\r\nsize:11\r\n\
13044             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
13045             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
13046        );
13047
13048        // The same filter told not to grow fills instead.
13049        assert_eq!(
13050            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
13051            "+OK\r\n"
13052        );
13053        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
13054        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
13055        assert_eq!(
13056            f.run(&[b"BF.ADD", b"n", b"c"]),
13057            "-ERR non scaling filter is full\r\n"
13058        );
13059        // And an item that is already in it still answers, because membership
13060        // is checked before fullness.
13061        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
13062        // A filter that will not grow has no expansion rate to report, in
13063        // either of the two spellings that make one.
13064        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
13065        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
13066        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
13067        // Asking for both at once is refused, which is one of the module's
13068        // errors that carries no prefix at all.
13069        assert_eq!(
13070            f.run(&[
13071                b"BF.RESERVE",
13072                b"q",
13073                b"0.01",
13074                b"2",
13075                b"NONSCALING",
13076                b"EXPANSION",
13077                b"2"
13078            ]),
13079            "-Nonscaling filters cannot expand\r\n"
13080        );
13081    }
13082
13083    /// A multi add stops where the filter did, so the reply can be shorter than
13084    /// the argument list.
13085    #[test]
13086    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
13087        let mut f = Fixture::new();
13088        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
13089        assert_eq!(
13090            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
13091            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
13092        );
13093        assert_eq!(
13094            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
13095            "*2\r\n:1\r\n:0\r\n"
13096        );
13097    }
13098
13099    /// `BF.INSERT` describes a filter and fills it in one command, with its own
13100    /// spelling of every complaint.
13101    #[test]
13102    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
13103        let mut f = Fixture::new();
13104        assert_eq!(
13105            f.run(&[
13106                b"BF.INSERT",
13107                b"i",
13108                b"CAPACITY",
13109                b"50",
13110                b"ERROR",
13111                b"0.001",
13112                b"ITEMS",
13113                b"a",
13114                b"b"
13115            ]),
13116            "*2\r\n:1\r\n:1\r\n"
13117        );
13118        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
13119        // NOCREATE is the only way to add without making the key.
13120        assert_eq!(
13121            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13122            "-ERR not found\r\n"
13123        );
13124        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13125        // The same mistakes as BF.RESERVE, in the sentences this command uses
13126        // for them, and one sentence where BF.RESERVE has two.
13127        assert_eq!(
13128            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13129            "-Bad capacity\r\n"
13130        );
13131        assert_eq!(
13132            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
13133            "-Bad error rate\r\n"
13134        );
13135        assert_eq!(
13136            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
13137            "-Bad expansion\r\n"
13138        );
13139        // An option is matched on its first letter and not on the word, so a
13140        // token nobody meant as an option is one anyway if it starts with the
13141        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
13142        // builds says so.
13143        assert_eq!(
13144            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
13145            "*1\r\n:1\r\n"
13146        );
13147        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
13148        // Only E and N need a second look, one for ERROR against EXPANSION and
13149        // the other for NOCREATE against NONSCALING, and both stop as soon as
13150        // they can tell the two apart.
13151        assert_eq!(
13152            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
13153            "*1\r\n:1\r\n"
13154        );
13155        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
13156        assert_eq!(
13157            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
13158            "*1\r\n:1\r\n"
13159        );
13160        assert_eq!(
13161            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
13162            "-ERR not found\r\n"
13163        );
13164        // A letter that starts nothing is the one case that is refused.
13165        assert_eq!(
13166            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13167            "-Unknown argument received\r\n"
13168        );
13169        // Everything after ITEMS is an item, even when it spells an option.
13170        assert_eq!(
13171            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13172            "*1\r\n:1\r\n"
13173        );
13174        // And ITEMS with nothing after it is the same as leaving it out.
13175        assert!(
13176            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
13177                .contains("wrong number of arguments")
13178        );
13179    }
13180
13181    /// A filter dumped a chunk at a time and put back into another key is the
13182    /// same filter.
13183    #[test]
13184    fn a_dump_replays_into_a_filter_that_answers_the_same() {
13185        let mut f = Fixture::new();
13186        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
13187        for i in 0..25u32 {
13188            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
13189        }
13190        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
13191
13192        // Iterator zero asks for the header and every one after it is a running
13193        // byte offset, and a chunk never spans two links.
13194        let mut iter = b"0".to_vec();
13195        let mut chunks = 0;
13196        loop {
13197            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
13198            let text = String::from_utf8_lossy(&raw).into_owned();
13199            let next = text
13200                .split("\r\n")
13201                .nth(1)
13202                .and_then(|n| n.strip_prefix(':'))
13203                .expect("a two element reply of an iterator and a chunk")
13204                .to_owned();
13205            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13206            let data = &body[body
13207                .windows(2)
13208                .position(|w| w == b"\r\n")
13209                .expect("a length line")
13210                + 2..body.len() - 2];
13211            if next == "0" {
13212                assert!(data.is_empty(), "the last chunk is empty");
13213                break;
13214            }
13215            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
13216            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
13217            iter = next.into_bytes();
13218            chunks += 1;
13219        }
13220        assert_eq!(chunks, 3, "a header and one chunk per link");
13221
13222        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
13223        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
13224        for i in 0..25u32 {
13225            assert_eq!(
13226                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
13227                ":1\r\n"
13228            );
13229        }
13230
13231        // A header on top of a filter is refused rather than merged, and so is
13232        // one that no filter wrote.
13233        assert_eq!(
13234            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
13235            "-ERR received bad data\r\n"
13236        );
13237        assert_eq!(
13238            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
13239            "-ERR received bad data\r\n"
13240        );
13241        // An offset past the end of the filter names itself.
13242        assert_eq!(
13243            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
13244            "-ERR invalid offset - no link found\r\n"
13245        );
13246        assert_eq!(
13247            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
13248            "-ERR Second argument must be numeric\r\n"
13249        );
13250        // The same complaint without the prefix on the way out, which is the
13251        // module's inconsistency and not a slip here.
13252        assert_eq!(
13253            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
13254            "-Second argument must be numeric\r\n"
13255        );
13256    }
13257
13258    /// The argument checks, which have a sentence each and read numbers the way
13259    /// Redis reads them everywhere else.
13260    #[test]
13261    fn reserve_reads_its_numbers_the_way_string2ll_does() {
13262        let mut f = Fixture::new();
13263        for (args, want) in [
13264            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
13265            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
13266            (
13267                vec![&b"0"[..], b"10"],
13268                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13269            ),
13270            (
13271                vec![&b"1"[..], b"10"],
13272                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13273            ),
13274            (
13275                vec![&b"inf"[..], b"10"],
13276                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13277            ),
13278            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
13279            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
13280            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
13281            (
13282                vec![&b"0.01"[..], b"0"],
13283                "-ERR capacity must be in the range [1, 1073741824]\r\n",
13284            ),
13285            (
13286                vec![&b"0.01"[..], b"1073741825"],
13287                "-ERR capacity must be in the range [1, 1073741824]\r\n",
13288            ),
13289        ] {
13290            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
13291            cmd.extend(args.iter().copied());
13292            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
13293        }
13294        assert_eq!(
13295            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
13296            "-ERR no expansion\r\n"
13297        );
13298        assert_eq!(
13299            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
13300            "-ERR bad expansion\r\n"
13301        );
13302        assert_eq!(
13303            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
13304            "-ERR expansion must be in the range [0, 32768]\r\n"
13305        );
13306        // Trailing rubbish after the capacity is ignored rather than refused.
13307        assert_eq!(
13308            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
13309            "+OK\r\n"
13310        );
13311        assert_eq!(
13312            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
13313            "-ERR item exists\r\n"
13314        );
13315        assert_eq!(
13316            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
13317            "-Invalid information value\r\n"
13318        );
13319        assert!(
13320            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
13321                .contains("wrong number of arguments")
13322        );
13323    }
13324
13325    /// The RESP3 shapes, which are where this family differs most from RESP2.
13326    #[test]
13327    fn the_bloom_family_answers_in_resp3_spelling_too() {
13328        let mut f = Fixture::new();
13329        f.out.set_proto(Proto::Resp3);
13330        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
13331        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
13332        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
13333        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
13334        assert_eq!(
13335            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
13336            "*2\r\n#t\r\n#f\r\n"
13337        );
13338        // The count stays an integer, because it counts rather than answers.
13339        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
13340        assert_eq!(
13341            f.run(&[b"BF.INFO", b"b"]),
13342            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
13343             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
13344             +Expansion rate\r\n:2\r\n"
13345        );
13346        // One field is a map of one here and a bare array of one on RESP2, so
13347        // this is the reply where the two protocols carry different facts.
13348        assert_eq!(
13349            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
13350            "%1\r\n+Capacity\r\n:100\r\n"
13351        );
13352    }
13353
13354    // ---------------------------------------------------------------- cuckoo
13355
13356    /// A dump header, which is the four counts and the three widths a filter
13357    /// writes in front of its fingerprints.
13358    ///
13359    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
13360    /// tests below want out of it is the states a filter cannot be put into
13361    /// from the wire.
13362    fn cf_header(
13363        items: u64,
13364        buckets: u64,
13365        deletes: u64,
13366        filters: u64,
13367        geometry: [u16; 3],
13368    ) -> Vec<u8> {
13369        let mut out = Vec::with_capacity(38);
13370        for n in [items, buckets, deletes, filters] {
13371            out.extend_from_slice(&n.to_le_bytes());
13372        }
13373        for n in geometry {
13374            out.extend_from_slice(&n.to_le_bytes());
13375        }
13376        out
13377    }
13378
13379    /// The filter a client gets when it does not describe one, and the thing a
13380    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
13381    /// take them out again.
13382    #[test]
13383    fn cf_add_makes_the_filter_and_counts_the_copies() {
13384        let mut f = Fixture::new();
13385        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13386        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13387        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
13388        // The NX form is the one that looks first, which is why it is a command
13389        // of its own rather than an option.
13390        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
13391        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
13392        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
13393        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
13394        assert_eq!(
13395            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
13396            "*2\r\n:1\r\n:0\r\n"
13397        );
13398        // The defaults are the module's configs: 1024 entries over buckets of
13399        // two, twenty kicks and a chain that grows by one.
13400        assert_eq!(
13401            f.run(&[b"CF.INFO", b"d"]),
13402            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13403             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
13404             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
13405             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13406        );
13407        assert_eq!(
13408            f.run(&[b"CF.DEBUG", b"d"]),
13409            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
13410             max_iterations:20 expansion:1\r\n"
13411        );
13412        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
13413        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13414
13415        // A delete takes one copy, so the same item goes twice and then stops.
13416        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13417        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
13418        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13419        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
13420        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
13421
13422        // A key with no filter under it gets three different sentences and one
13423        // plain miss, depending on which command asked.
13424        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
13425        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
13426        assert_eq!(
13427            f.run(&[b"CF.COMPACT", b"gone"]),
13428            "-Cuckoo filter was not found\r\n"
13429        );
13430        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
13431        // And `CF.COMPACT` is declared as taking any number of keys and takes
13432        // exactly one, which is the module's own arity being wrong rather than
13433        // this table's.
13434        assert!(
13435            f.run(&[b"CF.COMPACT", b"a", b"b"])
13436                .contains("wrong number of arguments")
13437        );
13438    }
13439
13440    /// The four that only read fingerprints treat a key holding something else
13441    /// as a key with no filter, and everything else answers `WRONGTYPE`.
13442    #[test]
13443    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
13444        let mut f = Fixture::new();
13445        f.run(&[b"SET", b"s", b"text"]);
13446        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
13447        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13448        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
13449        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
13450        // and is declared read only, so neither of the two halves of the family
13451        // is the same set as the flags say.
13452        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
13453        assert_eq!(
13454            f.run(&[b"CF.COMPACT", b"s"]),
13455            "-Cuckoo filter was not found\r\n"
13456        );
13457        for cmd in [
13458            vec![&b"CF.ADD"[..], b"s", b"x"],
13459            vec![&b"CF.ADDNX"[..], b"s", b"x"],
13460            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
13461            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
13462            vec![&b"CF.INFO"[..], b"s"],
13463            vec![&b"CF.DEBUG"[..], b"s"],
13464            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
13465            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
13466            vec![&b"CF.RESERVE"[..], b"s", b"64"],
13467        ] {
13468            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13469            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13470        }
13471    }
13472
13473    /// `CF.RESERVE` reads its options by name in an order of its own, and the
13474    /// first pair with a given name is the only one it looks at.
13475    #[test]
13476    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
13477        let mut f = Fixture::new();
13478        assert_eq!(
13479            f.run(&[
13480                b"CF.RESERVE",
13481                b"r",
13482                b"64",
13483                b"BUCKETSIZE",
13484                b"1",
13485                b"MAXITERATIONS",
13486                b"7",
13487                b"EXPANSION",
13488                b"4"
13489            ]),
13490            "+OK\r\n"
13491        );
13492        assert_eq!(
13493            f.run(&[b"CF.DEBUG", b"r"]),
13494            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13495             max_iterations:7 expansion:4\r\n"
13496        );
13497        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13498
13499        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13500        assert_eq!(
13501            f.run(&[b"CF.RESERVE", b"q", b"1"]),
13502            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13503        );
13504        // The range is the bucket size's and not a constant, so a capacity that
13505        // was fine at two slots a bucket is not at four.
13506        assert_eq!(
13507            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13508            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13509        );
13510        assert_eq!(
13511            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13512            "+OK\r\n"
13513        );
13514
13515        // The capacity is checked last, so a command that is wrong twice
13516        // answers about the option. Which option it answers about is the order
13517        // the module looks for them in and not the order they were written, so
13518        // a bad kick budget wins over a bad bucket size wherever the two sit.
13519        assert_eq!(
13520            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13521            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13522        );
13523        assert_eq!(
13524            f.run(&[
13525                b"CF.RESERVE",
13526                b"q2",
13527                b"64",
13528                b"EXPANSION",
13529                b"xx",
13530                b"BUCKETSIZE",
13531                b"0"
13532            ]),
13533            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13534        );
13535        assert_eq!(
13536            f.run(&[
13537                b"CF.RESERVE",
13538                b"q2",
13539                b"64",
13540                b"MAXITERATIONS",
13541                b"0",
13542                b"BUCKETSIZE",
13543                b"0"
13544            ]),
13545            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13546        );
13547        // A second pair with a name that has already been read is not looked at
13548        // at all, so this one is a filter with buckets of one rather than an
13549        // error about a bucket size of zero.
13550        assert_eq!(
13551            f.run(&[
13552                b"CF.RESERVE",
13553                b"q3",
13554                b"64",
13555                b"BUCKETSIZE",
13556                b"1",
13557                b"BUCKETSIZE",
13558                b"0"
13559            ]),
13560            "+OK\r\n"
13561        );
13562        // A pair nobody knows is dropped, which is the opposite of what
13563        // `CF.INSERT` does with the same mistake.
13564        assert_eq!(
13565            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13566            "+OK\r\n"
13567        );
13568        assert_eq!(
13569            f.run(&[b"CF.DEBUG", b"q4"]),
13570            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13571             max_iterations:20 expansion:1\r\n"
13572        );
13573        // And an option with nothing after it leaves an odd number of them,
13574        // which is an arity error rather than a complaint about the option.
13575        assert!(
13576            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13577                .contains("wrong number of arguments")
13578        );
13579    }
13580
13581    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
13582    /// with `CF.RESERVE` about nothing.
13583    #[test]
13584    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13585        let mut f = Fixture::new();
13586        assert_eq!(
13587            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13588            "*2\r\n:1\r\n:1\r\n"
13589        );
13590        assert_eq!(
13591            f.run(&[b"CF.DEBUG", b"i"]),
13592            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13593             max_iterations:20 expansion:1\r\n"
13594        );
13595        // The NX form has three answers rather than two, which is why it stays
13596        // integers on both protocols.
13597        assert_eq!(
13598            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13599            "*2\r\n:0\r\n:1\r\n"
13600        );
13601        assert_eq!(
13602            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13603            "-ERR not found\r\n"
13604        );
13605        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13606
13607        assert_eq!(
13608            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13609            "-Bad capacity\r\n"
13610        );
13611        // The bucket size cannot be given here, so the range names the config
13612        // that holds it instead of the option `CF.RESERVE` names.
13613        assert_eq!(
13614            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13615            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13616        );
13617        // Every occurrence is checked, which is where this differs from
13618        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
13619        // one is the one that would have been used.
13620        assert_eq!(
13621            f.run(&[
13622                b"CF.INSERT",
13623                b"i",
13624                b"CAPACITY",
13625                b"8",
13626                b"CAPACITY",
13627                b"2",
13628                b"ITEMS",
13629                b"a"
13630            ]),
13631            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13632        );
13633        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
13634        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
13635        // refused.
13636        assert_eq!(
13637            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13638            "*1\r\n:1\r\n"
13639        );
13640        assert_eq!(
13641            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13642            "*1\r\n:1\r\n"
13643        );
13644        assert_eq!(
13645            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13646            "-Unknown argument received\r\n"
13647        );
13648        // Everything after ITEMS is an item, even when it spells an option.
13649        assert_eq!(
13650            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13651            "*1\r\n:1\r\n"
13652        );
13653        // And the two ways of sending no items at all are the same complaint.
13654        assert!(
13655            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13656                .contains("wrong number of arguments")
13657        );
13658        assert!(
13659            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13660                .contains("wrong number of arguments")
13661        );
13662    }
13663
13664    /// The two walls a filter can hit, which say different things and are not
13665    /// the same wall.
13666    #[test]
13667    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13668        let mut f = Fixture::new();
13669        f.run(&[
13670            b"CF.RESERVE",
13671            b"s",
13672            b"4",
13673            b"BUCKETSIZE",
13674            b"1",
13675            b"EXPANSION",
13676            b"0",
13677        ]);
13678        for i in 0..4u32 {
13679            assert_eq!(
13680                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13681                ":1\r\n"
13682            );
13683        }
13684        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13685        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13686        // The add commands say it in a sentence and the insert commands say it
13687        // in the array, one value per item, and the array is never short.
13688        assert_eq!(
13689            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13690            "*2\r\n:-1\r\n:-1\r\n"
13691        );
13692        assert_eq!(
13693            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13694            "*2\r\n:0\r\n:-1\r\n"
13695        );
13696
13697        // A chain that is allowed to grow stops for a different reason, and the
13698        // count it stops at is the filter limit rather than the room: this one
13699        // gives up with three slots free. Loading a chain that already has
13700        // every filter it is allowed shows why, since it refuses an item
13701        // straight into an empty one.
13702        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13703        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13704        assert_eq!(
13705            f.run(&[b"CF.ADD", b"g", b"q"]),
13706            "-Maximum expansions reached\r\n"
13707        );
13708        assert_eq!(
13709            f.run(&[b"CF.INFO", b"g"]),
13710            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13711             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13712             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13713             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13714        );
13715    }
13716
13717    /// A filter dumped a chunk at a time and put back under another key is the
13718    /// same filter, and the headers that describe one nobody could build are
13719    /// refused on the way in.
13720    #[test]
13721    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13722        let mut f = Fixture::new();
13723        f.run(&[
13724            b"CF.RESERVE",
13725            b"src",
13726            b"8",
13727            b"BUCKETSIZE",
13728            b"2",
13729            b"EXPANSION",
13730            b"2",
13731        ]);
13732        for i in 0..40u32 {
13733            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13734        }
13735        // Position zero asks for the header and every one after it is a byte
13736        // offset across every filter laid end to end, and the walk ends on a
13737        // zero and a nil rather than an empty chunk.
13738        let mut pos = b"0".to_vec();
13739        let mut chunks = 0;
13740        loop {
13741            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13742            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13743            let next = head
13744                .split("\r\n")
13745                .nth(1)
13746                .and_then(|n| n.strip_prefix(':'))
13747                .expect("a two element reply of a position and a chunk")
13748                .to_owned();
13749            if next == "0" {
13750                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13751                break;
13752            }
13753            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13754            let at = body
13755                .windows(2)
13756                .position(|w| w == b"\r\n")
13757                .expect("a length line")
13758                + 2;
13759            let data = &body[at..body.len() - 2];
13760            assert_eq!(
13761                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13762                "+OK\r\n",
13763                "loading chunk {chunks}"
13764            );
13765            pos = next.into_bytes();
13766            chunks += 1;
13767        }
13768        assert!(chunks >= 2, "a header and at least one chunk");
13769
13770        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13771        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13772        for i in 0..40u32 {
13773            assert_eq!(
13774                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13775                ":1\r\n"
13776            );
13777        }
13778
13779        // A filter with nothing in it hands out no header at all, so a client
13780        // that dumps one has nothing to load back.
13781        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13782        assert_eq!(
13783            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13784            "*2\r\n:0\r\n$-1\r\n"
13785        );
13786
13787        // The positions this end will not take, which are not the same set at
13788        // both ends: a dump refuses a negative one and a load takes it as an
13789        // offset and fails to find anything there.
13790        assert_eq!(
13791            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13792            "-Invalid position\r\n"
13793        );
13794        assert_eq!(
13795            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13796            "-Invalid position\r\n"
13797        );
13798        assert_eq!(
13799            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13800            "-Invalid position\r\n"
13801        );
13802        assert_eq!(
13803            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13804            "-Couldn't load chunk!\r\n"
13805        );
13806        // A header on top of a filter is refused rather than merged.
13807        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13808        assert_eq!(
13809            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13810            "-ERR item exists\r\n"
13811        );
13812        // A chunk that is not the size of a header where a header should have
13813        // been is one sentence, and one that is the size of a header and
13814        // describes a filter nobody could build is another.
13815        assert_eq!(
13816            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13817            "-Invalid header\r\n"
13818        );
13819        for (why, bad) in [
13820            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13821            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13822            (
13823                "a bucket count that is not a power of two",
13824                cf_header(0, 3, 0, 1, [2, 20, 1]),
13825            ),
13826            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13827            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13828            (
13829                "a growth nobody could reach",
13830                cf_header(0, 8, 0, 1, [2, 20, 32769]),
13831            ),
13832            (
13833                "a chain that cannot grow and did",
13834                cf_header(0, 8, 0, 2, [2, 20, 0]),
13835            ),
13836            // The count is written in eight bytes and read into two, so a
13837            // number that is a multiple of the second arrives as none.
13838            (
13839                "a filter count that wraps",
13840                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13841            ),
13842        ] {
13843            assert_eq!(
13844                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13845                "-Couldn't create filter!\r\n",
13846                "{why}"
13847            );
13848        }
13849    }
13850
13851    /// The RESP3 shapes, which are where this family differs most from RESP2
13852    /// and where one of its answers stops being readable.
13853    #[test]
13854    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13855        let mut f = Fixture::new();
13856        f.out.set_proto(Proto::Resp3);
13857        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13858        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13859        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13860        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13861        assert_eq!(
13862            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13863            "*2\r\n#t\r\n#f\r\n"
13864        );
13865        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13866        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13867        // The count stays an integer, because it counts rather than answers.
13868        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13869        assert_eq!(
13870            f.run(&[b"CF.INFO", b"c"]),
13871            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13872             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13873             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13874             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13875        );
13876
13877        // `CF.INSERT` writes a boolean per item here and an integer per item on
13878        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
13879        // client cannot tell an item that did not fit from one that is already
13880        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
13881        f.run(&[
13882            b"CF.RESERVE",
13883            b"s",
13884            b"4",
13885            b"BUCKETSIZE",
13886            b"1",
13887            b"EXPANSION",
13888            b"0",
13889        ]);
13890        assert_eq!(
13891            f.run(&[
13892                b"CF.INSERT",
13893                b"s",
13894                b"ITEMS",
13895                b"a",
13896                b"b",
13897                b"c",
13898                b"d",
13899                b"e",
13900                b"f"
13901            ]),
13902            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13903        );
13904        assert_eq!(
13905            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13906            "*2\r\n:0\r\n:-1\r\n"
13907        );
13908        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13909        // The end of a dump is a nil and not an empty chunk, which is one
13910        // underscore here and a negative length on RESP2.
13911        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13912    }
13913
13914    // ------------------------------------------------------------------- cms
13915
13916    /// A sketch is made from either end, and both constructors look at the key
13917    /// before they look at their arguments.
13918    #[test]
13919    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13920        let mut f = Fixture::new();
13921        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13922        assert_eq!(
13923            f.run(&[b"CMS.INFO", b"d"]),
13924            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13925        );
13926        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13927        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13928        // Two over the error rounded up, and the log of the probability over the
13929        // log of a half rounded up, which for these two is 200 by 6.
13930        assert_eq!(
13931            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13932            "+OK\r\n"
13933        );
13934        assert_eq!(
13935            f.run(&[b"CMS.INFO", b"p"]),
13936            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13937        );
13938        // The key is checked first, so a width of zero at a key that is already
13939        // there is about the key and not about the width.
13940        assert_eq!(
13941            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13942            "-CMS: key already exists\r\n"
13943        );
13944        assert_eq!(
13945            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13946            "-CMS: invalid width\r\n"
13947        );
13948        assert_eq!(
13949            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13950            "-CMS: invalid depth\r\n"
13951        );
13952        assert_eq!(
13953            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13954            "-CMS: invalid overestimation value\r\n"
13955        );
13956        assert_eq!(
13957            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13958            "-CMS: invalid prob value\r\n"
13959        );
13960        // A probability whose float conversion is zero has no depth, and a width
13961        // past a signed sixty four bit integer has no width, and both are the
13962        // same sentence.
13963        assert_eq!(
13964            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13965            "-CMS: invalid init arguments\r\n"
13966        );
13967        // And a sketch bigger than a gibibyte of counters is refused here where
13968        // the reference reserves address space nobody has touched, which is
13969        // D-47.
13970        assert_eq!(
13971            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13972            "-CMS: Insufficient memory to create the key\r\n"
13973        );
13974        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13975    }
13976
13977    /// Every pair is parsed before any of them lands, the counters saturate,
13978    /// and the count is a signed total of what was asked for.
13979    #[test]
13980    fn increments_are_parsed_whole_and_the_counters_saturate() {
13981        let mut f = Fixture::new();
13982        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13983        assert_eq!(
13984            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13985            "*2\r\n:3\r\n:4\r\n"
13986        );
13987        // An item that is incremented twice in one call sees its own first
13988        // increment in the reply to the second.
13989        assert_eq!(
13990            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13991            "*2\r\n:4\r\n:5\r\n"
13992        );
13993        // A bad number anywhere means nothing at all is applied.
13994        assert_eq!(
13995            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13996            "-CMS: Cannot parse number\r\n"
13997        );
13998        assert_eq!(
13999            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
14000            "-CMS: Number cannot be negative\r\n"
14001        );
14002        assert_eq!(
14003            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
14004            "*2\r\n:5\r\n:4\r\n"
14005        );
14006        // The counters stop at four billion and the item that stopped says so in
14007        // its own slot while the one beside it answers a number.
14008        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
14009        assert_eq!(
14010            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
14011            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
14012        );
14013        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
14014        // The count is what was asked for rather than what landed, and it is
14015        // signed, so a big enough total comes back negative.
14016        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
14017        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
14018        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
14019        assert_eq!(
14020            f.run(&[b"CMS.INFO", b"w"]),
14021            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
14022        );
14023        // An odd number of arguments after the key is an arity error and not a
14024        // syntax one.
14025        assert!(
14026            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
14027                .contains("wrong number of arguments")
14028        );
14029        assert_eq!(
14030            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
14031            "-CMS: key does not exist\r\n"
14032        );
14033        assert_eq!(
14034            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
14035            "-CMS: key does not exist\r\n"
14036        );
14037    }
14038
14039    /// A merge overwrites its destination, and it is worked out in full before
14040    /// any of it is written.
14041    #[test]
14042    fn a_merge_lands_whole_or_not_at_all() {
14043        let mut f = Fixture::new();
14044        for name in [&b"m1"[..], b"m2", b"dst"] {
14045            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
14046        }
14047        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
14048        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
14049        assert_eq!(
14050            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
14051            "+OK\r\n"
14052        );
14053        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
14054        // Overwritten and not added to, so the same merge twice is the same
14055        // answer twice.
14056        assert_eq!(
14057            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
14058            "+OK\r\n"
14059        );
14060        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
14061        assert_eq!(
14062            f.run(&[
14063                b"CMS.MERGE",
14064                b"dst",
14065                b"2",
14066                b"m1",
14067                b"m2",
14068                b"WEIGHTS",
14069                b"2",
14070                b"3"
14071            ]),
14072            "+OK\r\n"
14073        );
14074        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14075        // A cell times a weight is checked wide rather than wrapped, so this is
14076        // a refusal and the destination is left exactly as it was.
14077        assert_eq!(
14078            f.run(&[
14079                b"CMS.MERGE",
14080                b"dst",
14081                b"1",
14082                b"m1",
14083                b"WEIGHTS",
14084                b"4611686018427387904"
14085            ]),
14086            "-CMS: MERGE overflow\r\n"
14087        );
14088        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14089        // The destination comes first, then the count, then the layout, then the
14090        // weights, then the sources one at a time.
14091        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
14092        assert_eq!(
14093            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
14094            "-CMS: key does not exist\r\n"
14095        );
14096        assert_eq!(
14097            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
14098            "-CMS: Number of keys must be positive\r\n"
14099        );
14100        assert_eq!(
14101            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
14102            "-CMS: wrong number of keys\r\n"
14103        );
14104        assert_eq!(
14105            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
14106            "-CMS: wrong number of keys/weights\r\n"
14107        );
14108        assert_eq!(
14109            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
14110            "-CMS: width/depth is not equal\r\n"
14111        );
14112        assert_eq!(
14113            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
14114            "-CMS: key does not exist\r\n"
14115        );
14116    }
14117
14118    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
14119    /// a sketch is refused by the two commands that would have to serialise it.
14120    #[test]
14121    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14122        let mut f = Fixture::new();
14123        f.run(&[b"SET", b"s", b"text"]);
14124        for cmd in [
14125            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
14126            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
14127            vec![&b"CMS.QUERY"[..], b"s", b"a"],
14128            vec![&b"CMS.INFO"[..], b"s"],
14129            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
14130        ] {
14131            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14132            let reply = f.run(&cmd);
14133            // The two constructors see the key before anything else and say so
14134            // in the module's own words, and the rest are `WRONGTYPE`.
14135            assert!(
14136                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
14137                "{name}: {reply}"
14138            );
14139        }
14140        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
14141        // Redis refuses to copy a module key that has no copy callback, and
14142        // these are its words rather than ours. `DUMP` is the other half of
14143        // D-48: the reference has a payload for one of these and we do not.
14144        assert_eq!(
14145            f.run(&[b"COPY", b"c", b"c2"]),
14146            "-ERR not supported for this module key\r\n"
14147        );
14148        assert_eq!(
14149            f.run(&[b"DUMP", b"c"]),
14150            "-ERR DUMP is not supported for this module key\r\n"
14151        );
14152        // A graph is nobody's module and keeps its own sentence.
14153        f.run(&[b"G.NADD", b"g", b"a"]);
14154        assert_eq!(
14155            f.run(&[b"COPY", b"g", b"g2"]),
14156            "-ERR COPY is not supported for a graph\r\n"
14157        );
14158        assert_eq!(
14159            f.run(&[b"DUMP", b"g"]),
14160            "-ERR DUMP is not supported for a graph\r\n"
14161        );
14162        // Everything that does not need a byte shape works on a sketch key the
14163        // way it works on any other.
14164        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
14165        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
14166        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
14167        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
14168        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
14169    }
14170
14171    // ------------------------------------------------------------------ topk
14172
14173    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
14174    /// it looks at any of them.
14175    #[test]
14176    fn a_reserve_takes_three_arguments_or_six() {
14177        let mut f = Fixture::new();
14178        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
14179        assert_eq!(
14180            f.run(&[b"TOPK.INFO", b"t"]),
14181            "*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"
14182        );
14183        // Four arguments and five are an arity error rather than a defaulted
14184        // depth or decay.
14185        for cmd in [
14186            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
14187            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
14188        ] {
14189            assert!(f.run(&cmd).contains("wrong number of arguments"));
14190        }
14191        assert_eq!(
14192            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
14193            "+OK\r\n"
14194        );
14195        // The key is checked first, so a reserve with nothing else right at a
14196        // key that is taken still says the key is taken.
14197        assert_eq!(
14198            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
14199            "-TopK: key already exists\r\n"
14200        );
14201        assert_eq!(
14202            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
14203            "-TopK: invalid k\r\n"
14204        );
14205        assert_eq!(
14206            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
14207            "-TopK: invalid width\r\n"
14208        );
14209        assert_eq!(
14210            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
14211            "-TopK: invalid depth\r\n"
14212        );
14213        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
14214        assert_eq!(
14215            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
14216            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
14217        );
14218        assert_eq!(
14219            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
14220            "+OK\r\n"
14221        );
14222        // Past the cap, with the one sentence in the family that has a prefix.
14223        assert_eq!(
14224            f.run(&[
14225                b"TOPK.RESERVE",
14226                b"w",
14227                b"1",
14228                b"4294967295",
14229                b"4294967295",
14230                b"0.9"
14231            ]),
14232            "-ERR Insufficient memory to create topk data structure\r\n"
14233        );
14234    }
14235
14236    /// What the sketch keeps, and the three ways of asking about it.
14237    #[test]
14238    fn the_kept_set_is_what_query_and_list_answer_from() {
14239        let mut f = Fixture::new();
14240        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
14241        // A null an item while there is room, then the name of whatever was
14242        // pushed out.
14243        assert_eq!(
14244            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
14245            "*2\r\n$-1\r\n$-1\r\n"
14246        );
14247        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
14248        // Two slots are full and `c` arrives with a count of one, which is not
14249        // under the smallest kept count, so it takes that slot straight away.
14250        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
14251        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
14252        assert_eq!(
14253            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
14254            "*3\r\n:1\r\n:0\r\n:1\r\n"
14255        );
14256        // The table still counts what the kept set let go of.
14257        assert_eq!(
14258            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14259            "*3\r\n:11\r\n:1\r\n:6\r\n"
14260        );
14261        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
14262        assert_eq!(
14263            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
14264            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
14265        );
14266        // Any prefix of the keyword turns the counts on, the empty string
14267        // included, and only a longer word or a different one is refused.
14268        assert_eq!(
14269            f.run(&[b"TOPK.LIST", b"t", b"w"]),
14270            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14271        );
14272        assert_eq!(
14273            f.run(&[b"TOPK.LIST", b"t", b""]),
14274            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14275        );
14276        assert_eq!(
14277            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
14278            "-WITHCOUNT keyword expected\r\n"
14279        );
14280        // And the keyword is looked at before the key, so a missing key with a
14281        // bad keyword complains about the keyword.
14282        assert_eq!(
14283            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
14284            "-WITHCOUNT keyword expected\r\n"
14285        );
14286        assert_eq!(
14287            f.run(&[b"TOPK.LIST", b"missing"]),
14288            "-TopK: key does not exist\r\n"
14289        );
14290        // An item counted zero times is kept and not listed.
14291        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
14292        assert_eq!(
14293            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
14294            "*1\r\n$-1\r\n"
14295        );
14296        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
14297        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
14298    }
14299
14300    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
14301    /// before it counted, and the reply counts what it wrote.
14302    #[test]
14303    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
14304        let mut f = Fixture::new();
14305        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
14306        // Three pairs, the middle one bad: two elements come back, one of them
14307        // the error, and the array header says two rather than three. That last
14308        // part is D-51 and it is why a client here stays in step.
14309        assert_eq!(
14310            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
14311            format!(
14312                "*2\r\n$-1\r\n-{}\r\n",
14313                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
14314            )
14315        );
14316        assert_eq!(
14317            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14318            "*3\r\n:3\r\n:0\r\n:0\r\n"
14319        );
14320        // A hundred thousand is in and one more is out.
14321        assert_eq!(
14322            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
14323            "*1\r\n$-1\r\n"
14324        );
14325        assert!(
14326            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
14327                .contains("smaller or equal to 100,000")
14328        );
14329        // Pairs have to be pairs.
14330        assert!(
14331            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
14332                .contains("wrong number of arguments")
14333        );
14334        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
14335    }
14336
14337    /// The RESP3 shapes, which are the two the protocols disagree about.
14338    #[test]
14339    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
14340        let mut f = Fixture::new();
14341        f.run(&[b"HELLO", b"3"]);
14342        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
14343        f.run(&[b"TOPK.ADD", b"t", b"a"]);
14344        assert_eq!(
14345            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
14346            "*2\r\n#t\r\n#f\r\n"
14347        );
14348        // The count stays an integer on both protocols.
14349        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
14350        assert_eq!(
14351            f.run(&[b"TOPK.INFO", b"t"]),
14352            "%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"
14353        );
14354        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
14355    }
14356
14357    /// A top k key answers the module sentences the other sketch families
14358    /// answer, and its own word for its type.
14359    #[test]
14360    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14361        let mut f = Fixture::new();
14362        f.run(&[b"SET", b"s", b"text"]);
14363        for cmd in [
14364            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
14365            vec![&b"TOPK.ADD"[..], b"s", b"a"],
14366            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
14367            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
14368            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
14369            vec![&b"TOPK.LIST"[..], b"s"],
14370            vec![&b"TOPK.INFO"[..], b"s"],
14371        ] {
14372            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14373            let reply = f.run(&cmd);
14374            assert!(
14375                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
14376                "{name}: {reply}"
14377            );
14378        }
14379        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
14380        assert_eq!(
14381            f.run(&[b"COPY", b"t", b"t2"]),
14382            "-ERR not supported for this module key\r\n"
14383        );
14384        assert_eq!(
14385            f.run(&[b"DUMP", b"t"]),
14386            "-ERR DUMP is not supported for this module key\r\n"
14387        );
14388        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14389        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14390        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14391        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
14392        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14393        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14394        // Every one of the six that is not the constructor says the same thing
14395        // about a key that is not there.
14396        assert_eq!(
14397            f.run(&[b"TOPK.INFO", b"t3"]),
14398            "-TopK: key does not exist\r\n"
14399        );
14400    }
14401
14402    // --------------------------------------------------------------- tdigest
14403
14404    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
14405    /// search rather than a lookup.
14406    #[test]
14407    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
14408        let mut f = Fixture::new();
14409        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
14410        // A hundred is the default and the capacity is six times it plus ten.
14411        assert_eq!(
14412            f.run(&[b"TDIGEST.INFO", b"t"]),
14413            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
14414             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
14415             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
14416        );
14417        assert_eq!(
14418            f.run(&[b"TDIGEST.CREATE", b"t"]),
14419            "-ERR T-Digest: key already exists\r\n"
14420        );
14421        // Three arguments is an arity error and not a missing keyword.
14422        assert!(
14423            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
14424                .contains("wrong number of arguments")
14425        );
14426        assert_eq!(
14427            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
14428            "+OK\r\n"
14429        );
14430        assert_eq!(
14431            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
14432            "+OK\r\n"
14433        );
14434        // The word is looked for across both trailing arguments and the number
14435        // is then read out of the last one whatever was found, so this looks for
14436        // a number inside the word `COMPRESSION` and does not find one.
14437        assert_eq!(
14438            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
14439            "-ERR T-Digest: error parsing compression parameter\r\n"
14440        );
14441        assert_eq!(
14442            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
14443            "-ERR T-Digest: wrong keyword\r\n"
14444        );
14445        assert_eq!(
14446            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
14447            "-ERR T-Digest: error parsing compression parameter\r\n"
14448        );
14449        assert_eq!(
14450            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
14451            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
14452        );
14453        // The reference's own ceiling, which is where the capacity stops fitting
14454        // in an int, and one past it.
14455        assert_eq!(
14456            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
14457            "-ERR T-Digest: allocation failed\r\n"
14458        );
14459        // And ours, which is a gibibyte of centroids and is D-52.
14460        assert_eq!(
14461            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
14462            "-ERR T-Digest: allocation failed\r\n"
14463        );
14464        // The key is checked before the arguments, so a bad compression at a key
14465        // that is already a digest still says the key is taken.
14466        assert_eq!(
14467            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
14468            "-ERR T-Digest: key already exists\r\n"
14469        );
14470    }
14471
14472    /// The four samples every note about this family is written against, and the
14473    /// answers a real 8.10.1 gives for them.
14474    #[test]
14475    fn the_quantile_family_answers_what_the_module_answers() {
14476        let mut f = Fixture::new();
14477        f.run(&[b"TDIGEST.CREATE", b"s"]);
14478        assert_eq!(
14479            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
14480            "+OK\r\n"
14481        );
14482        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14483        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14484        // The cdf of a sample is the weight below it plus half its own.
14485        assert_eq!(
14486            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14487            "*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"
14488        );
14489        assert_eq!(
14490            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14491            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14492        );
14493        // Out of order, the walk restarts, and 0.5 answers 3 either way while
14494        // the two after it are read from the front again.
14495        assert_eq!(
14496            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14497            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14498        );
14499        assert_eq!(
14500            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14501            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14502        );
14503        assert_eq!(
14504            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14505            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14506        );
14507        assert_eq!(
14508            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14509            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14510        );
14511        assert_eq!(
14512            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14513            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14514        );
14515        assert_eq!(
14516            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14517            "$3\r\n2.5\r\n"
14518        );
14519        assert_eq!(
14520            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14521            "$3\r\n2.5\r\n"
14522        );
14523        // The ranges, which are separate sentences from the parse failures.
14524        assert_eq!(
14525            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14526            "-ERR T-Digest: quantile should be in [0,1]\r\n"
14527        );
14528        assert_eq!(
14529            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14530            "-ERR T-Digest: error parsing quantile\r\n"
14531        );
14532        assert_eq!(
14533            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14534            "-ERR T-Digest: error parsing cdf\r\n"
14535        );
14536        assert_eq!(
14537            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14538            "-ERR T-Digest: error parsing value\r\n"
14539        );
14540        assert_eq!(
14541            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14542            "-ERR T-Digest: rank needs to be non negative\r\n"
14543        );
14544        assert_eq!(
14545            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14546            "-ERR T-Digest: error parsing rank\r\n"
14547        );
14548        // Both cuts have their own parse sentence and share the range one, and
14549        // equal cuts are refused rather than answering nothing.
14550        assert_eq!(
14551            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14552            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14553        );
14554        assert_eq!(
14555            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14556            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14557        );
14558        assert_eq!(
14559            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14560            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14561        );
14562        assert_eq!(
14563            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14564            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14565        );
14566    }
14567
14568    /// An empty digest answers every question, and answers most of them with
14569    /// something that is not a number.
14570    #[test]
14571    fn an_empty_digest_has_an_answer_for_everything() {
14572        let mut f = Fixture::new();
14573        f.run(&[b"TDIGEST.CREATE", b"e"]);
14574        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14575        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14576        assert_eq!(
14577            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14578            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14579        );
14580        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14581        assert_eq!(
14582            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14583            "$3\r\nnan\r\n"
14584        );
14585        // Minus two, which is a number no rank on a digest with samples in it
14586        // can ever be.
14587        assert_eq!(
14588            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14589            "*2\r\n:-2\r\n:-2\r\n"
14590        );
14591        assert_eq!(
14592            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14593            "*2\r\n:-2\r\n:-2\r\n"
14594        );
14595        assert_eq!(
14596            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14597            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14598        );
14599        // A reset puts a digest with samples back into exactly this state.
14600        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14601        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14602        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14603        // Down to the compression count, so a reset digest and a fresh one of
14604        // the same compression report the same nine numbers.
14605        f.run(&[b"TDIGEST.CREATE", b"e2"]);
14606        assert_eq!(
14607            f.run(&[b"TDIGEST.INFO", b"e"]),
14608            f.run(&[b"TDIGEST.INFO", b"e2"])
14609        );
14610    }
14611
14612    /// The double parser is Redis's and not this engine's, and the two disagree
14613    /// at both ends of the range.
14614    #[test]
14615    fn a_sample_is_read_the_way_redis_reads_a_double() {
14616        let mut f = Fixture::new();
14617        f.run(&[b"TDIGEST.CREATE", b"a"]);
14618        // Overflow and underflow are parse failures rather than an infinity and
14619        // a zero, which is where this parts company with the rest of the engine.
14620        for bad in [
14621            &b"nan"[..],
14622            b"1e400",
14623            b"-1e400",
14624            b"1e309",
14625            b"1e-400",
14626            b"",
14627            b" 1",
14628            b"1 ",
14629            b"1e",
14630            b"--1",
14631        ] {
14632            assert_eq!(
14633                f.run(&[b"TDIGEST.ADD", b"a", bad]),
14634                "-ERR T-Digest: error parsing val parameter\r\n",
14635                "{}",
14636                String::from_utf8_lossy(bad)
14637            );
14638        }
14639        // An infinity spelled out parses and is then refused for being one, with
14640        // a different sentence.
14641        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14642            assert_eq!(
14643                f.run(&[b"TDIGEST.ADD", b"a", word]),
14644                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14645                "{}",
14646                String::from_utf8_lossy(word)
14647            );
14648        }
14649        // These all parse: hex, a bare point either side, and the smallest
14650        // subnormal the reference will take.
14651        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14652            assert_eq!(
14653                f.run(&[b"TDIGEST.ADD", b"a", good]),
14654                "+OK\r\n",
14655                "{}",
14656                String::from_utf8_lossy(good)
14657            );
14658        }
14659        // Nothing landed from the failures, so six samples is what there is.
14660        assert!(
14661            f.run(&[b"TDIGEST.INFO", b"a"])
14662                .contains("Observations\r\n:6\r\n")
14663        );
14664        // Every value is parsed before any is added, so this whole command is a
14665        // no op.
14666        assert_eq!(
14667            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14668            "-ERR T-Digest: error parsing val parameter\r\n"
14669        );
14670        assert!(
14671            f.run(&[b"TDIGEST.INFO", b"a"])
14672                .contains("Observations\r\n:6\r\n")
14673        );
14674    }
14675
14676    /// What a merge does to its destination, to its inputs and to the buffer
14677    /// split `TDIGEST.INFO` reports.
14678    #[test]
14679    fn a_merge_sweeps_the_destination_between_its_inputs() {
14680        let mut f = Fixture::new();
14681        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14682        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14683        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14684        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14685        assert_eq!(
14686            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14687            "+OK\r\n"
14688        );
14689        // The destination did not exist, so the compression is the largest of
14690        // the inputs. The three from the first input were swept in before the
14691        // three from the second arrived, which is the one visible effect of the
14692        // reference folding one input at a time.
14693        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14694        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14695        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14696        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14697        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14698        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14699        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14700        // Reading a source sweeps it too, so a merge writes to keys it only
14701        // reads from.
14702        assert!(
14703            f.run(&[b"TDIGEST.INFO", b"m1"])
14704                .contains("Merged nodes\r\n:3\r\n")
14705        );
14706        // Without OVERRIDE the destination joins its own inputs, so this takes
14707        // it to nine observations and keeps its own compression.
14708        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14709        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14710        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14711        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14712        // With OVERRIDE the old destination is dropped and the compression goes
14713        // back to the largest of the inputs.
14714        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14715        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14716        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14717        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14718        // And COMPRESSION beats both.
14719        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14720        assert!(
14721            f.run(&[b"TDIGEST.INFO", b"d"])
14722                .contains("Compression\r\n:500\r\n")
14723        );
14724        // Naming the destination as a source folds it in twice.
14725        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14726        assert!(
14727            f.run(&[b"TDIGEST.INFO", b"d"])
14728                .contains("Observations\r\n:12\r\n")
14729        );
14730        // The arguments, in the order the reference checks them.
14731        assert_eq!(
14732            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14733            "-ERR T-Digest: error parsing numkeys\r\n"
14734        );
14735        assert_eq!(
14736            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14737            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14738        );
14739        assert!(
14740            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14741                .contains("wrong number of arguments")
14742        );
14743        assert!(
14744            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14745                .contains("wrong number of arguments")
14746        );
14747        assert_eq!(
14748            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14749            "-ERR T-Digest: wrong keyword\r\n"
14750        );
14751        // A source that is not there stops the whole thing, and the destination
14752        // is left as it was.
14753        assert_eq!(
14754            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14755            "-ERR T-Digest: key does not exist\r\n"
14756        );
14757        assert!(
14758            f.run(&[b"TDIGEST.INFO", b"d"])
14759                .contains("Observations\r\n:12\r\n")
14760        );
14761        // A destination that is not there and is also named as a source is the
14762        // same sentence rather than an empty merge.
14763        assert_eq!(
14764            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14765            "-ERR T-Digest: key does not exist\r\n"
14766        );
14767    }
14768
14769    /// The RESP3 shapes, which are the two the protocols disagree about.
14770    #[test]
14771    fn a_digest_answers_doubles_and_a_map_on_resp3() {
14772        let mut f = Fixture::new();
14773        f.run(&[b"HELLO", b"3"]);
14774        f.run(&[b"TDIGEST.CREATE", b"s"]);
14775        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14776        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14777        assert_eq!(
14778            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14779            "*2\r\n,1\r\n,4\r\n"
14780        );
14781        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14782        // The two infinities and the NaN go out as the bare words.
14783        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14784        assert_eq!(
14785            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14786            "*1\r\n,-inf\r\n"
14787        );
14788        f.run(&[b"TDIGEST.CREATE", b"e"]);
14789        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14790        // The ranks stay integers on both protocols.
14791        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14792        // Every question above swept the buffer in, so the four samples are all
14793        // merged by now and the compression count says it happened once.
14794        assert_eq!(
14795            f.run(&[b"TDIGEST.INFO", b"s"]),
14796            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14797             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14798             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14799        );
14800    }
14801
14802    /// A t digest key answers the module sentences the other sketch families
14803    /// answer, and its own word for its type.
14804    #[test]
14805    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14806        let mut f = Fixture::new();
14807        f.run(&[b"SET", b"s", b"text"]);
14808        for cmd in [
14809            vec![&b"TDIGEST.CREATE"[..], b"s"],
14810            vec![&b"TDIGEST.RESET"[..], b"s"],
14811            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14812            vec![&b"TDIGEST.MIN"[..], b"s"],
14813            vec![&b"TDIGEST.MAX"[..], b"s"],
14814            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14815            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14816            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14817            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14818            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14819            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14820            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14821            vec![&b"TDIGEST.INFO"[..], b"s"],
14822        ] {
14823            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14824            let reply = f.run(&cmd);
14825            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14826        }
14827        // The merge checks its destination the same way, and its sources too.
14828        f.run(&[b"TDIGEST.CREATE", b"t"]);
14829        assert!(
14830            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14831                .starts_with("-WRONGTYPE")
14832        );
14833        assert!(
14834            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14835                .starts_with("-WRONGTYPE")
14836        );
14837        assert_eq!(
14838            f.run(&[b"COPY", b"t", b"t2"]),
14839            "-ERR not supported for this module key\r\n"
14840        );
14841        assert_eq!(
14842            f.run(&[b"DUMP", b"t"]),
14843            "-ERR DUMP is not supported for this module key\r\n"
14844        );
14845        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14846        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14847        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14848        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14849        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14850        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14851        // An empty digest is still a key, so the twelve that are not the
14852        // constructor all say the same thing once it is gone.
14853        assert_eq!(
14854            f.run(&[b"TDIGEST.INFO", b"t3"]),
14855            "-ERR T-Digest: key does not exist\r\n"
14856        );
14857        // The key is looked at before the arguments, so a bad argument at a key
14858        // that is not there still says the key is not there.
14859        assert_eq!(
14860            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14861            "-ERR T-Digest: key does not exist\r\n"
14862        );
14863    }
14864
14865    // -------------------------------------------------------------------- ts
14866
14867    /// A `TS.INFO` reply with the memory usage taken out of it.
14868    ///
14869    /// That number is what a series costs here rather than what one costs in the
14870    /// module, which is D-53, and it moves whenever the layout of a chunk does.
14871    /// Everything either side of it is the wire contract and is worth pinning
14872    /// down exactly, so the tests below check the whole reply with the one
14873    /// number lifted out.
14874    fn without_memory(reply: &str) -> String {
14875        let head = "+memoryUsage\r\n:";
14876        let at = reply.find(head).expect("every TS.INFO reports memory");
14877        let rest = &reply[at + head.len()..];
14878        let end = rest.find("\r\n").expect("and it is a whole number");
14879        format!("{}{}", &reply[..at + head.len()], &rest[end..])
14880    }
14881
14882    /// A series is made empty and still says it has a chunk, and the options are
14883    /// read before the key is looked at.
14884    #[test]
14885    fn a_series_is_made_empty_and_reports_on_itself() {
14886        let mut f = Fixture::new();
14887        assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14888        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14889        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14890        // Fourteen fields, so twenty eight elements. An empty series reports one
14891        // chunk and zero at both ends, and neither the chunk type nor the
14892        // duplicate policy is ever a nil.
14893        assert_eq!(
14894            without_memory(&f.run(&[b"TS.INFO", b"t"])),
14895            "*28\r\n\
14896             +totalSamples\r\n:0\r\n\
14897             +memoryUsage\r\n:\r\n\
14898             +firstTimestamp\r\n:0\r\n\
14899             +lastTimestamp\r\n:0\r\n\
14900             +retentionTime\r\n:0\r\n\
14901             +chunkCount\r\n:1\r\n\
14902             +chunkSize\r\n:4096\r\n\
14903             +chunkType\r\n+compressed\r\n\
14904             +duplicatePolicy\r\n+block\r\n\
14905             +labels\r\n*0\r\n\
14906             +sourceKey\r\n$-1\r\n\
14907             +rules\r\n*0\r\n\
14908             +ignoreMaxTimeDiff\r\n:0\r\n\
14909             +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14910        );
14911        // A key that is already there is about the key whatever it holds, and
14912        // the existence is what is checked rather than the type.
14913        assert_eq!(
14914            f.run(&[b"TS.CREATE", b"t"]),
14915            "-ERR TSDB: key already exists\r\n"
14916        );
14917        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14918        assert_eq!(
14919            f.run(&[b"TS.CREATE", b"str"]),
14920            "-ERR TSDB: key already exists\r\n"
14921        );
14922        // But the arguments are read first, so a bad one at a key that is there
14923        // answers about the argument.
14924        assert_eq!(
14925            f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14926            "-ERR TSDB: Couldn't parse RETENTION\r\n"
14927        );
14928        // The seven that will not make a series say WRONGTYPE about a key
14929        // holding something else, where the two that would say a sentence.
14930        // The word is inside the sentence and not in front of it, because the
14931        // module writes its own error text and Redis puts ERR on the front of
14932        // anything a module writes.
14933        let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14934        assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14935        assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14936        assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14937        assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14938        assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14939        assert_eq!(
14940            f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14941            "-ERR TSDB: the key is not a TSDB key\r\n"
14942        );
14943        // And the ones that will not make one say so about a key that is gone.
14944        assert_eq!(
14945            f.run(&[b"TS.INFO", b"nope"]),
14946            "-ERR TSDB: the key does not exist\r\n"
14947        );
14948        assert_eq!(
14949            f.run(&[b"TS.GET", b"nope"]),
14950            "-ERR TSDB: the key does not exist\r\n"
14951        );
14952        assert_eq!(
14953            f.run(&[b"TS.ALTER", b"nope"]),
14954            "-ERR TSDB: the key does not exist\r\n"
14955        );
14956        assert_eq!(
14957            f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14958            "-ERR TSDB: the key does not exist\r\n"
14959        );
14960    }
14961
14962    /// Every option word, including the ones that are wrong, and the scan that
14963    /// finds them.
14964    #[test]
14965    fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14966        let mut f = Fixture::new();
14967        assert_eq!(
14968            f.run(&[
14969                b"TS.CREATE",
14970                b"t",
14971                b"RETENTION",
14972                b"5000",
14973                b"ENCODING",
14974                b"UNCOMPRESSED",
14975                b"CHUNK_SIZE",
14976                b"128",
14977                b"DUPLICATE_POLICY",
14978                b"LAST",
14979                b"IGNORE",
14980                b"10",
14981                b"0.5",
14982                b"LABELS",
14983                b"room",
14984                b"kitchen"
14985            ]),
14986            "+OK\r\n"
14987        );
14988        let info = f.run(&[b"TS.INFO", b"t"]);
14989        assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14990        assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14991        assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14992        assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14993        assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14994        // A plain double here, where a sample value out of TS.GET is the
14995        // shortest digits that read back as the same number.
14996        assert!(
14997            info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14998            "{info}"
14999        );
15000        assert!(
15001            info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
15002            "{info}"
15003        );
15004
15005        // A word that is not an option is read past rather than refused.
15006        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
15007        // LABELS eats everything after it in pairs, and the later scans still
15008        // look inside what it ate, so this sets a retention and stores a label
15009        // called RETENTION at the same time.
15010        assert_eq!(
15011            f.run(&[
15012                b"TS.CREATE",
15013                b"g",
15014                b"LABELS",
15015                b"a",
15016                b"b",
15017                b"RETENTION",
15018                b"5"
15019            ]),
15020            "+OK\r\n"
15021        );
15022        let greedy = f.run(&[b"TS.INFO", b"g"]);
15023        assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
15024        assert!(
15025            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"),
15026            "{greedy}"
15027        );
15028
15029        // Every way an option can be wrong, in the order the module reads them.
15030        assert_eq!(
15031            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
15032            "-ERR TSDB: Couldn't parse LABELS\r\n"
15033        );
15034        assert_eq!(
15035            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
15036            "-ERR TSDB: Couldn't parse LABELS\r\n"
15037        );
15038        assert_eq!(
15039            f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
15040            "-ERR TSDB: Couldn't parse RETENTION\r\n"
15041        );
15042        // A retention below zero is one of the two the module writes with no
15043        // ERR in front of it, where one that is not a number gets one.
15044        assert_eq!(
15045            f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
15046            "-TSDB: Couldn't parse RETENTION\r\n"
15047        );
15048        assert_eq!(
15049            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
15050            "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
15051        );
15052        assert_eq!(
15053            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
15054            "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
15055        );
15056        assert_eq!(
15057            f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
15058            "-ERR TSDB: unknown ENCODING parameter\r\n"
15059        );
15060        // And an ENCODING with nothing behind it is an arity error where every
15061        // other keyword in the same spot is a sentence.
15062        assert!(
15063            f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
15064                .contains("wrong number of arguments for 'ts.create' command")
15065        );
15066        assert_eq!(
15067            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
15068            "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
15069        );
15070        assert_eq!(
15071            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
15072            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15073        );
15074        assert_eq!(
15075            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
15076            "-ERR TSDB: Couldn't parse IGNORE\r\n"
15077        );
15078        assert_eq!(
15079            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
15080            "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
15081        );
15082        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
15083
15084        // An alter changes what was named and leaves the rest alone, and reads
15085        // an encoding only far enough to refuse a bad one.
15086        assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
15087        let after = f.run(&[b"TS.INFO", b"t"]);
15088        assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
15089        assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
15090        assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
15091        assert_eq!(
15092            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
15093            "-ERR TSDB: unknown ENCODING parameter\r\n"
15094        );
15095        // An encoding it does take is still not applied.
15096        assert_eq!(
15097            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
15098            "+OK\r\n"
15099        );
15100        assert!(
15101            f.run(&[b"TS.INFO", b"t"])
15102                .contains("+chunkType\r\n+uncompressed\r\n")
15103        );
15104    }
15105
15106    /// Samples go in, come back out and are refused for the reasons the module
15107    /// refuses them.
15108    #[test]
15109    fn samples_land_where_they_are_put_and_the_newest_comes_back() {
15110        let mut f = Fixture::new();
15111        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
15112        // The series was made on the way in.
15113        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
15114        assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
15115        // A sample value goes out as a simple string of the shortest digits
15116        // that read back as the same number.
15117        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
15118        assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
15119        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
15120        // An empty series has no newest sample and answers an empty array
15121        // rather than a nil.
15122        assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
15123        assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
15124
15125        // The value is read before the key, so a bad one against a key holding
15126        // a string is about the value.
15127        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15128        assert_eq!(
15129            f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
15130            "-ERR TSDB: invalid value\r\n"
15131        );
15132        // The grammar is tighter than the one a number argument usually gets:
15133        // no leading plus, no bare fraction, no infinity and nothing that does
15134        // not fit.
15135        for bad in [
15136            &b".5"[..],
15137            b"1.",
15138            b"+1",
15139            b" 1",
15140            b"0x10",
15141            b"inf",
15142            b"1e400",
15143            b"--1",
15144            b"1e",
15145        ] {
15146            assert_eq!(
15147                f.run(&[b"TS.ADD", b"v", b"1", bad]),
15148                "-ERR TSDB: invalid value\r\n",
15149                "{}",
15150                String::from_utf8_lossy(bad)
15151            );
15152        }
15153        // And a reading that is not a number is one of three words.
15154        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
15155
15156        // A timestamp that is not a number, and one that is and is below zero,
15157        // are two different sentences.
15158        assert_eq!(
15159            f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
15160            "-ERR TSDB: invalid timestamp\r\n"
15161        );
15162        assert_eq!(
15163            f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
15164            "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
15165        );
15166
15167        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
15168        // command beats what the series was told.
15169        assert_eq!(
15170            f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
15171            "-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"
15172        );
15173        assert_eq!(
15174            f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
15175            ":300\r\n"
15176        );
15177        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
15178        // ON_DUPLICATE is only read when the key was already there, which is
15179        // why a policy word that is not a policy passes on a fresh key.
15180        assert_eq!(
15181            f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
15182            ":1\r\n"
15183        );
15184        assert_eq!(
15185            f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
15186            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15187        );
15188
15189        // Retention is exact and it is checked before anything else happens, so
15190        // a sample landing behind the window is refused rather than trimmed.
15191        assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
15192        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
15193        assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
15194        assert_eq!(
15195            f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
15196            "-ERR TSDB: Timestamp is older than retention\r\n"
15197        );
15198        // And the window trims as it moves.
15199        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
15200        assert!(
15201            f.run(&[b"TS.INFO", b"r"])
15202                .contains("+totalSamples\r\n:1\r\n")
15203        );
15204
15205        // An ignore window drops a sample close enough to the newest one to be
15206        // uninteresting, and answers the newest timestamp so a client can tell.
15207        assert_eq!(
15208            f.run(&[
15209                b"TS.CREATE",
15210                b"i",
15211                b"DUPLICATE_POLICY",
15212                b"LAST",
15213                b"IGNORE",
15214                b"10",
15215                b"0.5"
15216            ]),
15217            "+OK\r\n"
15218        );
15219        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
15220        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
15221        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
15222    }
15223
15224    /// Every triple in a `TS.MADD` is answered on its own, and none of them
15225    /// makes a series.
15226    #[test]
15227    fn a_madd_answers_each_triple_and_creates_nothing() {
15228        let mut f = Fixture::new();
15229        assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
15230        assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
15231        assert_eq!(
15232            f.run(&[
15233                b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
15234            ]),
15235            "*3\r\n:100\r\n:100\r\n:200\r\n"
15236        );
15237        // A key that is not a series is an error in its own slot and the ones
15238        // after it still land.
15239        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15240        assert_eq!(
15241            f.run(&[
15242                b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
15243            ]),
15244            "*3\r\n\
15245             -ERR TSDB: the key is not a TSDB key\r\n\
15246             -ERR TSDB: the key is not a TSDB key\r\n\
15247             :300\r\n"
15248        );
15249        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
15250        // A bad value and a bad timestamp are answered in their slots too.
15251        assert_eq!(
15252            f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
15253            "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
15254        );
15255        // And a list that is not made of triples is an arity error.
15256        assert!(
15257            f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
15258                .contains("wrong number of arguments for 'ts.madd' command")
15259        );
15260    }
15261
15262    /// The two increments, which only ever write forwards.
15263    #[test]
15264    fn an_increment_walks_the_newest_value_up_and_down() {
15265        let mut f = Fixture::new();
15266        assert_eq!(
15267            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15268            ":100\r\n"
15269        );
15270        assert_eq!(
15271            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15272            ":100\r\n"
15273        );
15274        // Two on one timestamp add up rather than collide, because the sample
15275        // goes in under the last policy whatever the series says.
15276        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
15277        assert_eq!(
15278            f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
15279            ":200\r\n"
15280        );
15281        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
15282        // A timestamp behind the newest sample is the other of the two errors
15283        // the module writes with no ERR in front of it.
15284        assert_eq!(
15285            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
15286            "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
15287        );
15288        // The increment goes through the ordinary number reader, so it takes
15289        // what a sample value will not and refuses a NaN that a sample value
15290        // takes.
15291        assert_eq!(
15292            f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
15293            ":1\r\n"
15294        );
15295        assert_eq!(
15296            f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
15297            ":1\r\n"
15298        );
15299        assert_eq!(
15300            f.run(&[b"TS.INCRBY", b"t", b"nan"]),
15301            "-ERR TSDB: invalid increase/decrease value\r\n"
15302        );
15303        assert_eq!(
15304            f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
15305            "-ERR TSDB: invalid increase/decrease value\r\n"
15306        );
15307        // A key holding something else is WRONGTYPE and is answered before the
15308        // number is looked at.
15309        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15310        assert_eq!(
15311            f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
15312            "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15313        );
15314        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
15315        // The reference reads one past the end of its own arguments here and
15316        // answers whatever was in that memory, so there is nothing to copy and
15317        // this answers the same thing every time.
15318        assert_eq!(
15319            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
15320            "-ERR TSDB: invalid timestamp\r\n"
15321        );
15322        // And one behind a LABELS is a label name rather than the keyword, so
15323        // this lands at the clock rather than at 5.
15324        assert_eq!(
15325            f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
15326            format!(":{}\r\n", f.server.now_ms())
15327        );
15328        // Adding to a series whose newest value is not a number has no answer.
15329        assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
15330        assert_eq!(
15331            f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
15332            "-ERR TSDB: cannot increment/decrement NaN value\r\n"
15333        );
15334    }
15335
15336    /// Deleting a span, both ends included.
15337    #[test]
15338    fn deleting_takes_out_a_span_and_answers_how_many_went() {
15339        let mut f = Fixture::new();
15340        for at in [b"100".as_slice(), b"200", b"300", b"400"] {
15341            f.run(&[b"TS.ADD", b"t", at, b"1"]);
15342        }
15343        assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
15344        assert!(
15345            f.run(&[b"TS.INFO", b"t"])
15346                .contains("+totalSamples\r\n:2\r\n")
15347        );
15348        // Ends the wrong way round take nothing out rather than being an error.
15349        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
15350        // The two open ends.
15351        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
15352        // A series everything has been deleted from keeps its chunk and reports
15353        // zero at both ends again.
15354        let empty = f.run(&[b"TS.INFO", b"t"]);
15355        assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
15356        assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
15357        assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
15358        assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
15359        assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
15360        // The two ends have their own sentences.
15361        assert_eq!(
15362            f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
15363            "-ERR TSDB: wrong fromTimestamp\r\n"
15364        );
15365        assert_eq!(
15366            f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
15367            "-ERR TSDB: wrong toTimestamp\r\n"
15368        );
15369        assert_eq!(
15370            f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
15371            "-ERR TSDB: wrong fromTimestamp\r\n"
15372        );
15373    }
15374
15375    /// What RESP3 changes, which is the two places a number is written and the
15376    /// shape of `TS.INFO`.
15377    #[test]
15378    fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
15379        let mut f = Fixture::new();
15380        f.out = Out::new(Proto::Resp3);
15381        assert_eq!(
15382            f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
15383            "+OK\r\n"
15384        );
15385        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
15386        // A double rather than the simple string RESP2 gets.
15387        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
15388        assert_eq!(
15389            without_memory(&f.run(&[b"TS.INFO", b"t"])),
15390            "%14\r\n\
15391             +totalSamples\r\n:1\r\n\
15392             +memoryUsage\r\n:\r\n\
15393             +firstTimestamp\r\n:100\r\n\
15394             +lastTimestamp\r\n:100\r\n\
15395             +retentionTime\r\n:0\r\n\
15396             +chunkCount\r\n:1\r\n\
15397             +chunkSize\r\n:4096\r\n\
15398             +chunkType\r\n+compressed\r\n\
15399             +duplicatePolicy\r\n+block\r\n\
15400             +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
15401             +sourceKey\r\n_\r\n\
15402             +rules\r\n%0\r\n\
15403             +ignoreMaxTimeDiff\r\n:0\r\n\
15404             +ignoreMaxValDiff\r\n,0\r\n"
15405        );
15406    }
15407
15408    /// Reading a span back, both ways round, with the two ends and the three
15409    /// things that trim what comes out.
15410    #[test]
15411    fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
15412        let mut f = Fixture::new();
15413        for (at, v) in [
15414            (b"100".as_slice(), b"1".as_slice()),
15415            (b"200", b"2"),
15416            (b"300", b"3"),
15417            (b"400", b"4"),
15418        ] {
15419            f.run(&[b"TS.ADD", b"t", at, v]);
15420        }
15421        assert_eq!(
15422            f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
15423            "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
15424             *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
15425        );
15426        // Both ends are included.
15427        assert_eq!(
15428            f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
15429            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15430        );
15431        // Backwards, and the count takes from the front of what comes out, so
15432        // backwards it takes the newest.
15433        assert_eq!(
15434            f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
15435            "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
15436        );
15437        // Ends the wrong way round are empty rather than an error.
15438        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
15439        // The two filters.
15440        assert_eq!(
15441            f.run(&[
15442                b"TS.RANGE",
15443                b"t",
15444                b"-",
15445                b"+",
15446                b"FILTER_BY_VALUE",
15447                b"2",
15448                b"3"
15449            ]),
15450            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15451        );
15452        assert_eq!(
15453            f.run(&[
15454                b"TS.RANGE",
15455                b"t",
15456                b"-",
15457                b"+",
15458                b"FILTER_BY_TS",
15459                b"100",
15460                b"400"
15461            ]),
15462            "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
15463        );
15464        // A word that is not an option is ignored wherever it sits.
15465        assert_eq!(
15466            f.run(&[
15467                b"TS.RANGE",
15468                b"t",
15469                b"-",
15470                b"+",
15471                b"ZZZ",
15472                b"FILTER_BY_TS",
15473                b"400"
15474            ]),
15475            "*1\r\n*2\r\n:400\r\n+4\r\n"
15476        );
15477        // `LATEST` means nothing until there is a compaction rule to follow.
15478        assert_eq!(
15479            f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
15480            "*1\r\n*2\r\n:100\r\n+1\r\n"
15481        );
15482    }
15483
15484    /// The bucketing, which is one column a reduction and a flat row.
15485    #[test]
15486    fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15487        let mut f = Fixture::new();
15488        for (at, v) in [
15489            (b"100".as_slice(), b"1".as_slice()),
15490            (b"200", b"2"),
15491            (b"300", b"3"),
15492            (b"400", b"4"),
15493        ] {
15494            f.run(&[b"TS.ADD", b"t", at, v]);
15495        }
15496        assert_eq!(
15497            f.run(&[
15498                b"TS.RANGE",
15499                b"t",
15500                b"-",
15501                b"+",
15502                b"AGGREGATION",
15503                b"avg",
15504                b"200"
15505            ]),
15506            "*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"
15507        );
15508        // Three reductions is a row of four and not a row of two with a nested
15509        // three in it.
15510        assert_eq!(
15511            f.run(&[
15512                b"TS.RANGE",
15513                b"t",
15514                b"-",
15515                b"+",
15516                b"AGGREGATION",
15517                b"min,max,count",
15518                b"200"
15519            ]),
15520            "*3\r\n\
15521             *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15522             *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15523             *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15524        );
15525        // The timestamp a bucket is reported under.
15526        assert_eq!(
15527            f.run(&[
15528                b"TS.RANGE",
15529                b"t",
15530                b"-",
15531                b"+",
15532                b"AGGREGATION",
15533                b"avg",
15534                b"200",
15535                b"BUCKETTIMESTAMP",
15536                b"+"
15537            ]),
15538            "*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"
15539        );
15540        // An alignment moves where the bucket edges land.
15541        assert_eq!(
15542            f.run(&[
15543                b"TS.RANGE",
15544                b"t",
15545                b"100",
15546                b"400",
15547                b"ALIGN",
15548                b"100",
15549                b"AGGREGATION",
15550                b"sum",
15551                b"200"
15552            ]),
15553            "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15554        );
15555        // A `COUNT` sitting where the reduction name belongs is that name, and
15556        // the scan for a real one starts again two words later.
15557        assert_eq!(
15558            f.run(&[
15559                b"TS.RANGE",
15560                b"t",
15561                b"-",
15562                b"+",
15563                b"AGGREGATION",
15564                b"count",
15565                b"200"
15566            ]),
15567            "*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"
15568        );
15569        assert_eq!(
15570            f.run(&[
15571                b"TS.RANGE",
15572                b"t",
15573                b"-",
15574                b"+",
15575                b"AGGREGATION",
15576                b"count",
15577                b"200",
15578                b"COUNT",
15579                b"1"
15580            ]),
15581            "*1\r\n*2\r\n:0\r\n+1\r\n"
15582        );
15583    }
15584
15585    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
15586    /// carries two different things depending on which kind of empty it is.
15587    #[test]
15588    fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15589        let mut f = Fixture::new();
15590        for (at, v) in [
15591            (b"0".as_slice(), b"1".as_slice()),
15592            (b"100", b"2"),
15593            (b"500", b"nan"),
15594            (b"600", b"3"),
15595        ] {
15596            f.run(&[b"TS.ADD", b"g", at, v]);
15597        }
15598        // Without `EMPTY` the buckets with nothing in them are not there at all,
15599        // and neither is the one holding only a reading that is not a number.
15600        assert_eq!(
15601            f.run(&[
15602                b"TS.RANGE",
15603                b"g",
15604                b"-",
15605                b"+",
15606                b"AGGREGATION",
15607                b"avg",
15608                b"100"
15609            ]),
15610            "*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"
15611        );
15612        // The sum of nothing is zero rather than not a number.
15613        assert_eq!(
15614            f.run(&[
15615                b"TS.RANGE",
15616                b"g",
15617                b"-",
15618                b"+",
15619                b"AGGREGATION",
15620                b"sum",
15621                b"100",
15622                b"EMPTY"
15623            ]),
15624            "*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\
15625             *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\
15626             *2\r\n:600\r\n+3\r\n"
15627        );
15628        // Buckets 200 through 400 have no readings at all and carry the reading
15629        // before the gap either way round. Bucket 500 has a reading that is not
15630        // a number, so it carries whatever the bucket before it in the reading
15631        // direction answered, which is 2 forwards and 3 backwards.
15632        assert_eq!(
15633            f.run(&[
15634                b"TS.RANGE",
15635                b"g",
15636                b"-",
15637                b"+",
15638                b"AGGREGATION",
15639                b"last",
15640                b"100",
15641                b"EMPTY"
15642            ]),
15643            "*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\
15644             *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\
15645             *2\r\n:600\r\n+3\r\n"
15646        );
15647        assert_eq!(
15648            f.run(&[
15649                b"TS.REVRANGE",
15650                b"g",
15651                b"-",
15652                b"+",
15653                b"AGGREGATION",
15654                b"last",
15655                b"100",
15656                b"EMPTY"
15657            ]),
15658            "*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\
15659             *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\
15660             *2\r\n:0\r\n+1\r\n"
15661        );
15662        // And a window that opens on that bucket has nothing in range before it
15663        // to carry, so it answers not a number.
15664        assert_eq!(
15665            f.run(&[
15666                b"TS.RANGE",
15667                b"g",
15668                b"500",
15669                b"600",
15670                b"AGGREGATION",
15671                b"last",
15672                b"100",
15673                b"EMPTY"
15674            ]),
15675            "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15676        );
15677    }
15678
15679    /// The sentences a read answers when its options do not add up, which are
15680    /// the module's own word for word.
15681    #[test]
15682    fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15683        let mut f = Fixture::new();
15684        f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15685        f.run(&[b"SET", b"str", b"x"]);
15686        let cases: &[(&[&[u8]], &str)] = &[
15687            (
15688                &[b"TS.RANGE", b"t"],
15689                "-ERR wrong number of arguments for 'ts.range' command\r\n",
15690            ),
15691            // The key is resolved before a single option is read.
15692            (
15693                &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15694                "-ERR TSDB: the key does not exist\r\n",
15695            ),
15696            (
15697                &[b"TS.RANGE", b"str", b"-", b"+"],
15698                "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15699            ),
15700            (
15701                &[b"TS.RANGE", b"t", b"abc", b"+"],
15702                "-ERR TSDB: wrong fromTimestamp\r\n",
15703            ),
15704            (
15705                &[b"TS.RANGE", b"t", b"-", b"abc"],
15706                "-ERR TSDB: wrong toTimestamp\r\n",
15707            ),
15708            (
15709                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15710                "-ERR TSDB: COUNT argument is missing\r\n",
15711            ),
15712            (
15713                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15714                "-ERR TSDB: Couldn't parse COUNT\r\n",
15715            ),
15716            (
15717                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15718                "-ERR TSDB: Invalid COUNT value\r\n",
15719            ),
15720            (
15721                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15722                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15723            ),
15724            (
15725                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15726                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15727            ),
15728            (
15729                &[
15730                    b"TS.RANGE",
15731                    b"t",
15732                    b"-",
15733                    b"+",
15734                    b"AGGREGATION",
15735                    b"nope",
15736                    b"100",
15737                ],
15738                "-ERR TSDB: Unknown aggregation type\r\n",
15739            ),
15740            (
15741                &[
15742                    b"TS.RANGE",
15743                    b"t",
15744                    b"-",
15745                    b"+",
15746                    b"AGGREGATION",
15747                    b"avg,,min",
15748                    b"100",
15749                ],
15750                "-ERR TSDB: Empty aggregation type in list\r\n",
15751            ),
15752            // The list of names is read before the width is looked at.
15753            (
15754                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15755                "-ERR TSDB: Unknown aggregation type\r\n",
15756            ),
15757            (
15758                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15759                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15760            ),
15761            (
15762                &[
15763                    b"TS.RANGE",
15764                    b"t",
15765                    b"-",
15766                    b"+",
15767                    b"AGGREGATION",
15768                    b"avg",
15769                    b"100",
15770                    b"X",
15771                    b"EMPTY",
15772                ],
15773                "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15774            ),
15775            (
15776                &[
15777                    b"TS.RANGE",
15778                    b"t",
15779                    b"-",
15780                    b"+",
15781                    b"AGGREGATION",
15782                    b"avg",
15783                    b"100",
15784                    b"BUCKETTIMESTAMP",
15785                    b"z",
15786                ],
15787                "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15788            ),
15789            (
15790                &[
15791                    b"TS.RANGE",
15792                    b"t",
15793                    b"-",
15794                    b"+",
15795                    b"AGGREGATION",
15796                    b"avg",
15797                    b"100",
15798                    b"X",
15799                    b"Y",
15800                    b"BUCKETTIMESTAMP",
15801                    b"-",
15802                ],
15803                "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15804                 AGGREGATION flag\r\n",
15805            ),
15806            (
15807                &[
15808                    b"TS.RANGE",
15809                    b"t",
15810                    b"-",
15811                    b"+",
15812                    b"ALIGN",
15813                    b"z",
15814                    b"AGGREGATION",
15815                    b"avg",
15816                    b"100",
15817                ],
15818                "-ERR TSDB: unknown ALIGN parameter\r\n",
15819            ),
15820            (
15821                &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15822                "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15823            ),
15824            (
15825                &[
15826                    b"TS.RANGE",
15827                    b"t",
15828                    b"-",
15829                    b"+",
15830                    b"ALIGN",
15831                    b"-",
15832                    b"AGGREGATION",
15833                    b"avg",
15834                    b"100",
15835                ],
15836                "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15837            ),
15838            (
15839                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15840                "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15841            ),
15842            (
15843                &[
15844                    b"TS.RANGE",
15845                    b"t",
15846                    b"-",
15847                    b"+",
15848                    b"FILTER_BY_VALUE",
15849                    b"x",
15850                    b"2",
15851                ],
15852                "-ERR TSDB: Couldn't parse MIN\r\n",
15853            ),
15854            (
15855                &[
15856                    b"TS.RANGE",
15857                    b"t",
15858                    b"-",
15859                    b"+",
15860                    b"FILTER_BY_VALUE",
15861                    b"1",
15862                    b"y",
15863                ],
15864                "-ERR TSDB: Couldn't parse MAX\r\n",
15865            ),
15866            (
15867                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15868                "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15869            ),
15870        ];
15871        for (argv, want) in cases {
15872            let got = f.run(argv);
15873            assert_eq!(&got, want, "{:?}", argv.last());
15874        }
15875        // The one sentence here that is yo's own rather than the module's, which
15876        // is D-54. A read that would build more rows than yo will build is
15877        // refused instead of attempted.
15878        f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15879        f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15880        assert_eq!(
15881            f.run(&[
15882                b"TS.RANGE",
15883                b"wide",
15884                b"-",
15885                b"+",
15886                b"AGGREGATION",
15887                b"avg",
15888                b"1",
15889                b"EMPTY"
15890            ]),
15891            "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15892        );
15893    }
15894
15895    /// What RESP3 changes on a read, which is only how a number is written.
15896    #[test]
15897    fn resp3_writes_a_read_value_as_a_double() {
15898        let mut f = Fixture::new();
15899        f.out = Out::new(Proto::Resp3);
15900        for (at, v) in [
15901            (b"0".as_slice(), b"1".as_slice()),
15902            (b"100", b"2"),
15903            (b"500", b"nan"),
15904            (b"600", b"3"),
15905        ] {
15906            f.run(&[b"TS.ADD", b"g", at, v]);
15907        }
15908        assert_eq!(
15909            f.run(&[
15910                b"TS.RANGE",
15911                b"g",
15912                b"0",
15913                b"100",
15914                b"AGGREGATION",
15915                b"avg,min",
15916                b"200"
15917            ]),
15918            "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15919        );
15920        assert_eq!(
15921            f.run(&[
15922                b"TS.RANGE",
15923                b"g",
15924                b"500",
15925                b"600",
15926                b"AGGREGATION",
15927                b"last",
15928                b"100",
15929                b"EMPTY"
15930            ]),
15931            "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15932        );
15933    }
15934
15935    /// Two series with an overlap and a gap each, plus a third holding nothing,
15936    /// which is what the joined reads are measured against.
15937    fn joined() -> Fixture {
15938        let mut f = Fixture::new();
15939        f.run(&[b"TS.CREATE", b"z"]);
15940        for (at, v) in [
15941            (b"10".as_slice(), b"1".as_slice()),
15942            (b"20", b"2"),
15943            (b"40", b"4"),
15944            (b"50", b"5"),
15945        ] {
15946            f.run(&[b"TS.ADD", b"x", at, v]);
15947        }
15948        for (at, v) in [
15949            (b"20".as_slice(), b"20".as_slice()),
15950            (b"30", b"30"),
15951            (b"50", b"50"),
15952            (b"60", b"60"),
15953        ] {
15954            f.run(&[b"TS.ADD", b"y", at, v]);
15955        }
15956        f
15957    }
15958
15959    /// The joined read lines its keys up on the timestamp and writes a row as
15960    /// the timestamp and then a nested array of the columns, which is the one
15961    /// shape in the family that is not the flat pair.
15962    #[test]
15963    fn an_nrange_joins_its_keys_on_the_timestamp() {
15964        let mut f = joined();
15965        // One key still nests, so the shape does not depend on the count.
15966        assert_eq!(
15967            f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15968            "*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\
15969             *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15970        );
15971        // A key with no reading where another key has one writes NaN there.
15972        assert_eq!(
15973            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15974            "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15975             *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15976             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15977             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15978             *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15979             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15980        );
15981        // A series holding nothing is a column of NaN and never a row of its
15982        // own, and the same key twice answers twice.
15983        assert_eq!(
15984            f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15985            "*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"
15986        );
15987        assert_eq!(
15988            f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15989            "*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"
15990        );
15991        // COUNT is applied to the joined rows and not to each key, so backwards
15992        // it gives the newest joined row rather than the newest of each.
15993        assert_eq!(
15994            f.run(&[
15995                b"TS.NREVRANGE",
15996                b"2",
15997                b"x",
15998                b"y",
15999                b"-",
16000                b"+",
16001                b"COUNT",
16002                b"1"
16003            ]),
16004            "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
16005        );
16006        assert_eq!(
16007            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
16008            "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
16009        );
16010        // The two sample filters are settled a key at a time, before the join.
16011        assert_eq!(
16012            f.run(&[
16013                b"TS.NRANGE",
16014                b"2",
16015                b"x",
16016                b"y",
16017                b"-",
16018                b"+",
16019                b"FILTER_BY_VALUE",
16020                b"2",
16021                b"30"
16022            ]),
16023            "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
16024             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
16025             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
16026             *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
16027        );
16028    }
16029
16030    /// The aggregation on a joined read names one reduction a key and then the
16031    /// one bucket width, and each name may be a comma list, so a row can be
16032    /// wider than the key count.
16033    #[test]
16034    fn an_nrange_aggregation_names_one_reduction_a_key() {
16035        let mut f = joined();
16036        assert_eq!(
16037            f.run(&[
16038                b"TS.NRANGE",
16039                b"2",
16040                b"x",
16041                b"y",
16042                b"-",
16043                b"+",
16044                b"AGGREGATION",
16045                b"sum",
16046                b"sum",
16047                b"20"
16048            ]),
16049            "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
16050             *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
16051             *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
16052             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
16053        );
16054        // A comma list on the first key widens the row to three columns.
16055        assert_eq!(
16056            f.run(&[
16057                b"TS.NRANGE",
16058                b"2",
16059                b"x",
16060                b"y",
16061                b"-",
16062                b"+",
16063                b"AGGREGATION",
16064                b"sum,count",
16065                b"avg",
16066                b"20"
16067            ]),
16068            "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
16069             *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
16070             *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
16071             *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
16072        );
16073        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
16074        // sits one or two past the width whatever the key count is.
16075        assert_eq!(
16076            f.run(&[
16077                b"TS.NRANGE",
16078                b"2",
16079                b"x",
16080                b"y",
16081                b"-",
16082                b"+",
16083                b"AGGREGATION",
16084                b"avg",
16085                b"sum",
16086                b"100",
16087                b"EMPTY",
16088                b"BUCKETTIMESTAMP",
16089                b"end"
16090            ]),
16091            "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
16092        );
16093        // A COUNT landing in one of the name slots is a reduction name and not
16094        // the keyword, and the read then has no count at all.
16095        assert_eq!(
16096            f.run(&[
16097                b"TS.NRANGE",
16098                b"2",
16099                b"x",
16100                b"y",
16101                b"-",
16102                b"+",
16103                b"AGGREGATION",
16104                b"avg",
16105                b"COUNT",
16106                b"100"
16107            ]),
16108            "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
16109        );
16110    }
16111
16112    /// The sentences a joined read answers when it does not add up, which are
16113    /// the module's own and come out in the module's own order.
16114    #[test]
16115    fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
16116        let mut f = joined();
16117        f.run(&[b"SET", b"str", b"hi"]);
16118        let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
16119        let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
16120                       must be equal to numkeys\r\n";
16121        let cases: &[(&[&[u8]], &str)] = &[
16122            (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
16123            (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
16124            (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
16125            // Not enough words behind the count for the keys and both ends of
16126            // the span, which is an arity error however many keys were named.
16127            (
16128                &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
16129                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16130            ),
16131            (
16132                &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
16133                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16134            ),
16135            // The reduction names are read before the two ends of the span,
16136            // which no other option is.
16137            (
16138                &[
16139                    b"TS.NRANGE",
16140                    b"2",
16141                    b"x",
16142                    b"y",
16143                    b"abc",
16144                    b"+",
16145                    b"AGGREGATION",
16146                    b"nope",
16147                    b"sum",
16148                    b"100",
16149                ],
16150                "-ERR TSDB: Unknown aggregation type\r\n",
16151            ),
16152            (
16153                &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
16154                "-ERR TSDB: wrong fromTimestamp\r\n",
16155            ),
16156            (
16157                &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
16158                "-ERR TSDB: wrong toTimestamp\r\n",
16159            ),
16160            // A name slot that is missing or holds a number is the count
16161            // sentence, and a width slot that is itself a reduction name is
16162            // that sentence as well.
16163            (
16164                &[
16165                    b"TS.NRANGE",
16166                    b"2",
16167                    b"x",
16168                    b"y",
16169                    b"-",
16170                    b"+",
16171                    b"AGGREGATION",
16172                    b"avg",
16173                ],
16174                numkeys,
16175            ),
16176            (
16177                &[
16178                    b"TS.NRANGE",
16179                    b"2",
16180                    b"x",
16181                    b"y",
16182                    b"-",
16183                    b"+",
16184                    b"AGGREGATION",
16185                    b"100",
16186                    b"sum",
16187                    b"100",
16188                ],
16189                numkeys,
16190            ),
16191            (
16192                &[
16193                    b"TS.NRANGE",
16194                    b"2",
16195                    b"x",
16196                    b"y",
16197                    b"-",
16198                    b"+",
16199                    b"AGGREGATION",
16200                    b"avg",
16201                    b"sum",
16202                    b"sum",
16203                    b"100",
16204                ],
16205                numkeys,
16206            ),
16207            (
16208                &[
16209                    b"TS.NRANGE",
16210                    b"2",
16211                    b"x",
16212                    b"y",
16213                    b"-",
16214                    b"+",
16215                    b"AGGREGATION",
16216                    b"avg",
16217                    b"sum",
16218                    b"abc",
16219                ],
16220                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16221            ),
16222            (
16223                &[
16224                    b"TS.NRANGE",
16225                    b"2",
16226                    b"x",
16227                    b"y",
16228                    b"-",
16229                    b"+",
16230                    b"AGGREGATION",
16231                    b"avg",
16232                    b"sum",
16233                    b"0",
16234                ],
16235                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16236            ),
16237            // With one key none of that applies and the plain parser runs, so a
16238            // lone width is a missing width rather than a count mismatch.
16239            (
16240                &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
16241                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16242            ),
16243            (
16244                &[
16245                    b"TS.NRANGE",
16246                    b"1",
16247                    b"x",
16248                    b"-",
16249                    b"+",
16250                    b"AGGREGATION",
16251                    b"100",
16252                    b"200",
16253                ],
16254                "-ERR TSDB: Unknown aggregation type\r\n",
16255            ),
16256            // The keys come last and in the order they were named.
16257            (
16258                &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
16259                "-ERR TSDB: the key does not exist\r\n",
16260            ),
16261            (
16262                &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
16263                "-ERR WRONGTYPE Operation against a key \
16264                 holding the wrong kind of value\r\n",
16265            ),
16266        ];
16267        for (argv, want) in cases {
16268            let got = f.run(argv);
16269            assert_eq!(&got, want, "{argv:?}");
16270        }
16271    }
16272
16273    /// `TS.READ`, which is a key, one timestamp and everything from there on.
16274    #[test]
16275    fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
16276        let mut f = joined();
16277        assert_eq!(
16278            f.run(&[b"TS.READ", b"x", b"-"]),
16279            "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
16280             *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16281        );
16282        // A plus is the last sample on its own, and a timestamp between two
16283        // samples starts at the one behind it.
16284        assert_eq!(
16285            f.run(&[b"TS.READ", b"x", b"+"]),
16286            "*1\r\n*2\r\n:50\r\n+5\r\n"
16287        );
16288        assert_eq!(
16289            f.run(&[b"TS.READ", b"x", b"25"]),
16290            "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16291        );
16292        // Past the end, a series holding nothing and a key that is not there
16293        // are all the empty array rather than an error.
16294        assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
16295        assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
16296        assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
16297        assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
16298        // The timestamp refusal goes out with nothing in front of it, and a key
16299        // holding something else answers the bare WRONGTYPE rather than the
16300        // module's prefixed one, both unlike the rest of the family.
16301        assert_eq!(
16302            f.run(&[b"TS.READ", b"x", b"abc"]),
16303            "-TSDB: invalid timestamp\r\n"
16304        );
16305        assert_eq!(
16306            f.run(&[b"TS.READ", b"x", b"-1"]),
16307            "-TSDB: invalid timestamp\r\n"
16308        );
16309        f.run(&[b"SET", b"str", b"hi"]);
16310        assert_eq!(
16311            f.run(&[b"TS.READ", b"str", b"-"]),
16312            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
16313        );
16314        // Anything other than exactly three words is an arity error, so there
16315        // is nowhere to put an option even though the table says minus three.
16316        assert_eq!(
16317            f.run(&[b"TS.READ", b"x"]),
16318            "-ERR wrong number of arguments for 'ts.read' command\r\n"
16319        );
16320        assert_eq!(
16321            f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
16322            "-ERR wrong number of arguments for 'ts.read' command\r\n"
16323        );
16324    }
16325
16326    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
16327    /// to read the count to find them.
16328    #[test]
16329    fn getkeys_reads_the_count_of_a_joined_read() {
16330        let mut f = Fixture::new();
16331        assert_eq!(
16332            f.run(&[
16333                b"COMMAND",
16334                b"GETKEYS",
16335                b"TS.NRANGE",
16336                b"2",
16337                b"a",
16338                b"b",
16339                b"-",
16340                b"+"
16341            ]),
16342            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
16343        );
16344        assert_eq!(
16345            f.run(&[
16346                b"COMMAND",
16347                b"GETKEYS",
16348                b"TS.NREVRANGE",
16349                b"1",
16350                b"a",
16351                b"-",
16352                b"+"
16353            ]),
16354            "*1\r\n$1\r\na\r\n"
16355        );
16356        // A count of zero, or one too large for the words that follow it, is
16357        // the server's own refusal and not the module's.
16358        for n in [b"0".as_slice(), b"9", b"abc"] {
16359            assert_eq!(
16360                f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
16361                "-ERR Invalid arguments specified for command\r\n"
16362            );
16363        }
16364    }
16365
16366    /// The five series every test of the label surface works against.
16367    fn labelled() -> Fixture {
16368        let mut f = Fixture::new();
16369        f.run(&[
16370            b"TS.CREATE",
16371            b"a",
16372            b"LABELS",
16373            b"room",
16374            b"kitchen",
16375            b"x",
16376            b"1",
16377        ]);
16378        f.run(&[
16379            b"TS.CREATE",
16380            b"b",
16381            b"LABELS",
16382            b"room",
16383            b"bedroom",
16384            b"x",
16385            b"2",
16386        ]);
16387        f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
16388        f.run(&[b"TS.CREATE", b"d"]);
16389        f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
16390        f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
16391        f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
16392        f
16393    }
16394
16395    /// The filter grammar, which is four steps and a `strtok` rather than a
16396    /// grammar, and which every command that searches on labels shares.
16397    #[test]
16398    fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
16399        let mut f = labelled();
16400        let cases: &[(&[&[u8]], &str)] = &[
16401            // The plain forms, and the order the answer comes back in, which is
16402            // by key name and not by anything the series remembers.
16403            (
16404                &[b"TS.QUERYINDEX", b"room=kitchen"],
16405                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16406            ),
16407            (
16408                &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
16409                "*1\r\n$1\r\na\r\n",
16410            ),
16411            (
16412                &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
16413                "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
16414            ),
16415            // An empty list still counts as something that says which series to
16416            // take, it just never takes any.
16417            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
16418            // Absent and present, neither of which stands on its own.
16419            (
16420                &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
16421                "*1\r\n$1\r\nc\r\n",
16422            ),
16423            (
16424                &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
16425                "*1\r\n$1\r\na\r\n",
16426            ),
16427            (
16428                &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
16429                "-ERR TSDB: please provide at least one matcher\r\n",
16430            ),
16431            // A run of separators is one separator and everything past the
16432            // second field is dropped, so all three of these ask one question.
16433            (
16434                &[b"TS.QUERYINDEX", b"room==kitchen"],
16435                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16436            ),
16437            (
16438                &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
16439                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16440            ),
16441            (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
16442            // A bracket is only a list when it sits straight behind the
16443            // separator, and then the label in front of it has to be there.
16444            (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
16445            (
16446                &[b"TS.QUERYINDEX", b"=(1)"],
16447                "-ERR TSDB: failed parsing labels\r\n",
16448            ),
16449            (
16450                &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
16451                "-ERR TSDB: failed parsing labels\r\n",
16452            ),
16453            (
16454                &[b"TS.QUERYINDEX", b"room=(kitchen"],
16455                "-ERR TSDB: failed parsing labels\r\n",
16456            ),
16457            (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
16458            (
16459                &[b"TS.QUERYINDEX", b"nonsense"],
16460                "-ERR TSDB: failed parsing labels\r\n",
16461            ),
16462            // Nothing here says which series to take.
16463            (
16464                &[b"TS.QUERYINDEX", b"room!=kitchen"],
16465                "-ERR TSDB: please provide at least one matcher\r\n",
16466            ),
16467            // Names and values are both compared byte for byte.
16468            (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
16469            (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
16470            (
16471                &[b"TS.QUERYINDEX"],
16472                "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
16473            ),
16474        ];
16475        for (argv, want) in cases {
16476            let got = f.run(argv);
16477            assert_eq!(&got, want, "{:?}", argv.last());
16478        }
16479    }
16480
16481    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
16482    #[test]
16483    fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16484        let mut f = labelled();
16485        let cases: &[(&[&[u8]], &str)] = &[
16486            (
16487                &[b"TS.QUERYLABELS", b"LABELS"],
16488                "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16489            ),
16490            (
16491                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16492                "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16493            ),
16494            (
16495                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16496                "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16497            ),
16498            // The series wearing `r` twice contributes the smaller of the two
16499            // here, which is not the one it was written down as first.
16500            (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16501            (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16502            (
16503                &[b"TS.QUERYLABELS", b"VALUES"],
16504                "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16505            ),
16506            (
16507                &[b"TS.QUERYLABELS", b"ZZZ"],
16508                "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16509            ),
16510            (
16511                &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16512                "-ERR TSDB: unknown argument, expected FILTER\r\n",
16513            ),
16514            (
16515                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16516                "-ERR TSDB: FILTER given with no filter expressions\r\n",
16517            ),
16518            // With no filter at all every series is taken, which is why the
16519            // first case here answers about `r` as well. A filter that is there
16520            // still has to say which series to take.
16521            (
16522                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16523                "-ERR TSDB: please provide at least one matcher\r\n",
16524            ),
16525            (
16526                &[
16527                    b"TS.QUERYLABELS",
16528                    b"LABELS",
16529                    b"FILTER",
16530                    b"room=kitchen",
16531                    b"x=",
16532                ],
16533                "*1\r\n$4\r\nroom\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    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
16543    /// ways of asking for the labels back alongside it.
16544    #[test]
16545    fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16546        let mut f = labelled();
16547        let cases: &[(&[&[u8]], &str)] = &[
16548            // A series with no samples writes an empty array where the sample
16549            // goes rather than dropping out of the reply.
16550            (
16551                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16552                "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16553                 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16554            ),
16555            (
16556                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16557                "*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\
16558                 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16559                 *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",
16560            ),
16561            // A selected label the series does not wear is a nil, not a gap.
16562            (
16563                &[
16564                    b"TS.MGET",
16565                    b"SELECTED_LABELS",
16566                    b"x",
16567                    b"FILTER",
16568                    b"room=kitchen",
16569                ],
16570                "*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\
16571                 *2\r\n:100\r\n+1.5\r\n\
16572                 *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",
16573            ),
16574            // The other half of the duplicated name rule. This one takes the
16575            // first written down where `TS.QUERYLABELS` takes the smallest.
16576            (
16577                &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16578                "*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",
16579            ),
16580            (
16581                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16582                "*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\
16583                 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16584            ),
16585            // A word that is not an option is ignored, but a missing `FILTER`
16586            // is an arity error whatever else was written.
16587            (
16588                &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16589                "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16590            ),
16591            (
16592                &[b"TS.MGET", b"a", b"b", b"c"],
16593                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16594            ),
16595            (
16596                &[b"TS.MGET", b"FILTER"],
16597                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16598            ),
16599            // Both keyword checks happen before the filter is read, and the two
16600            // sentences spell the second keyword without its `ED`.
16601            (
16602                &[
16603                    b"TS.MGET",
16604                    b"WITHLABELS",
16605                    b"SELECTED_LABELS",
16606                    b"x",
16607                    b"FILTER",
16608                    b"bad",
16609                ],
16610                "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16611            ),
16612            (
16613                &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16614                "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16615            ),
16616        ];
16617        for (argv, want) in cases {
16618            let got = f.run(argv);
16619            assert_eq!(&got, want, "{:?}", argv.last());
16620        }
16621    }
16622
16623    /// What RESP3 changes across the label surface, which is a set where there
16624    /// was an array and a map where there was a pair of them.
16625    #[test]
16626    fn resp3_writes_the_label_surface_as_sets_and_maps() {
16627        let mut f = labelled();
16628        f.out = Out::new(Proto::Resp3);
16629        let cases: &[(&[&[u8]], &str)] = &[
16630            (
16631                &[b"TS.QUERYINDEX", b"room=kitchen"],
16632                "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16633            ),
16634            (
16635                &[b"TS.QUERYLABELS", b"LABELS"],
16636                "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16637            ),
16638            (
16639                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16640                "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16641            ),
16642            // The key stops being the first of three and becomes the map key,
16643            // and the labels stop being pairs and become a map of their own.
16644            (
16645                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16646                "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16647                 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16648            ),
16649            (
16650                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16651                "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16652                 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16653                 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16654            ),
16655            (
16656                &[
16657                    b"TS.MGET",
16658                    b"SELECTED_LABELS",
16659                    b"x",
16660                    b"FILTER",
16661                    b"room=kitchen",
16662                ],
16663                "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16664                 *2\r\n:100\r\n,1.5\r\n\
16665                 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16666            ),
16667            // A map with a name in it twice, which is what a series wearing one
16668            // label name twice turns into.
16669            (
16670                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16671                "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16672                 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16673            ),
16674        ];
16675        for (argv, want) in cases {
16676            let got = f.run(argv);
16677            assert_eq!(&got, want, "{:?}", argv.last());
16678        }
16679    }
16680
16681    /// The same five series with enough samples in them for a group to have
16682    /// something to fold.
16683    fn spanned() -> Fixture {
16684        let mut f = labelled();
16685        f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16686        f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16687        f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16688        f
16689    }
16690
16691    /// A span read out of every series a filter takes, with and without a group
16692    /// over the top of it.
16693    #[test]
16694    fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16695        let mut f = spanned();
16696        let cases: &[(&[&[u8]], &str)] = &[
16697            (
16698                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16699                "*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\
16700                 *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",
16701            ),
16702            // Newest first is applied to each series before anything else sees
16703            // the rows.
16704            (
16705                &[
16706                    b"TS.MREVRANGE",
16707                    b"-",
16708                    b"+",
16709                    b"WITHLABELS",
16710                    b"FILTER",
16711                    b"room=kitchen",
16712                ],
16713                "*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\
16714                 *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\
16715                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16716                 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16717            ),
16718            // A label a series does not wear comes back against a nil rather
16719            // than being left out.
16720            (
16721                &[
16722                    b"TS.MRANGE",
16723                    b"-",
16724                    b"+",
16725                    b"SELECTED_LABELS",
16726                    b"x",
16727                    b"FILTER",
16728                    b"room=kitchen",
16729                ],
16730                "*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\
16731                 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16732                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16733                 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16734            ),
16735            // The fold: 100 is in both series and adds up, the other two are in
16736            // one each and are still rows.
16737            (
16738                &[
16739                    b"TS.MRANGE",
16740                    b"-",
16741                    b"+",
16742                    b"FILTER",
16743                    b"room=kitchen",
16744                    b"GROUPBY",
16745                    b"room",
16746                    b"REDUCE",
16747                    b"sum",
16748                ],
16749                "*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\
16750                 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16751            ),
16752            // RESP2 has nowhere to put the reducer and the member keys, so a
16753            // group wearing labels writes them as two more labels.
16754            (
16755                &[
16756                    b"TS.MRANGE",
16757                    b"-",
16758                    b"+",
16759                    b"WITHLABELS",
16760                    b"FILTER",
16761                    b"room=kitchen",
16762                    b"GROUPBY",
16763                    b"room",
16764                    b"REDUCE",
16765                    b"max",
16766                ],
16767                "*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\
16768                 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16769                 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16770                 *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",
16771            ),
16772            // A count is applied to each member and then again to the fold.
16773            (
16774                &[
16775                    b"TS.MREVRANGE",
16776                    b"-",
16777                    b"+",
16778                    b"COUNT",
16779                    b"1",
16780                    b"FILTER",
16781                    b"room=kitchen",
16782                    b"GROUPBY",
16783                    b"room",
16784                    b"REDUCE",
16785                    b"count",
16786                ],
16787                "*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",
16788            ),
16789            // Nothing wears the label, so nothing is in any group.
16790            (
16791                &[
16792                    b"TS.MRANGE",
16793                    b"-",
16794                    b"+",
16795                    b"FILTER",
16796                    b"room=kitchen",
16797                    b"GROUPBY",
16798                    b"nope",
16799                    b"REDUCE",
16800                    b"sum",
16801                ],
16802                "*0\r\n",
16803            ),
16804            (
16805                &[
16806                    b"TS.MRANGE",
16807                    b"-",
16808                    b"+",
16809                    b"AGGREGATION",
16810                    b"sum,avg",
16811                    b"100",
16812                    b"FILTER",
16813                    b"room=bedroom",
16814                ],
16815                "*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",
16816            ),
16817            // The errors, in the order they are looked for.
16818            (
16819                &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16820                "-ERR TSDB: missing FILTER argument\r\n",
16821            ),
16822            (
16823                &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16824                "-ERR TSDB: missing labels for filter argument\r\n",
16825            ),
16826            (
16827                &[
16828                    b"TS.MRANGE",
16829                    b"-",
16830                    b"+",
16831                    b"GROUPBY",
16832                    b"room",
16833                    b"REDUCE",
16834                    b"sum",
16835                    b"FILTER",
16836                    b"room=kitchen",
16837                ],
16838                "-ERR TSDB: GROUPBY should always come after filter\r\n",
16839            ),
16840            // The group is four words from the end here, so the length is what
16841            // is wrong with it.
16842            (
16843                &[
16844                    b"TS.MRANGE",
16845                    b"-",
16846                    b"+",
16847                    b"FILTER",
16848                    b"room=kitchen",
16849                    b"GROUPBY",
16850                    b"room",
16851                    b"REDUCE",
16852                    b"sum",
16853                    b"x",
16854                ],
16855                "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16856            ),
16857            // And here it is not, so its words are filters and answer first.
16858            (
16859                &[
16860                    b"TS.MRANGE",
16861                    b"-",
16862                    b"+",
16863                    b"FILTER",
16864                    b"nope",
16865                    b"GROUPBY",
16866                    b"room",
16867                    b"REDUCE",
16868                    b"sum",
16869                    b"x",
16870                ],
16871                "-ERR TSDB: failed parsing labels\r\n",
16872            ),
16873            (
16874                &[
16875                    b"TS.MRANGE",
16876                    b"-",
16877                    b"+",
16878                    b"FILTER",
16879                    b"room=kitchen",
16880                    b"GROUPBY",
16881                    b"room",
16882                    b"REDUCE",
16883                    b"twa",
16884                ],
16885                "-ERR TSDB: Invalid reducer type\r\n",
16886            ),
16887            (
16888                &[
16889                    b"TS.MRANGE",
16890                    b"-",
16891                    b"+",
16892                    b"AGGREGATION",
16893                    b"sum,avg",
16894                    b"100",
16895                    b"FILTER",
16896                    b"room=kitchen",
16897                    b"GROUPBY",
16898                    b"room",
16899                    b"REDUCE",
16900                    b"sum",
16901                ],
16902                "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16903            ),
16904            // The label list ends at a keyword, so this is a `COUNT` with a
16905            // `FILTER` where its number should be.
16906            (
16907                &[
16908                    b"TS.MRANGE",
16909                    b"-",
16910                    b"+",
16911                    b"SELECTED_LABELS",
16912                    b"COUNT",
16913                    b"FILTER",
16914                    b"room=kitchen",
16915                ],
16916                "-ERR TSDB: Couldn't parse COUNT\r\n",
16917            ),
16918        ];
16919        for (argv, want) in cases {
16920            let got = f.run(argv);
16921            assert_eq!(&got, want, "{argv:?}");
16922        }
16923    }
16924
16925    /// The multi key reads on RESP3, where the key becomes a map key and the
16926    /// reducer and the member keys become fields of their own.
16927    #[test]
16928    fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16929        let mut f = spanned();
16930        f.out = Out::new(Proto::Resp3);
16931        let cases: &[(&[&[u8]], &str)] = &[
16932            (
16933                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16934                "%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\
16935                 *1\r\n*2\r\n:200\r\n,2\r\n",
16936            ),
16937            // The reductions a read asked for, which RESP2 has no room for at
16938            // all and which is empty on a read that asked for none.
16939            (
16940                &[
16941                    b"TS.MRANGE",
16942                    b"-",
16943                    b"+",
16944                    b"AGGREGATION",
16945                    b"sum,avg",
16946                    b"100",
16947                    b"FILTER",
16948                    b"room=bedroom",
16949                ],
16950                "%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\
16951                 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16952            ),
16953            (
16954                &[
16955                    b"TS.MRANGE",
16956                    b"-",
16957                    b"+",
16958                    b"FILTER",
16959                    b"room=kitchen",
16960                    b"GROUPBY",
16961                    b"room",
16962                    b"REDUCE",
16963                    b"sum",
16964                ],
16965                "%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\
16966                 $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\
16967                 *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",
16968            ),
16969            // The labels hold only the pair the group was made on, because the
16970            // reducer and the sources have somewhere else to go.
16971            (
16972                &[
16973                    b"TS.MRANGE",
16974                    b"-",
16975                    b"+",
16976                    b"WITHLABELS",
16977                    b"FILTER",
16978                    b"room=kitchen",
16979                    b"GROUPBY",
16980                    b"room",
16981                    b"REDUCE",
16982                    b"max",
16983                ],
16984                "%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\
16985                 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16986                 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16987                 *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",
16988            ),
16989            (
16990                &[
16991                    b"TS.MRANGE",
16992                    b"-",
16993                    b"+",
16994                    b"FILTER",
16995                    b"room=kitchen",
16996                    b"GROUPBY",
16997                    b"nope",
16998                    b"REDUCE",
16999                    b"sum",
17000                ],
17001                "%0\r\n",
17002            ),
17003        ];
17004        for (argv, want) in cases {
17005            let got = f.run(argv);
17006            assert_eq!(&got, want, "{argv:?}");
17007        }
17008    }
17009
17010    /// `TS.CREATERULE`, whose refusals come in an order of their own.
17011    #[test]
17012    fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
17013        let mut f = Fixture::new();
17014        f.run(&[b"TS.CREATE", b"src"]);
17015        f.run(&[b"TS.CREATE", b"dst"]);
17016        f.run(&[b"SET", b"plain", b"v"]);
17017        let cases: &[(&[&[u8]], &str)] = &[
17018            // The width is read before the reduction, the reduction before the
17019            // width being above zero, and all three before either key is looked
17020            // at, so a command that is wrong twice complains about the first.
17021            (
17022                &[
17023                    b"TS.CREATERULE",
17024                    b"src",
17025                    b"dst",
17026                    b"AGGREGATION",
17027                    b"nope",
17028                    b"x",
17029                ],
17030                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
17031            ),
17032            (
17033                &[
17034                    b"TS.CREATERULE",
17035                    b"src",
17036                    b"dst",
17037                    b"AGGREGATION",
17038                    b"nope",
17039                    b"10",
17040                ],
17041                "-ERR TSDB: Unknown aggregation type\r\n",
17042            ),
17043            (
17044                &[
17045                    b"TS.CREATERULE",
17046                    b"src",
17047                    b"dst",
17048                    b"AGGREGATION",
17049                    b"avg",
17050                    b"0",
17051                ],
17052                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
17053            ),
17054            (
17055                &[
17056                    b"TS.CREATERULE",
17057                    b"src",
17058                    b"dst",
17059                    b"AGGREGATION",
17060                    b"avg",
17061                    b"10",
17062                    b"x",
17063                ],
17064                "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
17065            ),
17066            (
17067                &[
17068                    b"TS.CREATERULE",
17069                    b"src",
17070                    b"src",
17071                    b"AGGREGATION",
17072                    b"avg",
17073                    b"10",
17074                ],
17075                "-ERR TSDB: the source key and destination key should be different\r\n",
17076            ),
17077            // A key holding something else answers the same as a key that is not
17078            // there at all, because the source is looked up first and neither of
17079            // them is a series.
17080            (
17081                &[
17082                    b"TS.CREATERULE",
17083                    b"nope",
17084                    b"plain",
17085                    b"AGGREGATION",
17086                    b"avg",
17087                    b"10",
17088                ],
17089                "-ERR TSDB: the key does not exist\r\n",
17090            ),
17091            (
17092                &[
17093                    b"TS.CREATERULE",
17094                    b"src",
17095                    b"nope",
17096                    b"AGGREGATION",
17097                    b"avg",
17098                    b"10",
17099                ],
17100                "-ERR TSDB: the key does not exist\r\n",
17101            ),
17102            // A keyword other than AGGREGATION is an arity error rather than a
17103            // syntax one, because the arity is all that is checked.
17104            (
17105                &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
17106                "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
17107            ),
17108            (
17109                &[
17110                    b"TS.CREATERULE",
17111                    b"src",
17112                    b"dst",
17113                    b"AGGREGATION",
17114                    b"avg",
17115                    b"10",
17116                ],
17117                "+OK\r\n",
17118            ),
17119            // The link is now in place, so the same rule again is refused from
17120            // the destination's end.
17121            (
17122                &[
17123                    b"TS.CREATERULE",
17124                    b"src",
17125                    b"dst",
17126                    b"AGGREGATION",
17127                    b"avg",
17128                    b"10",
17129                ],
17130                "-ERR TSDB: the destination key already has a src rule\r\n",
17131            ),
17132            // A source that is already someone's destination, and a destination
17133            // that is already someone's source, are two different sentences.
17134            (
17135                &[
17136                    b"TS.CREATERULE",
17137                    b"dst",
17138                    b"src",
17139                    b"AGGREGATION",
17140                    b"avg",
17141                    b"10",
17142                ],
17143                "-ERR TSDB: the source key already has a source rule\r\n",
17144            ),
17145            (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
17146            (
17147                &[b"TS.DELETERULE", b"src", b"dst"],
17148                "-ERR TSDB: compaction rule does not exist\r\n",
17149            ),
17150            // The source is looked up and the destination is not, so a missing
17151            // destination is a missing rule and a missing source is a missing
17152            // key, which is the other way round from `TS.CREATERULE`.
17153            (
17154                &[b"TS.DELETERULE", b"src", b"nope"],
17155                "-ERR TSDB: compaction rule does not exist\r\n",
17156            ),
17157            (
17158                &[b"TS.DELETERULE", b"nope", b"dst"],
17159                "-ERR TSDB: the key does not exist\r\n",
17160            ),
17161        ];
17162        for (argv, want) in cases {
17163            let got = f.run(argv);
17164            assert_eq!(&got, want, "{argv:?}");
17165        }
17166    }
17167
17168    /// What a rule writes, which is every bucket but the one it is filling.
17169    #[test]
17170    fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
17171        let mut f = Fixture::new();
17172        f.run(&[b"TS.CREATE", b"src"]);
17173        f.run(&[b"TS.CREATE", b"dst"]);
17174        // The readings written before the rule was made are not folded, so the
17175        // destination is still empty after the first two.
17176        f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
17177        f.run(&[
17178            b"TS.CREATERULE",
17179            b"src",
17180            b"dst",
17181            b"AGGREGATION",
17182            b"sum",
17183            b"100",
17184        ]);
17185        f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
17186        assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
17187        // The bucket the rule is filling holds only what it was given, so it is
17188        // 2 rather than 3, and it is written when a reading lands past it.
17189        assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
17190        f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
17191        assert_eq!(
17192            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17193            "*1\r\n*2\r\n:0\r\n+2\r\n"
17194        );
17195        // A reading into a bucket that has already been written works that
17196        // bucket out again over everything the source now holds.
17197        f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
17198        assert_eq!(
17199            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17200            "*1\r\n*2\r\n:0\r\n+11\r\n"
17201        );
17202        // Deleting from the source works the buckets it touched out again and
17203        // reopens the newest one, so `LATEST` starts from the whole bucket.
17204        assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
17205        assert_eq!(
17206            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17207            "*1\r\n*2\r\n:0\r\n+8\r\n"
17208        );
17209        assert_eq!(
17210            f.run(&[b"TS.GET", b"dst", b"LATEST"]),
17211            "*2\r\n:100\r\n+4\r\n"
17212        );
17213        // The link shows on both ends, and dropping either key takes it down.
17214        assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
17215        f.run(&[b"DEL", b"dst"]);
17216        assert_eq!(
17217            f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
17218            "-ERR TSDB: compaction rule does not exist\r\n"
17219        );
17220    }
17221
17222    /// The three shapes an `XADD` id can take, and the one rule behind all of
17223    /// them.
17224    #[test]
17225    fn xadd_ids_only_ever_go_up() {
17226        let mut f = Fixture::new();
17227        // A bare millisecond is that millisecond and sequence zero.
17228        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
17229        // And `5-*` is the next free sequence inside it.
17230        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
17231        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
17232        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
17233        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17234
17235        assert!(
17236            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
17237                .contains("equal or smaller")
17238        );
17239        assert!(
17240            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
17241                .contains("must be greater than 0-0")
17242        );
17243        assert!(
17244            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
17245                .contains("Invalid stream ID")
17246        );
17247        // The pairs have to be pairs, and Redis calls an odd one an arity error
17248        // rather than a syntax error even though the table has already passed.
17249        assert!(
17250            f.run(&[b"XADD", b"s", b"*", b"a"])
17251                .contains("wrong number of arguments")
17252        );
17253
17254        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
17255        // producer can tell nobody is consuming this yet from the write landed.
17256        assert_eq!(
17257            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
17258            "$-1\r\n"
17259        );
17260        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
17261        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
17262        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
17263    }
17264
17265    /// The trim options, which are three keywords that disagree about how many
17266    /// arguments they take.
17267    #[test]
17268    fn trimming_reads_its_options_the_way_redis_does() {
17269        let mut f = Fixture::new();
17270        for i in 1..=10u32 {
17271            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17272        }
17273        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
17274        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17275        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
17276        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17277
17278        // One argument after the keyword and the `~` is read as the threshold,
17279        // which is what a real server does and is the reason this is a number
17280        // complaint and not a syntax one.
17281        assert!(
17282            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
17283                .contains("not an integer")
17284        );
17285        assert!(
17286            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
17287                .contains("MAXLEN argument must be >= 0")
17288        );
17289        // The strategy check runs before the approximation check, so a LIMIT
17290        // with neither is told about the missing strategy.
17291        assert!(
17292            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
17293                .contains("without specifying a trimming strategy")
17294        );
17295        assert!(
17296            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
17297                .contains("without the special ~ option")
17298        );
17299        assert!(
17300            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
17301                .contains("at the same time are not compatible")
17302        );
17303        // NOMKSTREAM is XADD's and XTRIM does not take it.
17304        assert!(
17305            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
17306                .contains("syntax error")
17307        );
17308        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
17309    }
17310
17311    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
17312    #[test]
17313    fn xrange_looks_the_key_up_before_it_reads_the_count() {
17314        let mut f = Fixture::new();
17315        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
17316        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
17317
17318        assert_eq!(
17319            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17320            "*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\
17321             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17322        );
17323        assert_eq!(
17324            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
17325            "*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"
17326        );
17327        // The exclusive bound is stepped after the missing sequence is filled
17328        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
17329        // `6-1` is still in the range.
17330        assert_eq!(
17331            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
17332            "*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\
17333             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17334        );
17335        assert_eq!(
17336            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
17337            "*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"
17338        );
17339        assert!(
17340            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
17341                .contains("Invalid stream ID")
17342        );
17343
17344        // The two kinds of nothing. A key that is not there is an empty array
17345        // and a key that is there with a count of zero is a null array, because
17346        // the lookup happens first.
17347        assert_eq!(
17348            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
17349            "*0\r\n"
17350        );
17351        assert_eq!(
17352            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
17353            "*-1\r\n"
17354        );
17355        f.run(&[b"SET", b"str", b"v"]);
17356        assert!(
17357            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
17358                .starts_with("-WRONGTYPE")
17359        );
17360        // The count is read in a loop, so the last one wins.
17361        assert_eq!(
17362            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
17363            "*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"
17364        );
17365    }
17366
17367    /// `XDEL` and `XACK` check every id before they touch any of them.
17368    #[test]
17369    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
17370        let mut f = Fixture::new();
17371        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17372        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17373        assert!(
17374            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
17375                .contains("Invalid stream ID")
17376        );
17377        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17378        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
17379        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
17380        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
17381        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
17382    }
17383
17384    /// `XGROUP`, and the two different complaints it makes about arguments.
17385    #[test]
17386    fn xgroup_has_an_arity_per_subcommand() {
17387        let mut f = Fixture::new();
17388        assert!(
17389            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17390                .contains("requires the key")
17391        );
17392        assert_eq!(
17393            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
17394            "+OK\r\n"
17395        );
17396        // A second CREATE is BUSYGROUP and not an ordinary error, because a
17397        // client racing another one to make a group branches on the prefix.
17398        assert!(
17399            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17400                .starts_with("-BUSYGROUP")
17401        );
17402        assert_eq!(
17403            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17404            ":1\r\n"
17405        );
17406        assert_eq!(
17407            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17408            ":0\r\n"
17409        );
17410        assert_eq!(
17411            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
17412            ":0\r\n"
17413        );
17414
17415        // Below the subcommand's own arity is an arity error naming the pair.
17416        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
17417        assert!(
17418            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
17419            "{short}"
17420        );
17421        // At or above it in a shape the handler will not take is the other one.
17422        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
17423        assert!(
17424            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
17425            "{odd}"
17426        );
17427        assert!(
17428            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
17429                .contains("Try XGROUP HELP")
17430        );
17431
17432        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
17433        assert!(
17434            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
17435                .starts_with("-NOGROUP")
17436        );
17437        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
17438        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
17439        assert!(
17440            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
17441                .contains("requires the key")
17442        );
17443    }
17444
17445    /// A group read, an acknowledgement, and what is left in between.
17446    #[test]
17447    fn xreadgroup_hands_out_and_xack_takes_back() {
17448        let mut f = Fixture::new();
17449        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17450        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17451        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17452
17453        let first = f.run(&[
17454            b"XREADGROUP",
17455            b"GROUP",
17456            b"g",
17457            b"c1",
17458            b"COUNT",
17459            b"1",
17460            b"STREAMS",
17461            b"s",
17462            b">",
17463        ]);
17464        assert_eq!(
17465            first,
17466            "*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"
17467        );
17468        // A history read names its stream even with nothing to show, which is
17469        // the difference between it and a `>` read that found nothing.
17470        assert_eq!(
17471            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
17472            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
17473        );
17474        assert_eq!(
17475            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17476            "*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"
17477        );
17478
17479        assert_eq!(
17480            f.run(&[b"XPENDING", b"s", b"g"]),
17481            "*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"
17482        );
17483        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17484        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17485        // Empty is four nulls and not a zero with three empty things.
17486        assert_eq!(
17487            f.run(&[b"XPENDING", b"s", b"g"]),
17488            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17489        );
17490
17491        // A history read of an entry that has since been deleted is the id with
17492        // a null beside it, so the consumer can still acknowledge it.
17493        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17494        f.run(&[b"XDEL", b"s", b"2-1"]);
17495        assert_eq!(
17496            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17497            "*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"
17498        );
17499
17500        // The group lookup runs before the id parse, so a `+` at a stream with
17501        // no such group is told about the group and not about the id.
17502        assert!(
17503            f.run(&[
17504                b"XREADGROUP",
17505                b"GROUP",
17506                b"nope",
17507                b"c",
17508                b"STREAMS",
17509                b"s",
17510                b"+"
17511            ])
17512            .starts_with("-NOGROUP")
17513        );
17514        assert!(
17515            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17516                .contains("meaningless in the context of XREADGROUP")
17517        );
17518        assert!(
17519            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17520                .contains("only supported by XREADGROUP")
17521        );
17522        assert!(
17523            f.run(&[
17524                b"XREADGROUP",
17525                b"GROUP",
17526                b"g",
17527                b"c",
17528                b"STREAMS",
17529                b"s",
17530                b"a",
17531                b"b"
17532            ])
17533            .contains("Unbalanced 'xreadgroup' list of streams")
17534        );
17535    }
17536
17537    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
17538    /// answer.
17539    #[test]
17540    fn xread_with_no_block_writes_the_null_itself() {
17541        let mut f = Fixture::new();
17542        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17543        assert_eq!(
17544            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17545            "*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"
17546        );
17547        // Nothing new is a null array and not an empty one, and a stream with
17548        // nothing new is left out rather than sent with an empty list.
17549        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17550        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17551        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17552        assert_eq!(
17553            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17554            "*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"
17555        );
17556        // `$` is the last id, so nothing that is already there comes back.
17557        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17558        // And `+` is the last entry, whatever COUNT says.
17559        assert_eq!(
17560            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17561            "*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"
17562        );
17563        // A count of zero means unlimited here, which is the opposite of what it
17564        // means to XRANGE.
17565        assert_eq!(
17566            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17567            "*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"
17568        );
17569        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
17570        assert!(
17571            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17572                .contains("not an integer")
17573        );
17574        assert!(
17575            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17576                .contains("timeout is negative")
17577        );
17578        assert!(
17579            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17580                .contains("Unbalanced 'xread' list of streams")
17581        );
17582    }
17583
17584    /// A blocked reader, and the two ways it stops being blocked.
17585    #[test]
17586    fn a_blocked_xread_wakes_on_the_next_entry() {
17587        let mut f = Fixture::new();
17588        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17589        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17590        assert_eq!(flow, Flow::Block);
17591        assert!(reply.is_empty());
17592
17593        // Everybody parked on the stream gets the entry, because a read takes
17594        // nothing away. That is the difference between this and BLPOP.
17595        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17596        assert_eq!(flow, Flow::Block);
17597        assert_eq!(f.server.parked(), 2);
17598
17599        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17600        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";
17601        for at in 0..2 {
17602            let mut out = Out::new(Proto::Resp2);
17603            assert!(f.server.serve_waiter(at, 0, &mut out));
17604            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17605        }
17606
17607        // And a deadline that runs out is a null array, the same as a plain
17608        // XREAD that found nothing.
17609        f.server.forget_waiters(7);
17610        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17611        assert_eq!(flow, Flow::Block);
17612        let mut out = Out::new(Proto::Resp2);
17613        assert!(!f.server.serve_waiter(0, 0, &mut out));
17614        assert!(out.as_slice().is_empty());
17615        assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17616        assert_eq!(
17617            core::str::from_utf8(out.as_slice()).expect("ascii"),
17618            "*-1\r\n"
17619        );
17620    }
17621
17622    /// A blocked group reader whose group is destroyed under it.
17623    #[test]
17624    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17625        let mut f = Fixture::new();
17626        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17627        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17628        let (flow, _) = f.flow(&[
17629            b"XREADGROUP",
17630            b"GROUP",
17631            b"g",
17632            b"c",
17633            b"BLOCK",
17634            b"0",
17635            b"STREAMS",
17636            b"s",
17637            b">",
17638        ]);
17639        assert_eq!(flow, Flow::Block);
17640
17641        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17642        let mut out = Out::new(Proto::Resp2);
17643        assert!(f.server.serve_waiter(0, 0, &mut out));
17644        // The ordinary sentence and not a special one about having been parked,
17645        // which is what a running 8.10 sends.
17646        assert_eq!(
17647            core::str::from_utf8(out.as_slice()).expect("ascii"),
17648            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17649        );
17650    }
17651
17652    /// `XCLAIM`, whose argument shape is the odd one in the group.
17653    #[test]
17654    fn xclaim_reads_ids_until_one_will_not_parse() {
17655        let mut f = Fixture::new();
17656        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17657        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17658        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17659        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17660
17661        // Everything after the first argument that is not an id is an option, so
17662        // a `-` is an unrecognised option and not a bad id.
17663        assert!(
17664            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17665                .contains("Unrecognized XCLAIM option '-'")
17666        );
17667        assert_eq!(
17668            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17669            "*1\r\n$3\r\n1-1\r\n"
17670        );
17671        // An id that is pending but whose entry has gone is an empty answer, and
17672        // it leaves the pending list on the way past.
17673        f.run(&[b"XDEL", b"s", b"2-1"]);
17674        assert_eq!(
17675            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17676            "*0\r\n"
17677        );
17678        assert!(
17679            f.run(&[b"XPENDING", b"s", b"g"])
17680                .starts_with("*4\r\n:1\r\n")
17681        );
17682        assert!(
17683            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17684                .starts_with("-NOGROUP")
17685        );
17686        assert!(
17687            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17688                .contains("Invalid min-idle-time argument for XCLAIM")
17689        );
17690    }
17691
17692    /// `XAUTOCLAIM`, and the third value nobody expects.
17693    #[test]
17694    fn xautoclaim_reports_what_it_dropped() {
17695        let mut f = Fixture::new();
17696        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17697        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17698        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17699        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17700        f.run(&[b"XDEL", b"s", b"1-1"]);
17701
17702        // The cursor, what was claimed, and what was dropped for no longer being
17703        // in the stream. The third one is what makes a sweep converge.
17704        assert_eq!(
17705            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17706            "*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"
17707        );
17708        assert!(
17709            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17710                .contains("COUNT must be > 0")
17711        );
17712        assert!(
17713            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17714                .starts_with("-NOGROUP")
17715        );
17716    }
17717
17718    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
17719    #[test]
17720    fn xdelex_answers_one_integer_an_id() {
17721        let mut f = Fixture::new();
17722        for i in 1..=4 {
17723            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17724        }
17725        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17726        f.run(&[
17727            b"XREADGROUP",
17728            b"GROUP",
17729            b"g",
17730            b"c",
17731            b"COUNT",
17732            b"2",
17733            b"STREAMS",
17734            b"s",
17735            b">",
17736        ]);
17737
17738        // One means gone and minus one means it was not there to start with.
17739        assert_eq!(
17740            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17741            "*2\r\n:1\r\n:-1\r\n"
17742        );
17743        // `KEEPREF` leaves the pending entry behind, so the group still counts
17744        // the one it was handed even though the entry has gone.
17745        assert!(
17746            f.run(&[b"XPENDING", b"s", b"g"])
17747                .starts_with("*4\r\n:2\r\n")
17748        );
17749        // `DELREF` takes it out of every pending list on the way past.
17750        assert_eq!(
17751            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17752            "*1\r\n:1\r\n"
17753        );
17754        // `1-1` is still in the list, because the delete before it said KEEPREF.
17755        assert_eq!(
17756            f.run(&[b"XPENDING", b"s", b"g"]),
17757            "*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"
17758        );
17759
17760        // Two means somebody still wants it, and the question is wider than the
17761        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
17762        // refused even though no consumer has ever been handed it.
17763        assert_eq!(
17764            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17765            "*2\r\n:2\r\n:2\r\n"
17766        );
17767
17768        // A key that is not there answers minus ones without reading the IDs.
17769        assert_eq!(
17770            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17771            "*2\r\n:-1\r\n:-1\r\n"
17772        );
17773        // A key that is there validates every ID before deleting any of them.
17774        assert!(
17775            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17776                .starts_with("-ERR Invalid stream ID")
17777        );
17778        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17779
17780        assert!(
17781            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17782                .contains("Number of IDs must be a positive integer")
17783        );
17784        assert!(
17785            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17786                .contains("The `numids` parameter must match the number of arguments")
17787        );
17788        // The condition is one word, so a second one is a syntax error, and so
17789        // is one ID more than the count promised.
17790        assert!(
17791            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17792                .starts_with("-ERR syntax error")
17793        );
17794        assert!(
17795            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17796                .starts_with("-ERR syntax error")
17797        );
17798        // The key is looked up first, so the wrong type beats the syntax.
17799        f.run(&[b"SET", b"str", b"v"]);
17800        assert!(
17801            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17802                .starts_with("-WRONGTYPE")
17803        );
17804    }
17805
17806    /// `XACKDEL`, whose reply is about the pending list and not about the log.
17807    #[test]
17808    fn xackdel_reports_what_the_group_was_holding() {
17809        let mut f = Fixture::new();
17810        for i in 1..=3 {
17811            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17812        }
17813        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17814        f.run(&[
17815            b"XREADGROUP",
17816            b"GROUP",
17817            b"g",
17818            b"c",
17819            b"COUNT",
17820            b"1",
17821            b"STREAMS",
17822            b"s",
17823            b">",
17824        ]);
17825
17826        // Minus one is not about the stream: `2-1` is sitting there unread and
17827        // still answers minus one, because the group was not holding it. It also
17828        // stays, since only an ID that was acknowledged is deleted.
17829        assert_eq!(
17830            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17831            "*2\r\n:1\r\n:-1\r\n"
17832        );
17833        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17834
17835        // A missing group is minus one an ID and not a NOGROUP.
17836        assert_eq!(
17837            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17838            "*1\r\n:-1\r\n"
17839        );
17840        assert_eq!(
17841            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17842            "*1\r\n:-1\r\n"
17843        );
17844
17845        // The acknowledgement happens whatever the condition says, so an ACKED
17846        // that answers two has still emptied the pending list.
17847        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17848        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17849        assert_eq!(
17850            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17851            "*1\r\n:2\r\n"
17852        );
17853        assert_eq!(
17854            f.run(&[b"XPENDING", b"s", b"g"]),
17855            "*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"
17856        );
17857        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17858    }
17859
17860    /// `XNACK`, which hands an entry back to nobody.
17861    #[test]
17862    fn xnack_releases_an_entry_for_the_next_claim() {
17863        let mut f = Fixture::new();
17864        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17865        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17866        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17867        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17868        // Twice, so the delivery count is two and the words have something to
17869        // do with it.
17870        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17871
17872        assert_eq!(
17873            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17874            ":1\r\n"
17875        );
17876        // No owner, no idle time, and the count left where it was. A released
17877        // entry reads as idle for longer than any min-idle-time, which is what
17878        // puts it at the front of the next claim.
17879        assert_eq!(
17880            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17881            "*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"
17882        );
17883        // The consumer no longer holds it, so a filtered XPENDING skips it.
17884        assert_eq!(
17885            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17886            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17887        );
17888        // The bookmark did not move, so a `>` read will not hand it out again.
17889        assert_eq!(
17890            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17891            "*-1\r\n"
17892        );
17893        // A claim at any min-idle-time takes it.
17894        assert_eq!(
17895            f.run(&[
17896                b"XAUTOCLAIM",
17897                b"s",
17898                b"g",
17899                b"c2",
17900                b"99999999",
17901                b"-",
17902                b"JUSTID"
17903            ]),
17904            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17905        );
17906
17907        // `SILENT` takes one off the count rather than putting it back to zero,
17908        // which only shows on an entry that has been handed out more than once.
17909        // It was delivered and then claimed, so it is on two and goes to one.
17910        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17911        assert!(
17912            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17913                .contains(":-1\r\n:1\r\n")
17914        );
17915        // And it stops at zero rather than wrapping.
17916        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17917        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17918        assert!(
17919            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17920                .contains(":-1\r\n:0\r\n")
17921        );
17922        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
17923        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17924        assert!(
17925            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17926                .contains(":9223372036854775807\r\n")
17927        );
17928        f.run(&[
17929            b"XNACK",
17930            b"s",
17931            b"g",
17932            b"FATAL",
17933            b"IDS",
17934            b"1",
17935            b"1-1",
17936            b"RETRYCOUNT",
17937            b"3",
17938        ]);
17939        assert!(
17940            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17941                .contains(":-1\r\n:3\r\n")
17942        );
17943
17944        // Releasing something the group is not holding is zero, and `FORCE`
17945        // makes the pending entry rather than answering zero. A forced entry
17946        // starts at zero, since there was no earlier count to keep.
17947        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17948        assert_eq!(
17949            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17950            ":0\r\n"
17951        );
17952        assert_eq!(
17953            f.run(&[
17954                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17955            ]),
17956            ":1\r\n"
17957        );
17958        assert!(
17959            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17960                .contains(":-1\r\n:0\r\n")
17961        );
17962        // `FORCE` on an ID the stream does not have is still zero.
17963        assert_eq!(
17964            f.run(&[
17965                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17966            ]),
17967            ":0\r\n"
17968        );
17969
17970        // The group is looked up before the mode word, and it raises rather
17971        // than answering per ID the way the two delete commands do.
17972        assert_eq!(
17973            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17974            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17975        );
17976        assert!(
17977            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17978                .starts_with("-ERR")
17979        );
17980        // Its own sentences, which are not the ones XDELEX uses.
17981        assert!(
17982            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17983                .contains("numids must be a positive integer")
17984        );
17985        assert!(
17986            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17987                .contains("number of IDs doesn't match numids")
17988        );
17989        // Everything past the counted IDs is an option, so one too many is an
17990        // option nobody recognises and not a count that does not add up.
17991        assert!(
17992            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17993                .contains("Unrecognized XNACK option '2-1'")
17994        );
17995    }
17996
17997    /// `XINFO`, which is where the shape of the storage shows through.
17998    #[test]
17999    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
18000        let mut f = Fixture::new();
18001        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18002        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
18003        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
18004        f.run(&[
18005            b"XREADGROUP",
18006            b"GROUP",
18007            b"g",
18008            b"c1",
18009            b"COUNT",
18010            b"1",
18011            b"STREAMS",
18012            b"s",
18013            b">",
18014        ]);
18015
18016        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18017        // Ten pairs, since the six idempotency fields have nothing behind them
18018        // here and a zero would claim they had. That is D-27.
18019        assert!(info.starts_with("*20\r\n"), "{info}");
18020        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
18021        assert!(
18022            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
18023            "{info}"
18024        );
18025        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
18026        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
18027
18028        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
18029        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
18030        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
18031        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
18032        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
18033
18034        // A consumer that has never been given anything reports minus one for
18035        // inactive rather than the moment it turned up, which is what tells a
18036        // worker that is stuck from one that has nothing to do.
18037        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
18038        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
18039        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
18040        assert!(
18041            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
18042            "{consumers}"
18043        );
18044        // And in name order, which the storage does not hold them in.
18045        let c1 = consumers.find("c1").unwrap();
18046        let c2 = consumers.find("c2").unwrap();
18047        assert!(c1 < c2, "{consumers}");
18048
18049        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
18050        assert!(full.starts_with("*18\r\n"), "{full}");
18051        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
18052        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
18053
18054        assert!(
18055            f.run(&[b"XINFO", b"STREAM", b"missing"])
18056                .contains("no such key")
18057        );
18058        assert!(
18059            f.run(&[b"XINFO", b"GROUPS", b"missing"])
18060                .contains("no such key")
18061        );
18062        assert!(
18063            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
18064                .starts_with("-NOGROUP")
18065        );
18066        assert!(
18067            f.run(&[b"XINFO", b"NOSUCH", b"s"])
18068                .contains("Try XINFO HELP")
18069        );
18070        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
18071        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
18072    }
18073
18074    /// `XPENDING`'s long form, which reads its arguments by counting them.
18075    #[test]
18076    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
18077        let mut f = Fixture::new();
18078        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18079        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
18080        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
18081
18082        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
18083        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");
18084        assert_eq!(
18085            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
18086            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
18087        );
18088        // A consumer nobody has heard of holds nothing rather than erroring.
18089        assert_eq!(
18090            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
18091            "*0\r\n"
18092        );
18093        assert_eq!(
18094            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
18095            list
18096        );
18097        // IDLE is only read at position three.
18098        assert!(
18099            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
18100                .contains("syntax error")
18101        );
18102        assert!(
18103            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
18104                .contains("syntax error")
18105        );
18106        assert_eq!(
18107            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
18108            "*0\r\n"
18109        );
18110        assert!(
18111            f.run(&[b"XPENDING", b"missing", b"g"])
18112                .starts_with("-NOGROUP")
18113        );
18114    }
18115
18116    /// `XSETID`, which is three counters and two refusals.
18117    #[test]
18118    fn xsetid_will_not_go_below_what_is_there() {
18119        let mut f = Fixture::new();
18120        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
18121        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
18122        assert_eq!(
18123            f.run(&[
18124                b"XSETID",
18125                b"s",
18126                b"10-1",
18127                b"ENTRIESADDED",
18128                b"7",
18129                b"MAXDELETEDID",
18130                b"9-1"
18131            ]),
18132            "+OK\r\n"
18133        );
18134        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18135        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
18136        assert!(
18137            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
18138            "{info}"
18139        );
18140
18141        assert!(
18142            f.run(&[b"XSETID", b"s", b"1-1"])
18143                .contains("smaller than the target stream top item")
18144        );
18145        assert!(
18146            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
18147                .contains("entries_added must be positive")
18148        );
18149        assert!(
18150            f.run(&[b"XSETID", b"missing", b"1-1"])
18151                .contains("no such key")
18152        );
18153    }
18154
18155    /// RESP3, where the two reads answer a map and the entries stay an array.
18156    #[test]
18157    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
18158        let mut f = Fixture::new();
18159        f.run(&[b"HELLO", b"3"]);
18160        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18161        // A map header and then the key and the entries side by side, with no
18162        // two element array wrapping the pair.
18163        assert_eq!(
18164            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
18165            "%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"
18166        );
18167        // The fields are still one flat array and not a map, which is Redis's
18168        // shape and is what every consumer written before RESP3 expects.
18169        assert_eq!(
18170            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
18171            "*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"
18172        );
18173        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
18174    }
18175
18176    /// A store to migrate values into, so a test can watch the inversion.
18177    ///
18178    /// A vector rather than a file for the same reason the tier's own tests use
18179    /// one: the file work has not attached a real store yet, and what this is
18180    /// checking is the policy above the store rather than the store.
18181    struct Mem {
18182        blobs: Vec<Vec<u8>>,
18183    }
18184
18185    impl yo_kv::cold::Blocks for Mem {
18186        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
18187            self.blobs.push(bytes.to_vec());
18188            Ok(yo_common::Addr::new(
18189                yo_common::Space::Log,
18190                (self.blobs.len() - 1) as u64,
18191            ))
18192        }
18193
18194        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
18195            self.blobs
18196                .get(at.offset() as usize)
18197                .map(Vec::as_slice)
18198                .ok_or_else(|| {
18199                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
18200                })
18201        }
18202
18203        fn bytes(&self) -> u64 {
18204            self.blobs.iter().map(|b| b.len() as u64).sum()
18205        }
18206    }
18207
18208    /// A server holding several segments of strings, with somewhere to put them.
18209    ///
18210    /// Answers the fixture and what it was holding when it stopped filling.
18211    fn filled(attach: bool) -> (Fixture, usize) {
18212        let mut f = Fixture::new();
18213        if attach {
18214            f.server
18215                .striped(0)
18216                .hold_stripe(0)
18217                .attach(Box::new(Mem { blobs: Vec::new() }));
18218        }
18219        let val = vec![b'v'; 256];
18220        for i in 0..24000u32 {
18221            let k = format!("key:{i:08}");
18222            f.run(&[b"SET", k.as_bytes(), &val]);
18223        }
18224        let full = f.server.memory_bytes();
18225        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
18226        (f, full)
18227    }
18228
18229    /// Write until the server is under `limit` or the writes run out.
18230    ///
18231    /// The same shape the eviction test uses. A memory limit is enforced in
18232    /// front of a command, so nothing happens until something is written, and
18233    /// the budget means one command does not do the whole job.
18234    fn press(f: &mut Fixture, limit: usize) {
18235        let val = vec![b'v'; 256];
18236        for i in 0..3000u32 {
18237            let k = format!("new:{i:08}");
18238            assert_eq!(
18239                f.run(&[b"SET", k.as_bytes(), &val]),
18240                "+OK\r\n",
18241                "write {i} was refused"
18242            );
18243            f.server.refresh_memory();
18244            if f.server.memory_bytes() <= limit {
18245                return;
18246            }
18247        }
18248        panic!(
18249            "it never got under: {} against {limit}",
18250            f.server.memory_bytes()
18251        );
18252    }
18253
18254    #[test]
18255    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
18256        let mut f = Fixture::new();
18257        assert_eq!(
18258            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18259            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
18260            "no limit is the default"
18261        );
18262        // The same memory value parser `maxmemory` uses, and the same trap in
18263        // it, plus the one spelling that means no limit at all.
18264        for (typed, bytes) in [
18265            (&b"0"[..], "0"),
18266            (b"1024", "1024"),
18267            (b"1k", "1000"),
18268            (b"1gb", "1073741824"),
18269            (b"-1", "-1"),
18270        ] {
18271            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
18272            assert_eq!(
18273                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18274                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
18275                "set {}",
18276                String::from_utf8_lossy(typed)
18277            );
18278        }
18279        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
18280            assert_eq!(
18281                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
18282                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
18283                "refused {}",
18284                String::from_utf8_lossy(bad)
18285            );
18286        }
18287        // Nothing is attached, so the answer to a memory limit is still Redis's.
18288        let info = f.run(&[b"INFO", b"memory"]);
18289        assert!(info.contains("maxstore:-1"), "{info}");
18290        assert!(info.contains("yo_memory_regime:evict"), "{info}");
18291        assert!(info.contains("yo_store_bytes:0"), "{info}");
18292    }
18293
18294    #[test]
18295    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
18296        // The inversion. The same pressure that makes a Redis server throw keys
18297        // away makes this one move values to the file, and afterwards every key
18298        // is still there and still answers with what was stored in it.
18299        let (mut f, full) = filled(true);
18300        let keys = f.run(&[b"DBSIZE"]);
18301        assert!(
18302            f.run(&[b"INFO", b"memory"])
18303                .contains("yo_memory_regime:migrate"),
18304            "a database with somewhere to put values migrates"
18305        );
18306
18307        let limit = full - 2 * 1024 * 1024;
18308        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18309        f.run(&[
18310            b"CONFIG",
18311            b"SET",
18312            b"maxmemory",
18313            limit.to_string().as_bytes(),
18314        ]);
18315        press(&mut f, limit);
18316
18317        assert!(
18318            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18319            "nothing was thrown away"
18320        );
18321        let after: usize = f.run(&[b"DBSIZE"])[1..]
18322            .trim_end()
18323            .parse()
18324            .expect("a count");
18325        let before: usize = keys[1..].trim_end().parse().expect("a count");
18326        assert!(after > before, "the keys that came in are all still here");
18327        assert!(
18328            f.server.store_bytes() > 0,
18329            "and what came out of memory went to the file"
18330        );
18331        // And the values read back, which is the part that makes it a migration
18332        // rather than a loss.
18333        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
18334        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
18335        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
18336    }
18337
18338    #[test]
18339    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
18340        // The documented setting for a drop in cache. A file that may hold
18341        // nothing cannot be migrated to, so eviction is all that is left, and
18342        // the server behaves exactly as it did before any of this existed.
18343        let (mut f, full) = filled(true);
18344        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
18345        assert!(
18346            f.run(&[b"INFO", b"memory"])
18347                .contains("yo_memory_regime:evict"),
18348            "nothing may go to the file"
18349        );
18350
18351        let limit = full - 2 * 1024 * 1024;
18352        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18353        f.run(&[
18354            b"CONFIG",
18355            b"SET",
18356            b"maxmemory",
18357            limit.to_string().as_bytes(),
18358        ]);
18359        press(&mut f, limit);
18360
18361        assert!(
18362            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18363            "keys were thrown away, which is what was asked for"
18364        );
18365        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
18366    }
18367
18368    #[test]
18369    fn a_full_file_goes_back_to_evicting() {
18370        // A storage limit reached is a storage limit, and eviction is the right
18371        // answer to one. The budget here is a few kilobytes, so the first round
18372        // of migration fills it and everything after that is evicted.
18373        let (mut f, full) = filled(true);
18374        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
18375        let limit = full - 2 * 1024 * 1024;
18376        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18377        f.run(&[
18378            b"CONFIG",
18379            b"SET",
18380            b"maxmemory",
18381            limit.to_string().as_bytes(),
18382        ]);
18383        press(&mut f, limit);
18384
18385        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
18386        assert!(
18387            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18388            "and then it started evicting"
18389        );
18390        assert!(
18391            f.run(&[b"INFO", b"memory"])
18392                .contains("yo_memory_regime:evict"),
18393            "and it says so"
18394        );
18395    }
18396    // ------------------------------------------------------------- stripes
18397
18398    /// Every string command, run twice: once on a database that is one keyspace
18399    /// and once on a database that is eight, with the same commands in the same
18400    /// order and the replies compared byte for byte.
18401    ///
18402    /// This is the whole claim the striping rests on. A key belongs to one
18403    /// stripe and to no other, so the answer to a command cannot depend on how
18404    /// many stripes there are, and the way to check that is to ask the same
18405    /// question of two servers that differ in nothing else.
18406    ///
18407    /// The keys are chosen to land on different stripes rather than to look
18408    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
18409    /// those three keys are not all on the same one, and at eight stripes three
18410    /// keys land together about one time in fifty.
18411    #[test]
18412    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
18413        let script: &[&[&[u8]]] = &[
18414            // The single key commands, which are the ones that get handed one
18415            // stripe at the dispatch site.
18416            &[b"SET", b"k1", b"v1"],
18417            &[b"SET", b"k2", b"v2"],
18418            &[b"GET", b"k1"],
18419            &[b"GET", b"nothing"],
18420            &[b"GETSET", b"k1", b"v1b"],
18421            &[b"SETNX", b"k1", b"no"],
18422            &[b"SETNX", b"k3", b"yes"],
18423            &[b"APPEND", b"k3", b"!"],
18424            &[b"STRLEN", b"k3"],
18425            &[b"SETRANGE", b"k3", b"1", b"XY"],
18426            &[b"GETRANGE", b"k3", b"0", b"-1"],
18427            &[b"INCR", b"n1"],
18428            &[b"INCRBY", b"n1", b"41"],
18429            &[b"DECRBY", b"n1", b"2"],
18430            &[b"INCRBYFLOAT", b"f1", b"1.5"],
18431            &[b"SETEX", b"e1", b"100", b"v"],
18432            &[b"PSETEX", b"e2", b"100000", b"v"],
18433            &[b"GETEX", b"e1", b"PERSIST"],
18434            &[b"GETDEL", b"k2"],
18435            &[b"GET", b"k2"],
18436            &[b"DIGEST", b"k1"],
18437            &[b"DELEX", b"k3"],
18438            // The five that name more than one key, which are the ones that
18439            // cannot be handed one stripe at all.
18440            &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
18441            &[b"MGET", b"a", b"b", b"c", b"missing"],
18442            &[b"MSETNX", b"d", b"4", b"e", b"5"],
18443            &[b"MSETNX", b"e", b"6", b"f", b"7"],
18444            &[b"MGET", b"d", b"e", b"f"],
18445            &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
18446            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
18447            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
18448            &[b"MGET", b"g", b"h"],
18449            &[b"SET", b"s1", b"ohmytext"],
18450            &[b"SET", b"s2", b"mynewtext"],
18451            &[b"LCS", b"s1", b"s2"],
18452            &[b"LCS", b"s1", b"s2", b"LEN"],
18453            &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
18454            &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
18455            &[b"LCS", b"s1", b"gone"],
18456            // And the errors, which have to be the same errors.
18457            &[b"MSET", b"odd"],
18458            &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
18459            &[b"MGET"],
18460        ];
18461
18462        let mut one = Fixture::new();
18463        let mut many = Fixture::striped(8);
18464        for parts in script {
18465            let a = one.run(parts);
18466            let b = many.run(parts);
18467            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18468        }
18469    }
18470
18471    /// The keys of an `MSET` really do end up on different stripes.
18472    ///
18473    /// Without this the test above could pass on a server whose stripe number
18474    /// happened to be a constant, which is a striped database in name only.
18475    #[test]
18476    fn a_striped_database_spreads_the_keys_it_is_given() {
18477        let mut f = Fixture::striped(8);
18478        for i in 0..256 {
18479            let key = format!("key:{i}");
18480            f.run(&[b"SET", key.as_bytes(), b"v"]);
18481        }
18482        assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18483    }
18484
18485    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
18486    /// that is not a string comes back nil and the rest of the reply is intact.
18487    #[test]
18488    fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18489        let mut one = Fixture::new();
18490        let mut many = Fixture::striped(8);
18491        for f in [&mut one, &mut many] {
18492            f.run(&[b"SET", b"str", b"v"]);
18493            // Planted rather than pushed. `RPUSH` belongs to the list group,
18494            // which has not been taught about stripes yet and would refuse the
18495            // wide server. What is under test is what `MGET` does when it walks
18496            // onto a key that is not a string, and that does not care how the
18497            // key got there.
18498            f.server
18499                .striped(0)
18500                .hold(b"list")
18501                .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18502                .expect("a new list");
18503        }
18504        assert_eq!(
18505            one.run(&[b"MGET", b"str", b"list", b"gone"]),
18506            many.run(&[b"MGET", b"str", b"list", b"gone"])
18507        );
18508    }
18509
18510    /// The same claim for the keyspace group, and the same way of checking it.
18511    ///
18512    /// `SORT` is not in the script because it is the one command in that file
18513    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
18514    /// `RANDOMKEY` are not in it either, because those three do not promise an
18515    /// order and comparing two replies byte for byte would be asserting one.
18516    /// They get tests of their own below.
18517    #[test]
18518    fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18519        let script: &[&[&[u8]]] = &[
18520            &[b"SET", b"k1", b"v1"],
18521            &[b"SET", b"k2", b"v2"],
18522            &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18523            &[b"TYPE", b"k1"],
18524            &[b"TYPE", b"gone"],
18525            &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18526            &[b"EXPIRE", b"k1", b"100"],
18527            &[b"TTL", b"k1"],
18528            &[b"EXPIRE", b"k1", b"200", b"NX"],
18529            &[b"PERSIST", b"k1"],
18530            &[b"TTL", b"k1"],
18531            &[b"PEXPIREAT", b"k2", b"1900000000000"],
18532            &[b"EXPIRETIME", b"k2"],
18533            &[b"PEXPIRETIME", b"k2"],
18534            &[b"PERSIST", b"k2"],
18535            &[b"OBJECT", b"ENCODING", b"k1"],
18536            &[b"OBJECT", b"REFCOUNT", b"k1"],
18537            &[b"OBJECT", b"IDLETIME", b"k1"],
18538            &[b"OBJECT", b"FREQ", b"k1"],
18539            &[b"OBJECT", b"ENCODING", b"gone"],
18540            &[b"OBJECT", b"HELP"],
18541            &[b"RENAME", b"k1", b"k9"],
18542            &[b"GET", b"k9"],
18543            &[b"RENAME", b"gone", b"x"],
18544            &[b"RENAMENX", b"k9", b"k2"],
18545            &[b"RENAMENX", b"k9", b"k8"],
18546            &[b"GET", b"k8"],
18547            &[b"COPY", b"k8", b"c1"],
18548            &[b"COPY", b"k8", b"c1"],
18549            &[b"COPY", b"k8", b"c1", b"REPLACE"],
18550            &[b"COPY", b"k8", b"k8"],
18551            &[b"COPY", b"gone", b"c2"],
18552            &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18553            &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18554            &[b"MOVE", b"c1", b"1"],
18555            &[b"MOVE", b"c1", b"1"],
18556            &[b"MOVE", b"k8", b"0"],
18557            &[b"DEL", b"k2", b"gone"],
18558            &[b"UNLINK", b"k8", b"k8"],
18559            &[b"DBSIZE"],
18560        ];
18561
18562        let mut one = Fixture::new();
18563        let mut many = Fixture::striped(8);
18564        for parts in script {
18565            let a = one.run(parts);
18566            let b = many.run(parts);
18567            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18568        }
18569
18570        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
18571        // payload is taken from the store rather than parsed back out of a
18572        // reply that is not text. Both servers dump the same key and the bytes
18573        // are the same bytes, which is the first half of what is being checked
18574        // here.
18575        for f in [&mut one, &mut many] {
18576            f.run(&[b"SET", b"d1", b"payload"]);
18577            let payload = f
18578                .server
18579                .striped(0)
18580                .hold(b"d1")
18581                .dump(b"d1")
18582                .expect("a key that is there");
18583            assert!(
18584                f.run(&[b"DUMP", b"d1"])
18585                    .starts_with(&format!("${}", payload.len())),
18586                "a payload of the length the store gave"
18587            );
18588            assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18589            assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18590            assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18591            assert_eq!(
18592                f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18593                "-BUSYKEY Target key name already exists.\r\n"
18594            );
18595            assert_eq!(
18596                f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18597                "-ERR DUMP payload version or checksum are wrong\r\n"
18598            );
18599        }
18600    }
18601
18602    /// A `SCAN` of a database of eight stripes comes back with all of it.
18603    ///
18604    /// The cursor is the thing under test. It has to carry the stripe as well
18605    /// as the place in it, so a client that stops at one stripe and comes back
18606    /// carries on in that stripe and not at the top of the database, and the
18607    /// walk has to end once rather than eight times.
18608    #[test]
18609    fn a_scan_of_a_striped_database_walks_all_of_it() {
18610        let mut f = Fixture::striped(8);
18611        for i in 0..500 {
18612            let key = format!("key:{i}");
18613            f.run(&[b"SET", key.as_bytes(), b"v"]);
18614        }
18615
18616        let mut seen = Vec::new();
18617        let mut cursor = "0".to_owned();
18618        let mut calls = 0;
18619        loop {
18620            let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18621            let (next, keys) = scan_reply(&reply);
18622            seen.extend(keys);
18623            cursor = next;
18624            calls += 1;
18625            assert!(calls < 5_000, "a scan that will not finish");
18626            if cursor == "0" {
18627                break;
18628            }
18629        }
18630        seen.sort();
18631        assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18632        assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18633
18634        // And the options still work when the walk is over several stripes,
18635        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
18636        // applied by each stripe on the way.
18637        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18638        let (_, keys) = scan_reply(&reply);
18639        assert_eq!(keys.len(), 10, "key:40 through key:49");
18640        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18641        let (_, keys) = scan_reply(&reply);
18642        assert!(keys.is_empty(), "nothing here is a list");
18643    }
18644
18645    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
18646    ///
18647    /// The draw picks the stripe first, so the thing that can go wrong is that
18648    /// it always picks the same one, and two hundred draws over eight stripes
18649    /// would make that obvious.
18650    #[test]
18651    fn a_random_key_can_come_from_any_stripe() {
18652        let mut f = Fixture::striped(8);
18653        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18654        for i in 0..200 {
18655            let key = format!("key:{i}");
18656            f.run(&[b"SET", key.as_bytes(), b"v"]);
18657        }
18658        let mut homes = std::collections::HashSet::new();
18659        for _ in 0..200 {
18660            let got = f.run(&[b"RANDOMKEY"]);
18661            let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18662            assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18663            homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18664        }
18665        assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18666    }
18667
18668    /// Two keys that are not on the same stripe, which is what `RENAME` and
18669    /// `COPY` have to cope with and what a test has to arrange rather than
18670    /// hope for.
18671    fn apart(f: &mut Fixture, src: &str) -> String {
18672        let home = f.server.striped(0).stripe_of(src.as_bytes());
18673        for i in 0..1_000 {
18674            let dst = format!("dst:{i}");
18675            if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18676                return dst;
18677            }
18678        }
18679        panic!("eight stripes and a thousand keys all landed in one place");
18680    }
18681
18682    /// A rename whose two keys are on two stripes moves the value, the deadline
18683    /// and, for a collection, the body itself.
18684    #[test]
18685    fn a_rename_across_stripes_takes_everything_with_it() {
18686        let mut f = Fixture::striped(8);
18687        let dst = apart(&mut f, "src");
18688        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18689
18690        f.run(&[b"SET", src, b"v"]);
18691        f.run(&[b"EXPIRE", src, b"100"]);
18692        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18693        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18694        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18695        assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18696
18697        // A list, because a string lives in its record and a collection lives
18698        // in a slab, and the second of those is the one that can be left
18699        // behind. Planted through the store, since the list group has not been
18700        // taught about stripes yet.
18701        f.server
18702            .striped(0)
18703            .hold(src)
18704            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18705            .expect("a new list");
18706        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18707        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18708        assert_eq!(
18709            f.server.striped(0).hold(dst).llen(dst).expect("a list"),
18710            2,
18711            "the members are on the stripe the key moved to"
18712        );
18713
18714        // And `RENAMENX` still refuses a destination that is taken, which is
18715        // the one answer the cross stripe path has to work out for itself.
18716        f.run(&[b"SET", src, b"v"]);
18717        assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18718        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18719        assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18720    }
18721
18722    /// And a copy across two stripes leaves both keys behind it.
18723    #[test]
18724    fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18725        let mut f = Fixture::striped(8);
18726        let dst = apart(&mut f, "src");
18727        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18728
18729        f.run(&[b"SET", src, b"v"]);
18730        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18731        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18732        assert_eq!(
18733            f.run(&[b"COPY", src, dst]),
18734            ":0\r\n",
18735            "the destination is taken"
18736        );
18737        f.run(&[b"SET", src, b"w"]);
18738        assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18739        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18740
18741        // A collection is cloned rather than moved, so both keys have a body of
18742        // their own afterwards and writing to one does not show up in the
18743        // other.
18744        f.run(&[b"DEL", src, dst]);
18745        f.server
18746            .striped(0)
18747            .hold(src)
18748            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18749            .expect("a new list");
18750        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18751        f.server
18752            .striped(0)
18753            .hold(src)
18754            .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18755            .expect("a list that is there");
18756        assert_eq!(f.server.striped(0).hold(src).llen(src).expect("a list"), 3);
18757        assert_eq!(f.server.striped(0).hold(dst).llen(dst).expect("a list"), 2);
18758    }
18759
18760    /// Every bitmap command, on one stripe and on eight, replies compared byte
18761    /// for byte.
18762    ///
18763    /// `BITOP` is the one that names more than one key and it is where the work
18764    /// went. The rest are single key commands that now find their own stripe,
18765    /// and they are here because the cheapest way to be sure the routing is
18766    /// right is to ask.
18767    #[test]
18768    fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18769        let script: &[&[&[u8]]] = &[
18770            &[b"SET", b"k1", b"foobar"],
18771            &[b"SETBIT", b"b1", b"7", b"1"],
18772            &[b"SETBIT", b"b1", b"7", b"0"],
18773            &[b"GETBIT", b"k1", b"6"],
18774            &[b"GETBIT", b"k1", b"100"],
18775            &[b"BITCOUNT", b"k1"],
18776            &[b"BITCOUNT", b"k1", b"0", b"0"],
18777            &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18778            &[b"BITPOS", b"k1", b"1"],
18779            &[b"BITPOS", b"k1", b"0", b"2"],
18780            &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18781            &[
18782                b"BITFIELD",
18783                b"bf",
18784                b"SET",
18785                b"u8",
18786                b"0",
18787                b"255",
18788                b"GET",
18789                b"u8",
18790                b"0",
18791            ],
18792            &[
18793                b"BITFIELD",
18794                b"bf",
18795                b"OVERFLOW",
18796                b"SAT",
18797                b"INCRBY",
18798                b"u8",
18799                b"0",
18800                b"10",
18801            ],
18802            &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18803            // The multi key one, over sources that are not on one stripe unless
18804            // eight stripes have folded into one.
18805            &[b"SET", b"s1", b"abc"],
18806            &[b"SET", b"s2", b"abd"],
18807            &[b"SET", b"s3", b"a"],
18808            &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18809            &[b"GET", b"d1"],
18810            &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18811            &[b"GET", b"d2"],
18812            &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18813            &[b"STRLEN", b"d3"],
18814            &[b"BITOP", b"NOT", b"d4", b"s1"],
18815            &[b"STRLEN", b"d4"],
18816            &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18817            &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18818            &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18819            &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18820            // A source that is not there reads as empty, and a result with
18821            // nothing in it deletes the destination rather than writing one.
18822            &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18823            &[b"EXISTS", b"d1"],
18824            &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18825            &[b"GET", b"d9"],
18826            // And the errors, which have to be the same errors. The key that
18827            // is not a string is planted below rather than pushed here, since
18828            // the list group has not been taught about stripes yet.
18829            &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18830            &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18831            &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18832            &[b"BITOP", b"DIFF", b"d1", b"s1"],
18833            &[b"BITOP", b"NOPE", b"d1", b"s1"],
18834            &[b"BITCOUNT", b"list"],
18835            &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18836        ];
18837
18838        let mut one = Fixture::new();
18839        let mut many = Fixture::striped(8);
18840        for f in [&mut one, &mut many] {
18841            plant_list(f, b"list");
18842        }
18843        for parts in script {
18844            let a = one.run(parts);
18845            let b = many.run(parts);
18846            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18847        }
18848    }
18849
18850    /// A list under `key`, put there through the store.
18851    ///
18852    /// What a test does when it wants a key of the wrong type on a striped
18853    /// server, because the command that would make one is in a group that has
18854    /// not been taught about stripes yet.
18855    fn plant_list(f: &mut Fixture, key: &[u8]) {
18856        f.server
18857            .striped(0)
18858            .hold(key)
18859            .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18860            .expect("a new list");
18861    }
18862
18863    /// A `BITOP` whose keys are on two stripes reads both of them.
18864    ///
18865    /// The test above spreads its keys by hashing and would still pass if one
18866    /// stripe were doing all the work, since the answers would be the same. This
18867    /// one puts the destination and the two sources where they are known not to
18868    /// share a stripe.
18869    #[test]
18870    fn a_bitop_across_stripes_reads_every_source() {
18871        let mut f = Fixture::striped(8);
18872        let other = apart(&mut f, "src");
18873        let (src, far) = (b"src".as_slice(), other.as_bytes());
18874        assert_ne!(
18875            f.server.striped(0).stripe_of(src),
18876            f.server.striped(0).stripe_of(far),
18877            "the two keys are the point of the test"
18878        );
18879
18880        f.run(&[b"SET", src, b"abc"]);
18881        f.run(&[b"SET", far, b"abd"]);
18882        assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18883        assert_eq!(
18884            f.run(&[b"GET", far]),
18885            "$3\r\nab`\r\n",
18886            "a destination that is also a source"
18887        );
18888        f.run(&[b"SET", far, b"abd"]);
18889        assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18890        assert_eq!(
18891            f.run(&[b"GET", src]),
18892            "$3\r\n\0\0\x07\r\n",
18893            "and the other way round"
18894        );
18895
18896        // A result of nothing deletes a destination on whatever stripe it is
18897        // on, and a source of the wrong type is refused before anything is
18898        // written.
18899        f.run(&[b"SET", src, b"abc"]);
18900        f.run(&[b"DEL", far]);
18901        assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18902        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18903        f.run(&[b"SET", src, b"abc"]);
18904        f.run(&[b"DEL", far]);
18905        plant_list(&mut f, far);
18906        assert_eq!(
18907            f.run(&[b"BITOP", b"OR", b"out", src, far]),
18908            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18909        );
18910        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18911    }
18912
18913    /// Every HyperLogLog command, on one stripe and on eight.
18914    #[test]
18915    fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18916        let script: &[&[&[u8]]] = &[
18917            &[b"PFADD", b"h1", b"a", b"b", b"c"],
18918            &[b"PFADD", b"h1", b"a"],
18919            &[b"PFADD", b"h2"],
18920            &[b"PFADD", b"h2", b"c", b"d", b"e"],
18921            &[b"PFCOUNT", b"h1"],
18922            &[b"PFCOUNT", b"h2"],
18923            &[b"PFCOUNT", b"missing"],
18924            // The two that name more than one key.
18925            &[b"PFCOUNT", b"h1", b"h2"],
18926            &[b"PFCOUNT", b"h1", b"missing"],
18927            &[b"PFMERGE", b"m", b"h1", b"h2"],
18928            &[b"PFCOUNT", b"m"],
18929            &[b"STRLEN", b"m"],
18930            &[b"PFMERGE", b"m"],
18931            &[b"PFCOUNT", b"m"],
18932            &[b"PFMERGE", b"m2", b"missing"],
18933            &[b"PFCOUNT", b"m2"],
18934            // The debugging ones, which are single key and change what they
18935            // look at.
18936            &[b"PFDEBUG", b"ENCODING", b"h1"],
18937            &[b"PFDEBUG", b"DECODE", b"h1"],
18938            &[b"PFDEBUG", b"TODENSE", b"h1"],
18939            &[b"PFDEBUG", b"ENCODING", b"h1"],
18940            &[b"PFDEBUG", b"TODENSE", b"h1"],
18941            &[b"PFCOUNT", b"h1", b"h2"],
18942            &[b"PFSELFTEST"],
18943            // And the errors.
18944            &[b"SET", b"plain", b"not a sketch at all"],
18945            &[b"PFADD", b"plain", b"a"],
18946            &[b"PFCOUNT", b"plain"],
18947            &[b"PFCOUNT", b"h1", b"plain"],
18948            &[b"PFMERGE", b"plain", b"h1"],
18949            &[b"PFMERGE", b"m", b"plain"],
18950            &[b"PFDEBUG", b"ENCODING", b"gone"],
18951            &[b"PFDEBUG", b"NOPE", b"h1"],
18952        ];
18953
18954        let mut one = Fixture::new();
18955        let mut many = Fixture::striped(8);
18956        for parts in script {
18957            let a = one.run(parts);
18958            let b = many.run(parts);
18959            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18960        }
18961    }
18962
18963    /// Every set command, on one stripe and on eight.
18964    ///
18965    /// The commands that answer members answer them in whatever order the set
18966    /// or the table they were built in holds them, so those replies are
18967    /// compared as sets. Everything else is compared byte for byte. Two servers
18968    /// agreeing on the order would be a fact about the tables and not about the
18969    /// answer, and asserting it would make this test fail for a reason nobody
18970    /// cares about.
18971    #[test]
18972    fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18973        const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18974        let script: &[&[&[u8]]] = &[
18975            &[b"SADD", b"s1", b"a", b"b", b"c"],
18976            &[b"SADD", b"s1", b"a"],
18977            &[b"SADD", b"s2", b"b", b"c", b"d"],
18978            &[b"SADD", b"ints", b"1", b"2", b"3"],
18979            &[b"SCARD", b"s1"],
18980            &[b"SISMEMBER", b"s1", b"a"],
18981            &[b"SISMEMBER", b"s1", b"z"],
18982            &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18983            &[b"SMEMBERS", b"s1"],
18984            &[b"SREM", b"s1", b"c"],
18985            &[b"SADD", b"s1", b"c"],
18986            &[b"SSCAN", b"s1", b"0"],
18987            &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18988            // The two draws, on a set of one member, which is the only shape
18989            // whose answer two servers have to agree on.
18990            &[b"SADD", b"one", b"m"],
18991            &[b"SRANDMEMBER", b"one"],
18992            &[b"SRANDMEMBER", b"one", b"-3"],
18993            &[b"SRANDMEMBER", b"gone"],
18994            &[b"SPOP", b"one"],
18995            &[b"SPOP", b"one"],
18996            &[b"SPOP", b"gone", b"2"],
18997            // The one that names two keys.
18998            &[b"SMOVE", b"s1", b"s2", b"a"],
18999            &[b"SMOVE", b"s1", b"s2", b"zzz"],
19000            &[b"SMOVE", b"gone", b"s2", b"a"],
19001            &[b"SMEMBERS", b"s1"],
19002            &[b"SMEMBERS", b"s2"],
19003            // The algebra.
19004            &[b"SINTER", b"s1", b"s2"],
19005            &[b"SUNION", b"s1", b"s2"],
19006            &[b"SDIFF", b"s2", b"s1"],
19007            &[b"SINTER", b"s1", b"gone"],
19008            &[b"SUNION", b"s1", b"gone"],
19009            &[b"SDIFF", b"gone", b"s1"],
19010            &[b"SINTER", b"ints", b"s1"],
19011            &[b"SINTERCARD", b"2", b"s1", b"s2"],
19012            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
19013            &[b"SUNIONCARD", b"2", b"s1", b"s2"],
19014            &[b"SDIFFCARD", b"2", b"s2", b"s1"],
19015            &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
19016            &[b"SMEMBERS", b"d1"],
19017            &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
19018            &[b"SCARD", b"d2"],
19019            &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
19020            &[b"SCARD", b"d3"],
19021            // An empty result deletes the destination rather than storing a
19022            // set with nothing in it.
19023            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
19024            &[b"EXISTS", b"d4"],
19025            // And a destination that is also a source.
19026            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
19027            &[b"SCARD", b"s2"],
19028            // The errors, which have to be the same errors.
19029            &[b"SET", b"str", b"v"],
19030            &[b"SADD", b"str", b"a"],
19031            &[b"SINTER", b"s1", b"str"],
19032            &[b"SINTERSTORE", b"d5", b"s1", b"str"],
19033            &[b"EXISTS", b"d5"],
19034            &[b"SMOVE", b"str", b"s2", b"a"],
19035            &[b"SMOVE", b"s1", b"str", b"b"],
19036            &[b"SMOVE", b"gone", b"str", b"b"],
19037            &[b"SINTERCARD", b"0", b"s1"],
19038            &[b"SINTERCARD", b"3", b"s1", b"s2"],
19039            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
19040            &[b"SPOP", b"s1", b"-1"],
19041        ];
19042
19043        let mut one = Fixture::new();
19044        let mut many = Fixture::striped(8);
19045        for parts in script {
19046            let a = one.run(parts);
19047            let b = many.run(parts);
19048            let name = String::from_utf8_lossy(parts[0]).to_uppercase();
19049            if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
19050                assert_eq!(sorted(&a), sorted(&b), "{name}");
19051            } else {
19052                assert_eq!(a, b, "{name}");
19053            }
19054        }
19055    }
19056
19057    /// The algebra over sets that are known to be on different stripes.
19058    #[test]
19059    fn a_set_operation_across_stripes_reads_every_set() {
19060        let mut f = Fixture::striped(8);
19061        let second = apart(&mut f, "s1");
19062        let third = apart(&mut f, &second);
19063        let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
19064
19065        f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
19066        f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
19067        assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
19068        assert_eq!(
19069            sorted(&f.run(&[b"SUNION", s1, s2])),
19070            ["a", "b", "c", "d"],
19071            "a union of two stripes is both of them"
19072        );
19073        assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
19074        assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
19075        assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
19076        assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
19077
19078        // A destination on a third stripe, and then one that is also a source.
19079        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
19080        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
19081        assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
19082        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
19083        assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
19084        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
19085
19086        // An empty result deletes a destination wherever it is, and a key of
19087        // the wrong type stops the command before the destination is touched.
19088        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
19089        assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
19090        f.run(&[b"SET", s3, b"v"]);
19091        assert_eq!(
19092            f.run(&[b"SINTER", s1, s3]),
19093            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19094        );
19095        assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
19096    }
19097
19098    /// An `SMOVE` whose two keys are on two stripes.
19099    #[test]
19100    fn a_move_across_stripes_takes_the_member_with_it() {
19101        let mut f = Fixture::striped(8);
19102        let other = apart(&mut f, "src");
19103        let (src, dst) = (b"src".as_slice(), other.as_bytes());
19104
19105        f.run(&[b"SADD", src, b"a", b"b"]);
19106        f.run(&[b"SADD", dst, b"c"]);
19107        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
19108        assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
19109        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
19110        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
19111
19112        // A destination that is not there is created on its own stripe, and a
19113        // source that loses its last member is deleted from its own.
19114        f.run(&[b"DEL", dst]);
19115        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
19116        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
19117        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
19118
19119        // And a source that is not there answers zero without ever asking what
19120        // the destination holds, which is Redis's order and not the obvious
19121        // one.
19122        f.run(&[b"SET", dst, b"v"]);
19123        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
19124        f.run(&[b"SADD", src, b"b"]);
19125        assert_eq!(
19126            f.run(&[b"SMOVE", src, dst, b"b"]),
19127            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19128        );
19129    }
19130
19131    /// A count and a merge over sketches that are known to be on two stripes.
19132    #[test]
19133    fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
19134        let mut f = Fixture::striped(8);
19135        let other = apart(&mut f, "src");
19136        let (src, far) = (b"src".as_slice(), other.as_bytes());
19137
19138        for i in 0..150 {
19139            let ele = format!("e:{i}");
19140            f.run(&[b"PFADD", src, ele.as_bytes()]);
19141        }
19142        for i in 150..200 {
19143            let ele = format!("e:{i}");
19144            f.run(&[b"PFADD", far, ele.as_bytes()]);
19145        }
19146        // The three numbers a real server gives for these elements, which are
19147        // the numbers the single stripe tests in the keyspace crate check too.
19148        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
19149        assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
19150        assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
19151
19152        // A merge whose destination is on a third stripe, and then one that
19153        // writes into a source.
19154        let dest = apart(&mut f, &other);
19155        assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
19156        assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
19157        assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
19158        assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
19159        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
19160    }
19161
19162    /// Every sorted set command, on one stripe and on eight.
19163    ///
19164    /// Every reply here is compared byte for byte, unlike the set group, because
19165    /// a sorted set answers in rank order and members sharing a score come out
19166    /// in the order of their bytes. There is nothing left for the table the
19167    /// answer was built in to decide.
19168    #[test]
19169    fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
19170        let script: &[&[&[u8]]] = &[
19171            &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
19172            &[b"ZADD", b"z1", b"NX", b"9", b"a"],
19173            &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
19174            &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
19175            &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
19176            &[b"ZINCRBY", b"z1", b"1.5", b"b"],
19177            &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
19178            &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
19179            &[b"ZADD", b"one", b"1", b"m"],
19180            &[b"ZCARD", b"z1"],
19181            &[b"ZCARD", b"gone"],
19182            &[b"ZSCORE", b"z1", b"a"],
19183            &[b"ZSCORE", b"z1", b"zz"],
19184            &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
19185            &[b"ZRANK", b"z1", b"c"],
19186            &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
19187            &[b"ZREVRANK", b"z1", b"c"],
19188            &[b"ZRANK", b"z1", b"gone"],
19189            &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
19190            &[b"ZCOUNT", b"z1", b"(1", b"3"],
19191            &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
19192            // The range commands, which are one parse and one walk.
19193            &[b"ZRANGE", b"z1", b"0", b"-1"],
19194            &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
19195            &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
19196            &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
19197            &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
19198            &[b"ZREVRANGE", b"z1", b"0", b"-1"],
19199            &[
19200                b"ZRANGEBYSCORE",
19201                b"z1",
19202                b"-inf",
19203                b"+inf",
19204                b"LIMIT",
19205                b"1",
19206                b"1",
19207            ],
19208            &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
19209            &[b"ZSCAN", b"z1", b"0"],
19210            &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
19211            // The draw, on a sorted set of one member, which is the only shape
19212            // whose answer two servers have to agree on.
19213            &[b"ZRANDMEMBER", b"one"],
19214            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
19215            &[b"ZRANDMEMBER", b"gone"],
19216            // The one that copies a window into another key.
19217            &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
19218            &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
19219            &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
19220            &[b"EXISTS", b"d0"],
19221            // The algebra, in both its shapes.
19222            &[b"ZUNION", b"2", b"z1", b"z2"],
19223            &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
19224            &[
19225                b"ZUNION",
19226                b"2",
19227                b"z1",
19228                b"z2",
19229                b"WEIGHTS",
19230                b"2",
19231                b"3",
19232                b"AGGREGATE",
19233                b"MAX",
19234                b"WITHSCORES",
19235            ],
19236            &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
19237            &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
19238            &[b"ZDIFF", b"2", b"gone", b"z1"],
19239            &[b"ZINTERCARD", b"2", b"z1", b"z2"],
19240            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
19241            &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
19242            &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
19243            &[
19244                b"ZINTERSTORE",
19245                b"d2",
19246                b"2",
19247                b"z1",
19248                b"z2",
19249                b"AGGREGATE",
19250                b"MIN",
19251            ],
19252            &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
19253            &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
19254            &[b"ZCARD", b"d3"],
19255            // An empty result deletes the destination rather than storing a
19256            // sorted set with nothing in it.
19257            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
19258            &[b"EXISTS", b"d4"],
19259            // A plain set is a sorted set where every score is one, so it is a
19260            // legal input to all of these.
19261            &[b"SADD", b"plain", b"a", b"x"],
19262            &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
19263            &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
19264            // And a destination that is also a source.
19265            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
19266            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
19267            // The three removals and the two pops.
19268            &[b"ZREM", b"d5", b"x", b"nothere"],
19269            &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
19270            &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
19271            &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
19272            &[b"ZPOPMIN", b"z1"],
19273            &[b"ZPOPMAX", b"z1", b"2"],
19274            &[b"ZPOPMIN", b"gone"],
19275            &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
19276            &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
19277            // The errors, which have to be the same errors.
19278            &[b"SET", b"str", b"v"],
19279            &[b"ZADD", b"str", b"1", b"a"],
19280            &[b"ZSCORE", b"str", b"a"],
19281            &[b"ZADD", b"z1", b"nan", b"a"],
19282            &[b"ZUNION", b"2", b"z1", b"str"],
19283            &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
19284            &[b"EXISTS", b"d6"],
19285            &[b"ZINTERCARD", b"0", b"z1"],
19286            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
19287            &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
19288            &[b"ZMPOP", b"1", b"str", b"MIN"],
19289            &[b"ZPOPMIN", b"z1", b"-1"],
19290        ];
19291
19292        let mut one = Fixture::new();
19293        let mut many = Fixture::striped(8);
19294        for parts in script {
19295            let a = one.run(parts);
19296            let b = many.run(parts);
19297            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19298        }
19299    }
19300
19301    /// The algebra over sorted sets that are known to be on different stripes.
19302    #[test]
19303    fn a_sorted_set_operation_across_stripes_reads_every_input() {
19304        let mut f = Fixture::striped(8);
19305        let second = apart(&mut f, "z1");
19306        let third = apart(&mut f, &second);
19307        let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
19308
19309        f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
19310        f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
19311        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
19312        // come out in and the answer that says both stripes were read.
19313        assert_eq!(
19314            f.run(&[b"ZUNION", b"2", z1, z2]),
19315            "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
19316        );
19317        assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
19318        assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
19319        assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
19320        assert_eq!(
19321            f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
19322            ":1\r\n"
19323        );
19324
19325        // A destination on a third stripe, and the weights and the aggregate
19326        // reaching every input.
19327        assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
19328        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
19329        assert_eq!(
19330            f.run(&[
19331                b"ZUNIONSTORE",
19332                z3,
19333                b"2",
19334                z1,
19335                z2,
19336                b"WEIGHTS",
19337                b"2",
19338                b"3",
19339                b"AGGREGATE",
19340                b"MAX"
19341            ]),
19342            ":3\r\n"
19343        );
19344        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
19345        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
19346        assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
19347        assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
19348        assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
19349
19350        // A pop over keys on several stripes takes from the first one that has
19351        // anything, which is what makes the order of the keys matter.
19352        let popped = format!(
19353            "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
19354            second.len()
19355        );
19356        assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
19357        f.run(&[b"ZADD", z2, b"3", b"b"]);
19358
19359        // An empty result deletes a destination wherever it is, and an input of
19360        // the wrong type stops the command before the destination is touched.
19361        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
19362        assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
19363        f.run(&[b"SET", z3, b"v"]);
19364        assert_eq!(
19365            f.run(&[b"ZUNION", b"2", z1, z3]),
19366            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19367        );
19368        assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
19369
19370        // And a destination that is also a source works across stripes for the
19371        // reason it works on one: the whole result is built before anything is
19372        // written.
19373        assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
19374        assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
19375        assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
19376    }
19377
19378    /// A `ZRANGESTORE` whose two keys are on two stripes.
19379    #[test]
19380    fn a_range_store_across_stripes_copies_the_window() {
19381        let mut f = Fixture::striped(8);
19382        let other = apart(&mut f, "src");
19383        let third = apart(&mut f, &other);
19384        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19385
19386        f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
19387        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
19388        assert_eq!(
19389            f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
19390            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
19391        );
19392        assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
19393
19394        // A window walked backwards takes the other end of the sorted set and
19395        // still stores what it took in score order.
19396        assert_eq!(
19397            f.run(&[
19398                b"ZRANGESTORE",
19399                dst,
19400                src,
19401                b"+inf",
19402                b"-inf",
19403                b"BYSCORE",
19404                b"REV",
19405                b"LIMIT",
19406                b"0",
19407                b"2"
19408            ]),
19409            ":2\r\n"
19410        );
19411        assert_eq!(
19412            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19413            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19414        );
19415
19416        // An empty window deletes the destination on its own stripe, and a
19417        // source of the wrong type is refused before the destination is touched.
19418        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
19419        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19420        f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
19421        f.run(&[b"SET", plain, b"v"]);
19422        assert_eq!(
19423            f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
19424            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19425        );
19426        assert_eq!(
19427            f.run(&[b"ZCARD", dst]),
19428            ":3\r\n",
19429            "and left the destination"
19430        );
19431    }
19432
19433    /// Every list command, on one stripe and on eight.
19434    ///
19435    /// The blocking six are in here too, both when they can be answered on the
19436    /// spot and when they cannot, since a command that parks its client writes
19437    /// nothing at all and two servers have to agree about that as much as they
19438    /// agree about a reply.
19439    #[test]
19440    fn the_list_group_answers_the_same_however_many_stripes_there_are() {
19441        let script: &[&[&[u8]]] = &[
19442            &[b"RPUSH", b"l1", b"a", b"b", b"c"],
19443            &[b"LPUSH", b"l1", b"z"],
19444            &[b"RPUSHX", b"l1", b"d"],
19445            &[b"LPUSHX", b"gone", b"x"],
19446            &[b"RPUSHX", b"gone", b"x"],
19447            &[b"LLEN", b"l1"],
19448            &[b"LLEN", b"gone"],
19449            &[b"LRANGE", b"l1", b"0", b"-1"],
19450            &[b"LRANGE", b"l1", b"1", b"2"],
19451            &[b"LRANGE", b"l1", b"5", b"9"],
19452            &[b"LINDEX", b"l1", b"0"],
19453            &[b"LINDEX", b"l1", b"-1"],
19454            &[b"LINDEX", b"l1", b"99"],
19455            &[b"LSET", b"l1", b"0", b"y"],
19456            &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
19457            &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
19458            &[b"LPOS", b"l1", b"b"],
19459            &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
19460            &[b"LPOS", b"l1", b"nothere"],
19461            &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
19462            &[b"LREM", b"l1", b"1", b"aa"],
19463            &[b"LTRIM", b"l1", b"0", b"3"],
19464            &[b"LRANGE", b"l1", b"0", b"-1"],
19465            &[b"LPOP", b"l1"],
19466            &[b"RPOP", b"l1"],
19467            &[b"LPOP", b"l1", b"2"],
19468            &[b"LPOP", b"gone"],
19469            &[b"LPOP", b"gone", b"2"],
19470            &[b"EXISTS", b"l1"],
19471            // The ones that name two keys, and the one that takes a block of
19472            // elements rather than the one on the end.
19473            &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
19474            &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
19475            &[b"RPOPLPUSH", b"src", b"dst"],
19476            &[b"LRANGE", b"dst", b"0", b"-1"],
19477            &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
19478            &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
19479            &[
19480                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19481            ],
19482            &[
19483                b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19484            ],
19485            &[b"LRANGE", b"dst", b"0", b"-1"],
19486            &[
19487                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19488            ],
19489            &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19490            &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19491            &[b"LMPOP", b"1", b"gone", b"LEFT"],
19492            // The blocking ones, first with something there to answer them and
19493            // then with nothing, which parks the client and writes nothing.
19494            &[b"RPUSH", b"q", b"a", b"b", b"c"],
19495            &[b"BLPOP", b"gone", b"q", b"0"],
19496            &[b"BRPOP", b"q", b"0"],
19497            &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19498            &[b"RPUSH", b"q", b"x", b"y", b"z"],
19499            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19500            &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19501            &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19502            &[b"BLPOP", b"q", b"0"],
19503            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19504            // The errors, which have to be the same errors.
19505            &[b"SET", b"plain", b"v"],
19506            &[b"LPUSH", b"plain", b"a"],
19507            &[b"LLEN", b"plain"],
19508            &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19509            &[b"LRANGE", b"dst", b"0", b"-1"],
19510            &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19511            &[b"LSET", b"gone", b"0", b"v"],
19512            &[b"LSET", b"dst", b"99", b"v"],
19513            &[b"LPOP", b"dst", b"-1"],
19514            &[b"LMPOP", b"0", b"dst", b"LEFT"],
19515            &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19516        ];
19517
19518        let mut one = Fixture::new();
19519        let mut many = Fixture::striped(8);
19520        for parts in script {
19521            let a = one.run(parts);
19522            let b = many.run(parts);
19523            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19524        }
19525    }
19526
19527    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
19528    #[test]
19529    fn a_list_move_across_stripes_takes_the_elements_with_it() {
19530        let mut f = Fixture::striped(8);
19531        let other = apart(&mut f, "src");
19532        let third = apart(&mut f, &other);
19533        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19534
19535        f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19536        assert_eq!(
19537            f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19538            "$1\r\na\r\n"
19539        );
19540        assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19541        assert_eq!(
19542            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19543            "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19544            "one went on each end of the destination"
19545        );
19546        assert_eq!(
19547            f.run(&[b"LRANGE", src, b"0", b"-1"]),
19548            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19549        );
19550
19551        // A block of them, which under BULK arrives in the order it left.
19552        assert_eq!(
19553            f.run(&[
19554                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19555            ]),
19556            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19557        );
19558        assert_eq!(
19559            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19560            "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19561        );
19562        assert_eq!(
19563            f.run(&[b"EXISTS", src]),
19564            ":0\r\n",
19565            "and the source is gone with its last element"
19566        );
19567
19568        // An `EXACTLY` the source cannot fill moves nothing, and a source that
19569        // is not there at all is the two kinds of nothing the two commands have.
19570        f.run(&[b"RPUSH", src, b"e", b"f"]);
19571        assert_eq!(
19572            f.run(&[
19573                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19574            ]),
19575            "*-1\r\n"
19576        );
19577        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19578        assert_eq!(
19579            f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19580            "$-1\r\n"
19581        );
19582        assert_eq!(
19583            f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19584            "*-1\r\n"
19585        );
19586
19587        // A destination of the wrong type is refused before anything is taken,
19588        // which is the order that matters most here, since an element already
19589        // out of the source would have nowhere to go back to.
19590        f.run(&[b"SET", plain, b"v"]);
19591        assert_eq!(
19592            f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19593            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19594        );
19595        assert_eq!(
19596            f.run(&[b"LLEN", src]),
19597            ":2\r\n",
19598            "and left the source alone"
19599        );
19600        assert_eq!(
19601            f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19602            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19603        );
19604        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19605    }
19606
19607    /// A parked client served by a push that landed on another stripe.
19608    ///
19609    /// A waiter remembers the database and not the stripe, which is the point:
19610    /// serving it runs the same attempt the command ran, and the attempt finds
19611    /// the stripe each of its keys is on for itself.
19612    #[test]
19613    fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19614        let mut f = Fixture::striped(8);
19615        let other = apart(&mut f, "q");
19616        let (q, far) = (b"q".as_slice(), other.as_bytes());
19617
19618        assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19619        assert_eq!(f.server.parked(), 1);
19620        f.run(&[b"RPUSH", far, b"v"]);
19621        let mut out = Out::new(Proto::Resp2);
19622        assert!(f.server.serve_waiter(0, 0, &mut out));
19623        let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19624        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19625        assert_eq!(
19626            f.run(&[b"EXISTS", far]),
19627            ":0\r\n",
19628            "and it took the element with it"
19629        );
19630
19631        // And a move across two stripes is served the same way, by the push
19632        // that fills its source.
19633        f.server.forget_waiters(7);
19634        assert_eq!(
19635            f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19636            Flow::Block
19637        );
19638        f.run(&[b"RPUSH", q, b"w"]);
19639        let mut out = Out::new(Proto::Resp2);
19640        assert!(f.server.serve_waiter(0, 0, &mut out));
19641        assert_eq!(
19642            core::str::from_utf8(out.as_slice()).expect("ascii"),
19643            "$1\r\nw\r\n"
19644        );
19645        assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19646    }
19647
19648    /// Every stream command, on one stripe and on eight.
19649    ///
19650    /// Every ID is written out rather than left to the clock, so the two servers
19651    /// are being compared on what they store and not on how long the test took
19652    /// to get from one of them to the other.
19653    #[test]
19654    fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19655        let script: &[&[&[u8]]] = &[
19656            &[b"XADD", b"s", b"1-1", b"a", b"1"],
19657            &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19658            &[b"XADD", b"s", b"3-1", b"d", b"4"],
19659            &[b"XADD", b"s", b"1-1", b"e", b"5"],
19660            &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19661            &[b"XLEN", b"s"],
19662            &[b"XLEN", b"gone"],
19663            &[b"XRANGE", b"s", b"-", b"+"],
19664            &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19665            &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19666            &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19667            &[b"XREVRANGE", b"s", b"+", b"-"],
19668            &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19669            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19670            &[b"XREAD", b"STREAMS", b"s", b"$"],
19671            // The groups, which is where most of the state is.
19672            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19673            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19674            &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19675            &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19676            &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19677            &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19678            &[
19679                b"XREADGROUP",
19680                b"GROUP",
19681                b"g",
19682                b"c1",
19683                b"COUNT",
19684                b"1",
19685                b"STREAMS",
19686                b"s",
19687                b"0",
19688            ],
19689            &[
19690                b"XREADGROUP",
19691                b"GROUP",
19692                b"nope",
19693                b"c1",
19694                b"STREAMS",
19695                b"s",
19696                b">",
19697            ],
19698            &[b"XPENDING", b"s", b"g"],
19699            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19700            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19701            &[b"XPENDING", b"s", b"nope"],
19702            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19703            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19704            &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19705            &[b"XACK", b"s", b"g", b"1-1"],
19706            &[b"XACK", b"s", b"g", b"1-1"],
19707            &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19708            &[b"XPENDING", b"s", b"g"],
19709            &[b"XINFO", b"STREAM", b"s"],
19710            &[b"XINFO", b"GROUPS", b"s"],
19711            &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19712            &[b"XINFO", b"STREAM", b"gone"],
19713            // Deleting, trimming and moving the ID on.
19714            &[b"XDEL", b"s", b"3-1"],
19715            &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19716            &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19717            &[b"XADD", b"s", b"9-1", b"z", b"9"],
19718            &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19719            &[b"XTRIM", b"s", b"MINID", b"9"],
19720            &[b"XSETID", b"s", b"99-1"],
19721            &[b"XSETID", b"s", b"1-1"],
19722            &[b"XLEN", b"s"],
19723            &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19724            &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19725            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19726            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19727            // And the errors.
19728            &[b"SET", b"plain", b"v"],
19729            &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19730            &[b"XLEN", b"plain"],
19731            &[b"XREAD", b"STREAMS", b"plain", b"0"],
19732            &[b"XRANGE", b"s", b"bogus", b"+"],
19733            &[b"XADD", b"s", b"1-1", b"a"],
19734            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19735            &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19736        ];
19737
19738        let mut one = Fixture::new();
19739        let mut many = Fixture::striped(8);
19740        for parts in script {
19741            let a = one.run(parts);
19742            let b = many.run(parts);
19743            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19744        }
19745    }
19746
19747    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
19748    ///
19749    /// Nothing is shared between the two streams, so the only thing this can go
19750    /// wrong at is looking both of them up, which is exactly what a read that
19751    /// held one database and walked it would get wrong.
19752    #[test]
19753    fn a_stream_read_across_stripes_reads_every_key() {
19754        let mut f = Fixture::striped(8);
19755        let other = apart(&mut f, "s1");
19756        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19757
19758        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19759        f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19760        let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19761        assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19762        assert!(got.contains("1-1"), "the first one is in there: {got}");
19763        assert!(got.contains("2-1"), "and so is the second: {got}");
19764
19765        // A group read looks its group up on every key before it reads any of
19766        // them, so a group that is missing on the far key stops the near one.
19767        f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19768        let got = f.run(&[
19769            b"XREADGROUP",
19770            b"GROUP",
19771            b"g",
19772            b"c",
19773            b"STREAMS",
19774            s1,
19775            s2,
19776            b">",
19777            b">",
19778        ]);
19779        assert!(got.starts_with("-NOGROUP"), "{got}");
19780        assert_eq!(
19781            f.run(&[b"XPENDING", s1, b"g"]),
19782            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19783            "and read nothing from the key that did have the group"
19784        );
19785
19786        f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19787        let got = f.run(&[
19788            b"XREADGROUP",
19789            b"GROUP",
19790            b"g",
19791            b"c",
19792            b"STREAMS",
19793            s1,
19794            s2,
19795            b">",
19796            b">",
19797        ]);
19798        assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19799    }
19800
19801    /// A client parked on an `XREAD` woken by an entry on another stripe.
19802    #[test]
19803    fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19804        let mut f = Fixture::striped(8);
19805        let other = apart(&mut f, "s1");
19806        let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19807        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19808        f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19809
19810        assert_eq!(
19811            f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19812                .0,
19813            Flow::Block
19814        );
19815        f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19816        let mut out = Out::new(Proto::Resp2);
19817        assert!(f.server.serve_waiter(0, 0, &mut out));
19818        let want = format!(
19819            "*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",
19820            other.len()
19821        );
19822        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19823    }
19824
19825    /// Every JSON command, on one stripe and on eight.
19826    #[test]
19827    fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19828        let script: &[&[&[u8]]] = &[
19829            &[
19830                b"JSON.SET",
19831                b"d",
19832                b"$",
19833                br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19834            ],
19835            &[b"JSON.SET", b"d", b"$.a", b"2"],
19836            &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19837            &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19838            &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19839            &[b"JSON.GET", b"d"],
19840            &[b"JSON.GET", b"d", b"$.b"],
19841            &[b"JSON.GET", b"gone", b"$"],
19842            &[b"JSON.TYPE", b"d", b"$.b"],
19843            &[b"JSON.TYPE", b"d", b"$.s"],
19844            &[b"JSON.TOGGLE", b"d", b"$.t"],
19845            &[b"JSON.ARRLEN", b"d", b"$.b"],
19846            &[b"JSON.OBJLEN", b"d", b"$"],
19847            &[b"JSON.OBJKEYS", b"d", b"$"],
19848            &[b"JSON.STRLEN", b"d", b"$.s"],
19849            &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19850            &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19851            &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19852            &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19853            &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19854            &[b"JSON.ARRPOP", b"d", b"$.b"],
19855            &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19856            &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19857            &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19858            &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19859            &[b"JSON.RESP", b"d", b"$.b"],
19860            &[b"JSON.DEBUG", b"MEMORY", b"d"],
19861            &[b"JSON.CLEAR", b"d", b"$.b"],
19862            &[b"JSON.DEL", b"d", b"$.m"],
19863            &[b"JSON.FORGET", b"d", b"$.nothere"],
19864            // The two that name more than one key.
19865            &[
19866                b"JSON.MSET",
19867                b"m1",
19868                b"$",
19869                b"1",
19870                b"m2",
19871                b"$",
19872                b"2",
19873                b"m3",
19874                b"$",
19875                b"3",
19876            ],
19877            &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19878            &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19879            &[b"JSON.GET", b"m1", b"$"],
19880            &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19881            &[b"JSON.GET", b"m2", b"$"],
19882            // And the errors.
19883            &[b"SET", b"plain", b"v"],
19884            &[b"JSON.GET", b"plain", b"$"],
19885            &[b"JSON.SET", b"plain", b"$", b"1"],
19886            &[b"JSON.MGET", b"m1", b"plain", b"$"],
19887            &[b"JSON.SET", b"d", b"$.b", b"["],
19888            &[b"JSON.DEL", b"plain"],
19889        ];
19890
19891        let mut one = Fixture::new();
19892        let mut many = Fixture::striped(8);
19893        for parts in script {
19894            let a = one.run(parts);
19895            let b = many.run(parts);
19896            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19897        }
19898    }
19899
19900    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
19901    ///
19902    /// `JSON.MSET` works every triple out against the keyspace as it was before
19903    /// the command and writes nothing until all of them are known to work, so
19904    /// the thing to check is that a triple that cannot be written stops the
19905    /// ones on other stripes as well as the ones on its own.
19906    #[test]
19907    fn a_json_multi_write_across_stripes_reaches_every_key() {
19908        let mut f = Fixture::striped(8);
19909        let second = apart(&mut f, "m1");
19910        let third = apart(&mut f, &second);
19911        let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19912
19913        assert_eq!(
19914            f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19915            "+OK\r\n"
19916        );
19917        assert_eq!(
19918            f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19919            "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19920        );
19921
19922        // A value that is not JSON is refused before anything is written, and
19923        // the key on the far stripe keeps what it had.
19924        assert_eq!(
19925            f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19926            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19927        );
19928        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19929
19930        // A path that names nowhere is not an error. That triple is skipped,
19931        // the ones on the other stripes are still written, and the reply is a
19932        // nil rather than OK.
19933        assert_eq!(
19934            f.run(&[
19935                b"JSON.MSET",
19936                m1,
19937                b"$",
19938                b"9",
19939                m2,
19940                b"$.deep",
19941                b"9",
19942                m3,
19943                b"$",
19944                b"7"
19945            ]),
19946            "$-1\r\n"
19947        );
19948        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19949        assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19950        assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19951    }
19952
19953    /// Every geospatial command, on one stripe and on eight.
19954    #[test]
19955    fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19956        let script: &[&[&[u8]]] = &[
19957            &[
19958                b"GEOADD",
19959                b"g",
19960                b"13.361389",
19961                b"38.115556",
19962                b"palermo",
19963                b"15.087269",
19964                b"37.502669",
19965                b"catania",
19966            ],
19967            &[
19968                b"GEOADD",
19969                b"g",
19970                b"NX",
19971                b"13.361389",
19972                b"38.115556",
19973                b"palermo",
19974            ],
19975            &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19976            &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19977            &[b"GEOHASH", b"g", b"palermo", b"catania"],
19978            &[b"GEODIST", b"g", b"palermo", b"catania"],
19979            &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19980            &[b"GEODIST", b"g", b"palermo", b"nothere"],
19981            &[
19982                b"GEOSEARCH",
19983                b"g",
19984                b"FROMLONLAT",
19985                b"15",
19986                b"37",
19987                b"BYRADIUS",
19988                b"200",
19989                b"KM",
19990                b"ASC",
19991                b"WITHCOORD",
19992                b"WITHDIST",
19993                b"WITHHASH",
19994            ],
19995            &[
19996                b"GEOSEARCH",
19997                b"g",
19998                b"FROMMEMBER",
19999                b"palermo",
20000                b"BYBOX",
20001                b"400",
20002                b"400",
20003                b"KM",
20004                b"DESC",
20005            ],
20006            &[
20007                b"GEORADIUS",
20008                b"g",
20009                b"15",
20010                b"37",
20011                b"200",
20012                b"KM",
20013                b"COUNT",
20014                b"1",
20015            ],
20016            &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
20017            &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
20018            &[
20019                b"GEOSEARCHSTORE",
20020                b"dst",
20021                b"g",
20022                b"FROMLONLAT",
20023                b"15",
20024                b"37",
20025                b"BYRADIUS",
20026                b"200",
20027                b"KM",
20028            ],
20029            &[b"ZRANGE", b"dst", b"0", b"-1"],
20030            &[
20031                b"GEOSEARCHSTORE",
20032                b"dst",
20033                b"g",
20034                b"FROMLONLAT",
20035                b"15",
20036                b"37",
20037                b"BYRADIUS",
20038                b"1",
20039                b"M",
20040                b"STOREDIST",
20041            ],
20042            &[b"EXISTS", b"dst"],
20043            &[
20044                b"GEORADIUS",
20045                b"g",
20046                b"15",
20047                b"37",
20048                b"200",
20049                b"KM",
20050                b"STORE",
20051                b"dst",
20052            ],
20053            &[b"ZCARD", b"dst"],
20054            // And the errors.
20055            &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
20056            &[b"SET", b"plain", b"v"],
20057            &[b"GEOPOS", b"plain", b"a"],
20058            &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
20059            &[
20060                b"GEOSEARCHSTORE",
20061                b"dst",
20062                b"g",
20063                b"FROMLONLAT",
20064                b"15",
20065                b"37",
20066                b"BYRADIUS",
20067                b"200",
20068                b"KM",
20069                b"WITHCOORD",
20070            ],
20071        ];
20072
20073        let mut one = Fixture::new();
20074        let mut many = Fixture::striped(8);
20075        for parts in script {
20076            let a = one.run(parts);
20077            let b = many.run(parts);
20078            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20079        }
20080    }
20081
20082    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
20083    #[test]
20084    fn a_geo_search_store_across_stripes_writes_what_it_found() {
20085        let mut f = Fixture::striped(8);
20086        let other = apart(&mut f, "g");
20087        let third = apart(&mut f, &other);
20088        let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
20089
20090        f.run(&[
20091            b"GEOADD",
20092            g,
20093            b"13.361389",
20094            b"38.115556",
20095            b"palermo",
20096            b"15.087269",
20097            b"37.502669",
20098            b"catania",
20099        ]);
20100        assert_eq!(
20101            f.run(&[
20102                b"GEOSEARCHSTORE",
20103                dst,
20104                g,
20105                b"FROMLONLAT",
20106                b"15",
20107                b"37",
20108                b"BYRADIUS",
20109                b"200",
20110                b"KM",
20111                b"ASC",
20112            ]),
20113            ":2\r\n"
20114        );
20115        assert_eq!(
20116            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
20117            "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
20118            "the geohash is the score, so the order is not the search order"
20119        );
20120        assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
20121
20122        // `STOREDIST` stores the distance in the unit the search was asked in,
20123        // which is the destination stripe's sorted set and not the source's.
20124        assert_eq!(
20125            f.run(&[
20126                b"GEOSEARCHSTORE",
20127                dst,
20128                g,
20129                b"FROMMEMBER",
20130                b"palermo",
20131                b"BYRADIUS",
20132                b"200",
20133                b"KM",
20134                b"STOREDIST",
20135            ]),
20136            ":2\r\n"
20137        );
20138        assert_eq!(
20139            f.run(&[b"ZSCORE", dst, b"palermo"]),
20140            "$1\r\n0\r\n",
20141            "the centre is nought away from itself"
20142        );
20143
20144        // A search that found nothing deletes the destination on its own
20145        // stripe, and a source of the wrong type is refused with the
20146        // destination left alone.
20147        assert_eq!(
20148            f.run(&[
20149                b"GEOSEARCHSTORE",
20150                dst,
20151                g,
20152                b"FROMLONLAT",
20153                b"0",
20154                b"0",
20155                b"BYRADIUS",
20156                b"1",
20157                b"M",
20158            ]),
20159            ":0\r\n"
20160        );
20161        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
20162        f.run(&[
20163            b"GEOSEARCHSTORE",
20164            dst,
20165            g,
20166            b"FROMLONLAT",
20167            b"15",
20168            b"37",
20169            b"BYRADIUS",
20170            b"200",
20171            b"KM",
20172        ]);
20173        f.run(&[b"SET", plain, b"v"]);
20174        assert_eq!(
20175            f.run(&[
20176                b"GEOSEARCHSTORE",
20177                dst,
20178                plain,
20179                b"FROMLONLAT",
20180                b"15",
20181                b"37",
20182                b"BYRADIUS",
20183                b"200",
20184                b"KM",
20185            ]),
20186            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
20187        );
20188        assert_eq!(
20189            f.run(&[b"ZCARD", dst]),
20190            ":2\r\n",
20191            "and left the destination"
20192        );
20193    }
20194
20195    /// Every time series command, on one stripe and on eight.
20196    ///
20197    /// Every timestamp is written out rather than left to the clock, so the two
20198    /// servers are compared on the samples they hold and not on how long the
20199    /// test took to get from one of them to the other.
20200    #[test]
20201    fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
20202        let script: &[&[&[u8]]] = &[
20203            &[
20204                b"TS.CREATE",
20205                b"ts:a",
20206                b"LABELS",
20207                b"sensor",
20208                b"a",
20209                b"room",
20210                b"1",
20211            ],
20212            &[b"TS.CREATE", b"ts:a"],
20213            &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
20214            &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
20215            &[
20216                b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
20217            ],
20218            &[
20219                b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
20220            ],
20221            &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
20222            &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
20223            &[b"TS.GET", b"ts:a"],
20224            &[b"TS.GET", b"gone"],
20225            &[b"TS.RANGE", b"ts:a", b"-", b"+"],
20226            &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
20227            &[
20228                b"TS.RANGE",
20229                b"ts:a",
20230                b"-",
20231                b"+",
20232                b"AGGREGATION",
20233                b"avg",
20234                b"2000",
20235            ],
20236            &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
20237            &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20238            &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20239            &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
20240            &[b"TS.READ", b"ts:a", b"0"],
20241            &[b"TS.READ", b"ts:a", b"+"],
20242            // The filters, which are the ones that have to walk every stripe.
20243            &[b"TS.QUERYINDEX", b"sensor=a"],
20244            &[b"TS.QUERYINDEX", b"room=1"],
20245            &[b"TS.QUERYINDEX", b"room=9"],
20246            &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
20247            &[
20248                b"TS.QUERYLABELS",
20249                b"VALUES",
20250                b"sensor",
20251                b"FILTER",
20252                b"room=1",
20253            ],
20254            &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
20255            &[
20256                b"TS.MGET",
20257                b"SELECTED_LABELS",
20258                b"sensor",
20259                b"FILTER",
20260                b"sensor=a",
20261            ],
20262            &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
20263            &[
20264                b"TS.MREVRANGE",
20265                b"-",
20266                b"+",
20267                b"WITHLABELS",
20268                b"FILTER",
20269                b"sensor=a",
20270            ],
20271            &[
20272                b"TS.MRANGE",
20273                b"-",
20274                b"+",
20275                b"FILTER",
20276                b"room=1",
20277                b"GROUPBY",
20278                b"room",
20279                b"REDUCE",
20280                b"max",
20281            ],
20282            &[b"TS.INFO", b"ts:a"],
20283            // And a rule, which is the one thing here that names two keys.
20284            &[
20285                b"TS.CREATERULE",
20286                b"ts:a",
20287                b"ts:down",
20288                b"AGGREGATION",
20289                b"avg",
20290                b"1000",
20291            ],
20292            &[b"TS.CREATE", b"ts:down"],
20293            &[
20294                b"TS.CREATERULE",
20295                b"ts:a",
20296                b"ts:down",
20297                b"AGGREGATION",
20298                b"avg",
20299                b"1000",
20300            ],
20301            &[b"TS.ADD", b"ts:a", b"5000", b"4"],
20302            &[b"TS.ADD", b"ts:a", b"6000", b"5"],
20303            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20304            &[b"TS.GET", b"ts:down", b"LATEST"],
20305            &[b"TS.INFO", b"ts:down"],
20306            &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
20307            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20308            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20309            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20310            &[b"TS.DEL", b"ts:a", b"0", b"1000"],
20311            // And the errors.
20312            &[b"SET", b"plain", b"v"],
20313            &[b"TS.ADD", b"plain", b"1", b"1"],
20314            &[b"TS.GET", b"plain"],
20315            &[b"TS.READ", b"plain", b"0"],
20316            &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
20317            &[b"TS.RANGE", b"gone", b"-", b"+"],
20318            &[b"TS.INFO", b"gone"],
20319        ];
20320
20321        let mut one = Fixture::new();
20322        let mut many = Fixture::striped(8);
20323        for parts in script {
20324            let a = one.run(parts);
20325            let b = many.run(parts);
20326            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20327        }
20328    }
20329
20330    /// A compaction rule whose two ends are on two stripes.
20331    ///
20332    /// This is the one thing in the family that walks from a key to another key,
20333    /// and it walks it in both directions: a sample on the source closes a
20334    /// bucket on the destination, a `LATEST` read on the destination folds the
20335    /// bucket the source is still filling, and a delete on the source rewrites
20336    /// what the destination already held. The same script is run against a
20337    /// server one stripe wide, where the two keys share a store, and against one
20338    /// eight stripes wide, where they do not.
20339    #[test]
20340    fn a_compaction_rule_across_stripes_reaches_both_ends() {
20341        let mut many = Fixture::striped(8);
20342        let other = apart(&mut many, "src");
20343        let (src, dst) = (b"src".as_slice(), other.as_bytes());
20344        let mut one = Fixture::new();
20345        let mut both = |parts: &[&[u8]]| {
20346            let a = one.run(parts);
20347            let b = many.run(parts);
20348            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20349            a
20350        };
20351
20352        both(&[b"TS.CREATE", src]);
20353        both(&[b"TS.CREATE", dst]);
20354        assert_eq!(
20355            both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
20356            "+OK\r\n"
20357        );
20358        both(&[b"TS.ADD", src, b"1000", b"1"]);
20359        both(&[b"TS.ADD", src, b"1500", b"3"]);
20360        // The bucket the source is filling is not written down yet, and asking
20361        // for it works it out off the source.
20362        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20363        let open = both(&[b"TS.GET", dst, b"LATEST"]);
20364        assert!(open.contains(":1000"), "the open bucket is folded: {open}");
20365
20366        // A sample past the bucket closes it, which is the write that has to
20367        // land on the other stripe.
20368        both(&[b"TS.ADD", src, b"2000", b"5"]);
20369        let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
20370        assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
20371        assert!(got.contains(":1000"), "{got}");
20372
20373        // And a delete on the source takes it away again.
20374        both(&[b"TS.DEL", src, b"1000", b"1999"]);
20375        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20376
20377        // Both ends still know about each other, and the link comes apart from
20378        // the source.
20379        assert!(
20380            both(&[b"TS.INFO", dst]).contains("src"),
20381            "the source is named"
20382        );
20383        assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
20384        assert_eq!(
20385            both(&[b"TS.DELETERULE", src, dst]),
20386            "-ERR TSDB: compaction rule does not exist\r\n"
20387        );
20388    }
20389
20390    /// A label filter takes the series it names wherever they landed.
20391    #[test]
20392    fn a_label_query_across_stripes_finds_every_series() {
20393        let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
20394        let mut many = Fixture::striped(8);
20395        let mut homes: Vec<usize> = names
20396            .iter()
20397            .map(|name| many.server.striped(0).stripe_of(name))
20398            .collect();
20399        homes.sort_unstable();
20400        homes.dedup();
20401        assert!(homes.len() > 1, "the six keys are not all on one stripe");
20402
20403        let mut one = Fixture::new();
20404        let mut both = |parts: &[&[u8]]| {
20405            let a = one.run(parts);
20406            let b = many.run(parts);
20407            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20408            a
20409        };
20410        for name in &names {
20411            both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
20412            both(&[b"TS.ADD", name, b"1000", b"1"]);
20413        }
20414
20415        let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
20416        assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
20417        assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20418        assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20419        assert_eq!(
20420            both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
20421            "*1\r\n$4\r\nroom\r\n"
20422        );
20423    }
20424
20425    /// Every hash command, and the field import beside it, on one stripe and on
20426    /// eight.
20427    ///
20428    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
20429    /// stripes do not draw the same numbers, so the only draw here is off a hash
20430    /// holding one field, where every generator gives the same answer.
20431    #[test]
20432    fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
20433        let script: &[&[&[u8]]] = &[
20434            &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
20435            &[b"HMSET", b"h", b"c", b"3"],
20436            &[b"HSETNX", b"h", b"a", b"9"],
20437            &[b"HSETNX", b"h", b"d", b"4"],
20438            &[b"HGET", b"h", b"a"],
20439            &[b"HGET", b"h", b"nope"],
20440            &[b"HMGET", b"h", b"a", b"nope"],
20441            &[b"HLEN", b"h"],
20442            &[b"HEXISTS", b"h", b"a"],
20443            &[b"HSTRLEN", b"h", b"a"],
20444            &[b"HGETALL", b"h"],
20445            &[b"HKEYS", b"h"],
20446            &[b"HVALS", b"h"],
20447            &[b"HINCRBY", b"h", b"a", b"5"],
20448            &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
20449            &[b"HSCAN", b"h", b"0"],
20450            &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
20451            &[b"HSCAN", b"h", b"0", b"NOVALUES"],
20452            &[b"HDEL", b"h", b"d"],
20453            &[b"HSET", b"one", b"f", b"v"],
20454            &[b"HRANDFIELD", b"one"],
20455            &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
20456            // The field deadlines.
20457            &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
20458            &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
20459            &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
20460            &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20461            &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20462            &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
20463            &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
20464            &[b"HGET", b"h", b"b"],
20465            // The three that came later and word everything their own way.
20466            &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
20467            &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
20468            &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
20469            &[b"HGET", b"h", b"e"],
20470            // And the import, whose key is the third word.
20471            &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
20472            &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
20473            &[b"HGETALL", b"imp"],
20474            &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
20475            &[b"HIMPORT", b"DISCARD", b"fs"],
20476            // And the errors.
20477            &[b"SET", b"plain", b"v"],
20478            &[b"HSET", b"plain", b"a", b"1"],
20479            &[b"HGETALL", b"plain"],
20480            &[b"HGET", b"gone", b"a"],
20481            &[b"HINCRBY", b"h", b"a", b"nan"],
20482        ];
20483
20484        let mut one = Fixture::new();
20485        let mut many = Fixture::striped(8);
20486        // The field deadlines are absolute milliseconds worked out from the
20487        // clock, so both servers are put on the same one rather than left to
20488        // read the wall a moment apart.
20489        one.server.set_clock_ms(1_700_000_000_000);
20490        many.server.set_clock_ms(1_700_000_000_000);
20491        for parts in script {
20492            let a = one.run(parts);
20493            let b = many.run(parts);
20494            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20495        }
20496    }
20497
20498    /// Every array command, on one stripe and on eight.
20499    #[test]
20500    fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20501        let script: &[&[&[u8]]] = &[
20502            &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20503            &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20504            &[b"ARGET", b"a", b"1"],
20505            &[b"ARGET", b"a", b"99"],
20506            &[b"ARMGET", b"a", b"0", b"5", b"99"],
20507            &[b"ARGETRANGE", b"a", b"0", b"7"],
20508            &[b"ARLEN", b"a"],
20509            &[b"ARCOUNT", b"a"],
20510            &[b"ARINSERT", b"a", b"m", b"n"],
20511            &[b"ARSCAN", b"a", b"0", b"20"],
20512            &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20513            &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20514            &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20515            &[b"ARLASTITEMS", b"a", b"2"],
20516            &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20517            &[b"ARNEXT", b"a"],
20518            &[b"ARSEEK", b"a", b"3"],
20519            &[b"AROP", b"a", b"0", b"20", b"USED"],
20520            &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20521            &[b"ARINFO", b"a"],
20522            &[b"ARINFO", b"a", b"FULL"],
20523            &[b"ARDEL", b"a", b"0"],
20524            &[b"ARDELRANGE", b"a", b"1", b"2"],
20525            &[b"ARCOUNT", b"a"],
20526            &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20527            &[b"ARGETRANGE", b"r", b"0", b"9"],
20528            // And the errors.
20529            &[b"SET", b"plain", b"v"],
20530            &[b"ARGET", b"plain", b"0"],
20531            &[b"ARSET", b"plain", b"0", b"v"],
20532            &[b"ARGET", b"gone", b"0"],
20533            &[b"ARSET", b"a", b"bad", b"v"],
20534        ];
20535
20536        let mut one = Fixture::new();
20537        let mut many = Fixture::striped(8);
20538        for parts in script {
20539            let a = one.run(parts);
20540            let b = many.run(parts);
20541            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20542        }
20543    }
20544
20545    /// Every graph and vector set command, on one stripe and on eight.
20546    ///
20547    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
20548    /// not: it draws from the stripe's generator, and the stripes do not share
20549    /// one.
20550    #[test]
20551    fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20552        let script: &[&[&[u8]]] = &[
20553            &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20554            &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20555            &[b"G.NADD", b"g", b"n3"],
20556            &[b"G.NGET", b"g", b"n1"],
20557            &[b"G.NGET", b"g", b"gone"],
20558            &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20559            &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20560            &[b"G.OUT", b"g", b"n1", b"knows"],
20561            &[b"G.IN", b"g", b"n2", b"knows"],
20562            &[b"G.DEG", b"g", b"n1", b"knows"],
20563            &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20564            &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20565            &[b"G.PATH", b"g", b"n1", b"n3"],
20566            &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20567            &[b"G.NDEL", b"g", b"n3"],
20568            &[b"G.NGET", b"g", b"n3"],
20569            // The vector set, which is one index under one key.
20570            &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20571            &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20572            &[b"VCARD", b"v"],
20573            &[b"VDIM", b"v"],
20574            &[b"VEMB", b"v", b"e1"],
20575            &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20576            &[b"VSIM", b"v", b"ELE", b"e1"],
20577            &[b"VISMEMBER", b"v", b"e1"],
20578            &[b"VISMEMBER", b"v", b"gone"],
20579            &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20580            &[b"VGETATTR", b"v", b"e1"],
20581            &[b"VRANGE", b"v", b"-", b"+"],
20582            &[b"VLINKS", b"v", b"e1"],
20583            &[b"VINFO", b"v"],
20584            &[b"VREM", b"v", b"e2"],
20585            &[b"VCARD", b"v"],
20586            // And the errors.
20587            &[b"SET", b"plain", b"v"],
20588            &[b"G.NGET", b"plain", b"n1"],
20589            &[b"VCARD", b"plain"],
20590            &[b"G.NADD", b"gone2", b"n"],
20591            &[b"VEMB", b"gone3", b"e"],
20592        ];
20593
20594        let mut one = Fixture::new();
20595        let mut many = Fixture::striped(8);
20596        for parts in script {
20597            let a = one.run(parts);
20598            let b = many.run(parts);
20599            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20600        }
20601    }
20602
20603    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
20604    /// command, on one stripe and on eight.
20605    #[test]
20606    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20607        let script: &[&[&[u8]]] = &[
20608            // The bloom filter.
20609            &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20610            &[b"BF.ADD", b"bf", b"a"],
20611            &[b"BF.ADD", b"bf", b"a"],
20612            &[b"BF.MADD", b"bf", b"b", b"c"],
20613            &[b"BF.EXISTS", b"bf", b"a"],
20614            &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20615            &[b"BF.CARD", b"bf"],
20616            &[b"BF.INFO", b"bf"],
20617            &[b"BF.INFO", b"bf", b"CAPACITY"],
20618            &[b"BF.DEBUG", b"bf"],
20619            &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20620            &[b"BF.EXISTS", b"made", b"x"],
20621            &[b"BF.SCANDUMP", b"bf", b"0"],
20622            // The cuckoo filter.
20623            &[b"CF.RESERVE", b"cf", b"100"],
20624            &[b"CF.ADD", b"cf", b"a"],
20625            &[b"CF.ADDNX", b"cf", b"a"],
20626            &[b"CF.COUNT", b"cf", b"a"],
20627            &[b"CF.EXISTS", b"cf", b"a"],
20628            &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20629            &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20630            &[b"CF.DEL", b"cf", b"a"],
20631            &[b"CF.COMPACT", b"cf"],
20632            &[b"CF.INFO", b"cf"],
20633            &[b"CF.DEBUG", b"cf"],
20634            &[b"CF.SCANDUMP", b"cf", b"0"],
20635            // The count min sketch.
20636            &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20637            &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20638            &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20639            &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20640            &[b"CMS.INFO", b"cms"],
20641            // The top k sketch.
20642            &[b"TOPK.RESERVE", b"tk", b"3"],
20643            &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20644            &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20645            &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20646            &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20647            &[b"TOPK.LIST", b"tk"],
20648            &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20649            &[b"TOPK.INFO", b"tk"],
20650            // The t digest.
20651            &[b"TDIGEST.CREATE", b"td"],
20652            &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20653            &[b"TDIGEST.MIN", b"td"],
20654            &[b"TDIGEST.MAX", b"td"],
20655            &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20656            &[b"TDIGEST.CDF", b"td", b"3"],
20657            &[b"TDIGEST.RANK", b"td", b"3"],
20658            &[b"TDIGEST.REVRANK", b"td", b"3"],
20659            &[b"TDIGEST.BYRANK", b"td", b"0"],
20660            &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20661            &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20662            &[b"TDIGEST.INFO", b"td"],
20663            &[b"TDIGEST.RESET", b"td"],
20664            &[b"TDIGEST.MIN", b"td"],
20665            // And the errors.
20666            &[b"SET", b"plain", b"v"],
20667            &[b"BF.ADD", b"plain", b"a"],
20668            &[b"CF.ADD", b"plain", b"a"],
20669            &[b"CMS.QUERY", b"plain", b"a"],
20670            &[b"TOPK.ADD", b"plain", b"a"],
20671            &[b"TDIGEST.ADD", b"plain", b"1"],
20672            &[b"CMS.INFO", b"gone"],
20673            &[b"TOPK.INFO", b"gone"],
20674            &[b"TDIGEST.INFO", b"gone"],
20675        ];
20676
20677        let mut one = Fixture::new();
20678        let mut many = Fixture::striped(8);
20679        for parts in script {
20680            let a = one.run(parts);
20681            let b = many.run(parts);
20682            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20683        }
20684    }
20685
20686    /// The two sketch merges, with their sources on stripes of their own.
20687    ///
20688    /// These are the only two commands in the ten groups that name more than one
20689    /// key, and both read a run of sources and write a destination, so both go
20690    /// wrong in the same way if a merge holds one store and looks every source up
20691    /// in it.
20692    #[test]
20693    fn a_sketch_merge_across_stripes_reads_every_source() {
20694        let mut many = Fixture::striped(8);
20695        let other = apart(&mut many, "s1");
20696        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20697        let mut one = Fixture::new();
20698        let mut both = |parts: &[&[u8]]| {
20699            let a = one.run(parts);
20700            let b = many.run(parts);
20701            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20702            a
20703        };
20704
20705        // The count min sketch. The destination has to be the sources' shape,
20706        // and it is named first, so all three keys are read before anything is
20707        // written.
20708        for key in [b"cd".as_slice(), s1, s2] {
20709            both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20710        }
20711        both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20712        both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20713        assert_eq!(
20714            both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20715            "+OK\r\n",
20716            "the merge took both sources"
20717        );
20718        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20719        // And with weights, which are read against the sources in order.
20720        both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20721        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20722        // A source that is not a sketch is answered before anything is written.
20723        both(&[b"SET", b"plain", b"v"]);
20724        assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20725        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20726
20727        // The t digest, which builds its destination and then puts it in place.
20728        // The two source keys are used again here, so what they held goes first.
20729        both(&[b"FLUSHALL"]);
20730        both(&[b"TDIGEST.CREATE", b"td"]);
20731        both(&[b"TDIGEST.CREATE", s1]);
20732        both(&[b"TDIGEST.CREATE", s2]);
20733        both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20734        both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20735        assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20736        assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20737        assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20738    }
20739
20740    /// Every shape of `SORT`, on one stripe and on eight.
20741    ///
20742    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
20743    /// destination are four different names and nothing lines them up, so on
20744    /// eight stripes this script is reading and writing all over the database
20745    /// while on one it is doing what it always did.
20746    #[test]
20747    fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20748        let script: &[&[&[u8]]] = &[
20749            &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20750            &[b"SORT", b"l"],
20751            &[b"SORT", b"l", b"DESC"],
20752            &[b"SORT", b"l", b"ALPHA"],
20753            &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20754            &[b"SORT_RO", b"l"],
20755            // A weight per element, so the order comes off keys the command
20756            // never named.
20757            &[
20758                b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20759            ],
20760            &[b"SORT", b"l", b"BY", b"w_*"],
20761            &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20762            &[b"DEL", b"w_2"],
20763            &[b"SORT", b"l", b"BY", b"w_*"],
20764            // And the answer off another set of keys again, with `#` mixed in
20765            // so the rows are not all lookups.
20766            &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20767            &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20768            // A pattern that reaches into a hash, which is another key again.
20769            &[b"HSET", b"h_1", b"f", b"9"],
20770            &[b"HSET", b"h_2", b"f", b"8"],
20771            &[b"HSET", b"h_3", b"f", b"7"],
20772            &[b"HSET", b"h_10", b"f", b"6"],
20773            &[b"SORT", b"l", b"BY", b"h_*->f"],
20774            &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20775            // The destination, which is a fourth place to land.
20776            &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20777            &[b"LRANGE", b"out", b"0", b"-1"],
20778            &[b"SORT", b"l", b"STORE", b"l"],
20779            &[b"LRANGE", b"l", b"0", b"-1"],
20780            // An empty result takes the destination away rather than leaving a
20781            // list of nothing behind.
20782            &[b"SORT", b"missing", b"STORE", b"out"],
20783            &[b"EXISTS", b"out"],
20784            // A set and a sorted set sort the same way a list does, and a set
20785            // written to a destination is sorted even when nothing asked.
20786            &[b"SADD", b"s", b"c", b"a", b"b"],
20787            &[b"SORT", b"s", b"ALPHA"],
20788            &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20789            &[b"LRANGE", b"out", b"0", b"-1"],
20790            &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20791            &[b"SORT", b"z", b"BY", b"nosort"],
20792            &[b"SORT", b"z", b"ALPHA", b"DESC"],
20793            // And the two ways it refuses: a key of the wrong type, and an
20794            // element that is not a number under a numeric sort.
20795            &[b"SET", b"str", b"v"],
20796            &[b"SORT", b"str"],
20797            &[b"RPUSH", b"words", b"one", b"two"],
20798            &[b"SORT", b"words"],
20799            &[b"SORT_RO", b"l", b"STORE", b"out"],
20800        ];
20801
20802        let mut one = Fixture::new();
20803        let mut many = Fixture::striped(8);
20804        for parts in script {
20805            let a = one.run(parts);
20806            let b = many.run(parts);
20807            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20808        }
20809    }
20810
20811    /// One `SORT` whose four kinds of key are on stripes of their own.
20812    ///
20813    /// The script above spreads keys around by writing enough of them, and this
20814    /// one checks the spread rather than trusting it: the list, the weight key
20815    /// for one of its elements and the destination are asserted to be in three
20816    /// places before the command runs.
20817    #[test]
20818    fn a_sort_across_stripes_reads_every_pattern_key() {
20819        let mut f = Fixture::striped(8);
20820        let out = apart(&mut f, "l");
20821        let (list, dest) = (b"l".as_slice(), out.as_bytes());
20822
20823        f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20824        f.run(&[
20825            b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20826        ]);
20827        f.run(&[
20828            b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20829        ]);
20830
20831        // The weights are four keys and they are not all in one place, which is
20832        // the thing that would go unnoticed if the command held a stripe.
20833        let db = f.server.striped(0);
20834        let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20835            .iter()
20836            .map(|k| db.stripe_of(k.as_slice()))
20837            .collect();
20838        assert!(
20839            weights.iter().any(|s| *s != weights[0]),
20840            "the four weight keys all landed on one stripe, so this proves nothing"
20841        );
20842
20843        assert_eq!(
20844            f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20845            "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20846            "the order came off the weights and the answer off the data keys"
20847        );
20848        assert_eq!(
20849            f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20850            ":4\r\n"
20851        );
20852        assert_eq!(
20853            f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20854            "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20855            "the destination is on a stripe of its own and got the whole answer"
20856        );
20857    }
20858
20859    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
20860    /// decide what shape it is stored in.
20861    ///
20862    /// This is the setting that would go wrong quietly. A stripe that kept the
20863    /// old ladder would hold the same hash in a different encoding from the
20864    /// stripe next to it, and the only thing that would ever say so is
20865    /// `OBJECT ENCODING`, which is why the check is on that.
20866    #[test]
20867    fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20868        let mut f = Fixture::striped(8);
20869        let other = apart(&mut f, "h");
20870        let (first, second) = (b"h".as_slice(), other.as_bytes());
20871
20872        assert_eq!(
20873            f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20874            "+OK\r\n"
20875        );
20876        assert_eq!(
20877            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20878            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20879            "the read comes off one stripe and has to answer for all of them"
20880        );
20881        for key in [first, second] {
20882            f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20883            assert_eq!(
20884                f.run(&[b"OBJECT", b"ENCODING", key]),
20885                "$8\r\nlistpack\r\n",
20886                "two fields is still under the ladder"
20887            );
20888            f.run(&[b"HSET", key, b"c", b"3"]);
20889            assert_eq!(
20890                f.run(&[b"OBJECT", b"ENCODING", key]),
20891                "$9\r\nhashtable\r\n",
20892                "three fields is over it, on whichever stripe the key is on"
20893            );
20894        }
20895
20896        // And the policy, which every stripe has to agree about for the same
20897        // reason: an eviction draws from one stripe at a time.
20898        assert_eq!(
20899            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20900            "+OK\r\n"
20901        );
20902        let db = f.server.striped(0);
20903        assert!(
20904            (0..db.width()).all(|i| db.hold_stripe(i).policy().name() == "allkeys-lru"),
20905            "a stripe kept the old policy"
20906        );
20907    }
20908
20909    /// What an index holds, as the two numbers `FT.INFO` reports about it.
20910    ///
20911    /// Read off the registry rather than parsed back out of an `FT.INFO` reply,
20912    /// because the reply is thirty odd fields and these two are the ones the
20913    /// keyspace hook moves.
20914    fn held(f: &Fixture, name: &[u8]) -> (usize, u32) {
20915        let search = f.server.search.lock();
20916        let index = search.named(name).expect("the index is there");
20917        (index.held.docs.len(), index.held.docs.last())
20918    }
20919
20920    /// A hash written under an index's prefix reaches it, and one written
20921    /// outside the prefix does not.
20922    #[test]
20923    fn a_hash_that_is_written_reaches_the_index_that_follows_it() {
20924        let mut f = Fixture::new();
20925        f.run(&[
20926            b"FT.CREATE",
20927            b"ix",
20928            b"PREFIX",
20929            b"1",
20930            b"p:",
20931            b"SCHEMA",
20932            b"t",
20933            b"TEXT",
20934        ]);
20935        f.run(&[b"HSET", b"p:1", b"t", b"running dogs"]);
20936        assert_eq!(held(&f, b"ix"), (1, 1));
20937        f.run(&[b"HSET", b"other:1", b"t", b"running dogs"]);
20938        assert_eq!(held(&f, b"ix"), (1, 1));
20939
20940        // Every field of the key and not the one the command named, since a
20941        // document is read from nothing every time.
20942        f.run(&[b"HSET", b"p:1", b"u", b"beta"]);
20943        f.run(&[b"HDEL", b"p:1", b"u"]);
20944        assert_eq!(held(&f, b"ix"), (1, 3));
20945        let search = f.server.search.lock();
20946        let index = search.named(b"ix").expect("there");
20947        assert_eq!(index.held.docs.id(b"p:1"), Some(3));
20948    }
20949
20950    /// A fresh index reads the keys that were already there, and walks past a
20951    /// key of the wrong type without counting a failure.
20952    #[test]
20953    fn a_fresh_index_reads_the_keys_that_were_already_there() {
20954        let mut f = Fixture::new();
20955        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20956        f.run(&[b"SET", b"p:str", b"not a hash"]);
20957        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
20958        f.run(&[
20959            b"FT.CREATE",
20960            b"ix",
20961            b"PREFIX",
20962            b"1",
20963            b"p:",
20964            b"SCHEMA",
20965            b"t",
20966            b"TEXT",
20967        ]);
20968
20969        assert_eq!(held(&f, b"ix"), (1, 1));
20970        let search = f.server.search.lock();
20971        let index = search.named(b"ix").expect("there");
20972        assert_eq!(index.trouble.whole().failures(), 0);
20973    }
20974
20975    /// `SKIPINITIALSCAN` leaves what was there alone, and a later write to one
20976    /// of those keys still lands.
20977    #[test]
20978    fn an_index_that_skipped_the_scan_fills_up_on_the_next_write() {
20979        let mut f = Fixture::new();
20980        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20981        f.run(&[
20982            b"FT.CREATE",
20983            b"ix",
20984            b"PREFIX",
20985            b"1",
20986            b"p:",
20987            b"SKIPINITIALSCAN",
20988            b"SCHEMA",
20989            b"t",
20990            b"TEXT",
20991        ]);
20992        assert_eq!(held(&f, b"ix"), (0, 0));
20993        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
20994        assert_eq!(held(&f, b"ix"), (1, 1));
20995    }
20996
20997    /// A command that changed nothing leaves the document where it was, which
20998    /// is not the same as a command that was not a write.
20999    ///
21000    /// All five of these were measured against 8.10.1. Writing the same value
21001    /// again moves the number and a deadline set for later does not, which is
21002    /// the pair that makes the rule "the fields are not what they were" rather
21003    /// than "this was a write".
21004    #[test]
21005    fn only_a_real_change_gives_the_document_a_new_number() {
21006        let mut f = Fixture::new();
21007        f.run(&[
21008            b"FT.CREATE",
21009            b"ix",
21010            b"PREFIX",
21011            b"1",
21012            b"p:",
21013            b"SCHEMA",
21014            b"t",
21015            b"TEXT",
21016        ]);
21017        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21018        assert_eq!(held(&f, b"ix"), (1, 1));
21019
21020        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21021        assert_eq!(held(&f, b"ix"), (1, 2), "the same value still rewrites");
21022
21023        for quiet in [
21024            vec![b"HSETNX".as_slice(), b"p:1", b"t", b"other"],
21025            vec![b"HDEL".as_slice(), b"p:1", b"nosuch"],
21026            vec![b"HGET".as_slice(), b"p:1", b"t"],
21027            vec![b"HGETALL".as_slice(), b"p:1"],
21028            vec![b"HEXPIRE".as_slice(), b"p:1", b"100", b"FIELDS", b"1", b"t"],
21029            vec![b"HPERSIST".as_slice(), b"p:1", b"FIELDS", b"1", b"t"],
21030            vec![
21031                b"HGETEX".as_slice(),
21032                b"p:1",
21033                b"EX",
21034                b"100",
21035                b"FIELDS",
21036                b"1",
21037                b"t",
21038            ],
21039            vec![b"HGETDEL".as_slice(), b"p:1", b"FIELDS", b"1", b"nosuch"],
21040        ] {
21041            f.run(&quiet);
21042            assert_eq!(held(&f, b"ix"), (1, 2), "{:?} moved the document", quiet[0]);
21043        }
21044
21045        // And the ones that do change something.
21046        f.run(&[b"HSET", b"p:2", b"n", b"1"]);
21047        f.run(&[b"HINCRBY", b"p:2", b"n", b"1"]);
21048        assert_eq!(held(&f, b"ix"), (2, 4));
21049        // A deadline that has already passed takes the field away, and taking
21050        // the last field away takes the key and the document with it. The
21051        // number still moves on the way past, because the field going and the
21052        // key going are two separate pieces of news and the first of them
21053        // writes the document one last time.
21054        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"n"]);
21055        assert_eq!(held(&f, b"ix"), (1, 5));
21056    }
21057
21058    /// The two ways of emptying a hash, which do not leave the same thing
21059    /// behind. `HDEL` of the last field spends no number and is counted as a
21060    /// refusal, and a deadline that has already passed spends one on a document
21061    /// nobody sees and is counted as nothing. Measured against 8.10.1 and not
21062    /// something anyone would guess.
21063    #[test]
21064    fn a_key_emptied_by_a_deadline_spends_a_number_and_one_emptied_by_hdel_does_not() {
21065        /// The index's own failure count.
21066        fn refused(f: &Fixture, name: &[u8]) -> u64 {
21067            let search = f.server.search.lock();
21068            let index = search.named(name).expect("the index is there");
21069            index.trouble.whole().failures()
21070        }
21071
21072        let mut f = Fixture::new();
21073        f.run(&[
21074            b"FT.CREATE",
21075            b"ix",
21076            b"PREFIX",
21077            b"1",
21078            b"p:",
21079            b"SCHEMA",
21080            b"t",
21081            b"TEXT",
21082        ]);
21083        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21084        assert_eq!(held(&f, b"ix"), (1, 1));
21085        f.run(&[b"HDEL", b"p:1", b"t"]);
21086        assert_eq!(
21087            held(&f, b"ix"),
21088            (0, 1),
21089            "HDEL of the last field spends none"
21090        );
21091        assert_eq!(refused(&f, b"ix"), 1, "and is counted as a refusal");
21092
21093        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21094        assert_eq!(held(&f, b"ix"), (1, 2));
21095        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"t"]);
21096        assert_eq!(held(&f, b"ix"), (0, 3), "a deadline spends one");
21097        assert_eq!(refused(&f, b"ix"), 1, "and is counted as nothing");
21098
21099        f.run(&[b"HSET", b"p:3", b"t", b"alpha"]);
21100        assert_eq!(held(&f, b"ix"), (1, 4));
21101        f.run(&[b"HGETDEL", b"p:3", b"FIELDS", b"1", b"t"]);
21102        assert_eq!(held(&f, b"ix"), (0, 5), "and so does HGETDEL");
21103
21104        // Two fields and one command is one rewrite and not two, whichever way
21105        // the fields go.
21106        f.run(&[b"HSET", b"p:4", b"t", b"alpha", b"u", b"beta"]);
21107        assert_eq!(held(&f, b"ix"), (1, 6));
21108        f.run(&[b"HEXPIRE", b"p:4", b"0", b"FIELDS", b"2", b"t", b"u"]);
21109        assert_eq!(held(&f, b"ix"), (0, 7));
21110        assert_eq!(refused(&f, b"ix"), 1);
21111    }
21112
21113    /// `HSETEX` with a deadline that has already passed is two pieces of news
21114    /// from one command, so the number moves twice and the value never reaches
21115    /// the index.
21116    #[test]
21117    fn a_field_written_already_past_its_deadline_moves_the_number_twice() {
21118        let mut f = Fixture::new();
21119        f.run(&[
21120            b"FT.CREATE",
21121            b"ix",
21122            b"PREFIX",
21123            b"1",
21124            b"p:",
21125            b"SCHEMA",
21126            b"t",
21127            b"TEXT",
21128            b"u",
21129            b"TEXT",
21130        ]);
21131        f.run(&[b"HSET", b"p:1", b"u", b"keepme"]);
21132        assert_eq!(held(&f, b"ix"), (1, 1));
21133        f.run(&[
21134            b"HSETEX", b"p:1", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21135        ]);
21136        assert_eq!(
21137            held(&f, b"ix"),
21138            (1, 3),
21139            "the key lived and the field did not"
21140        );
21141
21142        // And the same when the key does not survive it.
21143        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21144        assert_eq!(held(&f, b"ix"), (2, 4));
21145        f.run(&[
21146            b"HSETEX", b"p:2", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21147        ]);
21148        assert_eq!(held(&f, b"ix"), (1, 6));
21149    }
21150
21151    /// The number one key is indexed under, or `None` when it holds no
21152    /// document.
21153    fn number(f: &Fixture, name: &[u8], key: &[u8]) -> Option<u32> {
21154        let search = f.server.search.lock();
21155        let index = search.named(name).expect("the index is there");
21156        index.held.docs.id(key)
21157    }
21158
21159    /// An index over `p:` with one document under `p:1`, which is where four of
21160    /// the tests below start.
21161    fn indexed() -> Fixture {
21162        let mut f = Fixture::new();
21163        f.run(&[
21164            b"FT.CREATE",
21165            b"ix",
21166            b"PREFIX",
21167            b"1",
21168            b"p:",
21169            b"SCHEMA",
21170            b"t",
21171            b"TEXT",
21172        ]);
21173        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21174        f
21175    }
21176
21177    /// Every way a keyspace command takes a key away leaves no document behind,
21178    /// and none of them spends a number or is counted as a refusal.
21179    #[test]
21180    fn a_key_a_keyspace_command_takes_away_loses_its_document() {
21181        for take in [
21182            vec![b"DEL".as_slice(), b"p:1"],
21183            vec![b"UNLINK".as_slice(), b"p:1"],
21184            vec![b"PEXPIREAT".as_slice(), b"p:1", b"1"],
21185            vec![b"EXPIRE".as_slice(), b"p:1", b"-1"],
21186        ] {
21187            let mut f = indexed();
21188            assert_eq!(held(&f, b"ix"), (1, 1));
21189            f.run(&take);
21190            assert_eq!(held(&f, b"ix"), (0, 1), "{:?} left something", take[0]);
21191            let search = f.server.search.lock();
21192            let index = search.named(b"ix").expect("the index is there");
21193            assert_eq!(index.trouble.whole().failures(), 0, "{:?}", take[0]);
21194        }
21195
21196        // A deadline that has not passed yet is not one of them.
21197        let mut f = indexed();
21198        f.run(&[b"EXPIRE", b"p:1", b"1000"]);
21199        assert_eq!(held(&f, b"ix"), (1, 1));
21200        f.run(&[b"PERSIST", b"p:1"]);
21201        assert_eq!(held(&f, b"ix"), (1, 1));
21202    }
21203
21204    /// A rename inside the prefix keeps the number the document had, which is
21205    /// the one write on a followed key that does not spend one. Out of the
21206    /// prefix is an erase and into it is a fresh reading, both measured.
21207    #[test]
21208    fn a_rename_inside_the_prefix_keeps_the_number_the_document_had() {
21209        let mut f = indexed();
21210        f.run(&[b"RENAME", b"p:1", b"p:2"]);
21211        assert_eq!(held(&f, b"ix"), (1, 1), "nothing was read again");
21212        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
21213        assert_eq!(number(&f, b"ix", b"p:1"), None);
21214
21215        f.run(&[b"RENAME", b"p:2", b"q:1"]);
21216        assert_eq!(held(&f, b"ix"), (0, 1), "out of the prefix is an erase");
21217
21218        f.run(&[b"RENAME", b"q:1", b"p:3"]);
21219        assert_eq!(held(&f, b"ix"), (1, 2), "and into it is a reading");
21220        assert_eq!(number(&f, b"ix", b"p:3"), Some(2));
21221
21222        // `RENAMENX` goes the same way, and the one that answers zero changes
21223        // nothing.
21224        f.run(&[b"HSET", b"p:4", b"t", b"beta"]);
21225        assert_eq!(f.run(&[b"RENAMENX", b"p:3", b"p:4"]), ":0\r\n");
21226        assert_eq!(held(&f, b"ix"), (2, 3));
21227        f.run(&[b"RENAMENX", b"p:3", b"p:5"]);
21228        assert_eq!(number(&f, b"ix", b"p:5"), Some(2));
21229    }
21230
21231    /// A rename over a key that already had a document leaves one document and
21232    /// not two. A real server leaves both, and D-64 is that difference.
21233    #[test]
21234    fn a_rename_over_a_document_leaves_one_of_them() {
21235        let mut f = indexed();
21236        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
21237        assert_eq!(held(&f, b"ix"), (2, 2));
21238        f.run(&[b"RENAME", b"p:1", b"p:2"]);
21239        assert_eq!(held(&f, b"ix"), (1, 2));
21240        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
21241    }
21242
21243    /// A key that arrives under the prefix by being copied or restored is read
21244    /// as a new document, and one that is written over by something that is not
21245    /// a hash is erased without a word.
21246    #[test]
21247    fn a_key_that_arrives_under_the_prefix_is_read_and_one_overwritten_is_erased() {
21248        let mut f = indexed();
21249        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
21250        f.run(&[b"COPY", b"q:1", b"p:2"]);
21251        assert_eq!(held(&f, b"ix"), (2, 2));
21252        assert_eq!(number(&f, b"ix", b"p:2"), Some(2));
21253
21254        // Out of the prefix, where the source keeps the document it had.
21255        f.run(&[b"COPY", b"p:1", b"q:2"]);
21256        assert_eq!(held(&f, b"ix"), (2, 2));
21257
21258        // Over a key that has one, which is a new reading and not a rename.
21259        f.run(&[b"COPY", b"q:1", b"p:1", b"REPLACE"]);
21260        assert_eq!(held(&f, b"ix"), (2, 3));
21261        assert_eq!(number(&f, b"ix", b"p:1"), Some(3));
21262
21263        // And a string landing on top of a document takes it away, spending no
21264        // number and counting no failure.
21265        f.run(&[b"SET", b"s:1", b"plain"]);
21266        f.run(&[b"COPY", b"s:1", b"p:1", b"REPLACE"]);
21267        assert_eq!(held(&f, b"ix"), (1, 3));
21268        let dump = f.run(&[b"DUMP", b"q:1"]);
21269        assert!(dump.starts_with('$'), "{dump}");
21270    }
21271
21272    /// The keyspace group reads a key back on database zero whatever database
21273    /// the command ran on, which is measured and is not what the hash commands
21274    /// do. A `COPY` into another database indexes nothing and takes away
21275    /// whatever the destination had, and a `RESTORE` anywhere else is invisible.
21276    #[test]
21277    fn the_keyspace_group_reads_database_zero_whatever_database_it_ran_on() {
21278        let mut f = indexed();
21279        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
21280        assert_eq!(held(&f, b"ix"), (2, 2));
21281        // Into database one, so the indexes look for `p:2` on database zero,
21282        // find the one that is still there and read it again.
21283        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
21284        assert_eq!(held(&f, b"ix"), (2, 3));
21285        // And with nothing under that name on database zero, the copy leaves
21286        // the index one document lighter than it found it.
21287        f.run(&[b"DEL", b"p:2"]);
21288        assert_eq!(held(&f, b"ix"), (1, 3));
21289        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
21290        assert_eq!(held(&f, b"ix"), (1, 3), "the copy landed out of sight");
21291
21292        // A restore on another database is the same story.
21293        let dump = f.run(&[b"DUMP", b"p:1"]);
21294        assert!(dump.starts_with('$'), "{dump}");
21295        f.run(&[b"SELECT", b"1"]);
21296        f.run(&[b"HSET", b"q:1", b"t", b"gamma"]);
21297        f.run(&[b"RENAME", b"q:1", b"p:3"]);
21298        assert_eq!(held(&f, b"ix"), (1, 3), "and so is a rename");
21299    }
21300
21301    /// `MOVE` is not a change at all, because an index follows a key by name
21302    /// and a write on any database still reaches it.
21303    #[test]
21304    fn a_move_leaves_the_document_where_it_is() {
21305        let mut f = indexed();
21306        f.run(&[b"MOVE", b"p:1", b"1"]);
21307        assert_eq!(held(&f, b"ix"), (1, 1), "the key moved and nothing else");
21308        assert_eq!(number(&f, b"ix", b"p:1"), Some(1));
21309
21310        f.run(&[b"SELECT", b"1"]);
21311        f.run(&[b"HSET", b"p:1", b"t", b"beta"]);
21312        assert_eq!(held(&f, b"ix"), (1, 2), "and a write there still lands");
21313        f.run(&[b"DEL", b"p:1"]);
21314        assert_eq!(held(&f, b"ix"), (0, 2));
21315    }
21316
21317    /// A flush takes every index with it, whichever database it flushed.
21318    #[test]
21319    fn a_flush_drops_the_indexes() {
21320        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
21321            let mut f = indexed();
21322            f.run(&[flush]);
21323            assert!(f.server.search.lock().is_empty(), "{flush:?} kept an index");
21324            assert_eq!(f.run(&[b"FT._LIST"]), "*0\r\n");
21325        }
21326
21327        // Even on a database no index ever read, which is what a real server
21328        // does and is not what anyone would guess.
21329        let mut f = indexed();
21330        f.run(&[b"SELECT", b"9"]);
21331        f.run(&[b"FLUSHDB"]);
21332        assert!(f.server.search.lock().is_empty());
21333    }
21334
21335    /// A key that will not read is counted against the index and against the
21336    /// field, and `FT.INFO` says so.
21337    #[test]
21338    fn a_hash_that_will_not_read_is_counted_where_ft_info_reports_it() {
21339        let mut f = Fixture::new();
21340        f.run(&[
21341            b"FT.CREATE",
21342            b"ix",
21343            b"PREFIX",
21344            b"1",
21345            b"p:",
21346            b"SCHEMA",
21347            b"n",
21348            b"NUMERIC",
21349        ]);
21350        f.run(&[b"HSET", b"p:1", b"n", b"notanumber"]);
21351        assert_eq!(held(&f, b"ix"), (0, 0));
21352
21353        let reply = f.run(&[b"FT.INFO", b"ix"]);
21354        assert!(
21355            reply.contains("SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value: 'notanumber'"),
21356            "{reply}"
21357        );
21358        assert!(reply.contains("hash_indexing_failures"), "{reply}");
21359    }
21360
21361    /// An index can only be made on database zero, and the check comes after
21362    /// the `IFNX` shortcut and before everything else.
21363    #[test]
21364    fn an_index_can_only_be_made_on_database_zero() {
21365        let mut f = Fixture::new();
21366        f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]);
21367        f.run(&[b"SELECT", b"1"]);
21368        let refused = "-Cannot create index on db != 0\r\n";
21369        assert_eq!(
21370            f.run(&[b"FT.CREATE", b"jx", b"SCHEMA", b"t", b"TEXT"]),
21371            refused
21372        );
21373        // The name is taken, and it still answers about the database.
21374        assert_eq!(
21375            f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21376            refused
21377        );
21378        // And so does one whose arguments are nonsense.
21379        assert_eq!(
21380            f.run(&[b"FT.CREATE", b"zz", b"BOGUS", b"SCHEMA", b"t", b"TEXT"]),
21381            refused
21382        );
21383        // `IFNX` over a name that is taken is the one that gets through.
21384        assert_eq!(
21385            f.run(&[b"FT._CREATEIFNX", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21386            "+OK\r\n"
21387        );
21388        assert_eq!(f.server.search.lock().len(), 1);
21389    }
21390
21391    /// The scan reads the database the create was run on, and after that the
21392    /// index follows its keys in every database.
21393    ///
21394    /// The asymmetry is a real server's, measured, and it is the sort of thing
21395    /// nobody would arrive at by choosing.
21396    #[test]
21397    fn the_scan_is_one_database_and_the_following_is_all_of_them() {
21398        let mut f = Fixture::new();
21399        f.run(&[b"SELECT", b"1"]);
21400        f.run(&[b"HSET", b"p:9", b"t", b"on one"]);
21401        f.run(&[b"SELECT", b"0"]);
21402        f.run(&[b"HSET", b"p:0", b"t", b"on zero"]);
21403        f.run(&[
21404            b"FT.CREATE",
21405            b"ix",
21406            b"PREFIX",
21407            b"1",
21408            b"p:",
21409            b"SCHEMA",
21410            b"t",
21411            b"TEXT",
21412        ]);
21413        assert_eq!(held(&f, b"ix"), (1, 1), "the scan read database zero only");
21414
21415        f.run(&[b"SELECT", b"1"]);
21416        f.run(&[b"HSET", b"p:8", b"t", b"later"]);
21417        assert_eq!(
21418            held(&f, b"ix"),
21419            (2, 2),
21420            "and then it follows every database"
21421        );
21422    }
21423
21424    /// Four documents over the two kinds of field a query can ask about, which
21425    /// is the corpus the searches below read.
21426    fn corpus(f: &mut Fixture) {
21427        f.run(&[
21428            b"FT.CREATE",
21429            b"sx",
21430            b"PREFIX",
21431            b"1",
21432            b"d:",
21433            b"SCHEMA",
21434            b"t",
21435            b"TEXT",
21436            b"g",
21437            b"TAG",
21438            b"n",
21439            b"NUMERIC",
21440        ]);
21441        for (key, text, tag, number) in [
21442            (b"d:1".as_slice(), "alpha beta", "aa,bb", "1"),
21443            (b"d:2", "alpha gamma", "bb", "2"),
21444            (b"d:3", "delta", "cc", "3"),
21445            (b"d:4", "alpha beta gamma", "aa,cc", "4"),
21446        ] {
21447            f.run(&[
21448                b"HSET",
21449                key,
21450                b"t",
21451                text.as_bytes(),
21452                b"g",
21453                tag.as_bytes(),
21454                b"n",
21455                number.as_bytes(),
21456            ]);
21457        }
21458    }
21459
21460    /// A search answers a total and then a row for every key in the window,
21461    /// with the fields of that key after it.
21462    #[test]
21463    fn a_search_answers_a_total_and_then_the_rows() {
21464        let mut f = Fixture::new();
21465        corpus(&mut f);
21466        assert_eq!(
21467            f.run(&[b"FT.SEARCH", b"sx", b"delta"]),
21468            "*3\r\n:1\r\n$3\r\nd:3\r\n*6\r\n$1\r\nt\r\n$5\r\ndelta\r\n$1\r\ng\r\n$2\r\ncc\r\n$1\r\nn\r\n$1\r\n3\r\n"
21469        );
21470        // The fields are what the key holds and not what the schema names, so
21471        // a field nobody indexed comes back too.
21472        f.run(&[b"HSET", b"d:3", b"extra", b"more"]);
21473        assert!(f.run(&[b"FT.SEARCH", b"sx", b"delta"]).contains("extra"));
21474        // `NOCONTENT` leaves the keys on their own, and `LIMIT 0 0` leaves
21475        // the total on its own.
21476        assert_eq!(
21477            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
21478            "*2\r\n:1\r\n$3\r\nd:3\r\n"
21479        );
21480        assert_eq!(
21481            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
21482            "*1\r\n:3\r\n"
21483        );
21484    }
21485
21486    /// The window is ten rows when nobody said, and the cap is on how wide it
21487    /// is rather than on where it starts.
21488    #[test]
21489    fn the_window_is_ten_rows_and_a_million_wide_at_most() {
21490        let mut f = Fixture::new();
21491        corpus(&mut f);
21492        assert_eq!(
21493            f.run(&[
21494                b"FT.SEARCH",
21495                b"sx",
21496                b"alpha",
21497                b"NOCONTENT",
21498                b"LIMIT",
21499                b"1",
21500                b"1"
21501            ]),
21502            "*2\r\n:3\r\n$3\r\nd:2\r\n"
21503        );
21504        assert_eq!(
21505            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0"]),
21506            "-SEARCH_PARSE_ARGS LIMIT requires two arguments\r\n"
21507        );
21508        assert_eq!(
21509            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"-1"]),
21510            "-SEARCH_PARSE_ARGS LIMIT needs two numeric arguments\r\n"
21511        );
21512        assert_eq!(
21513            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"1000001"]),
21514            "-SEARCH_LIMIT_OVER LIMIT exceeds maximum of 1000000\r\n"
21515        );
21516        assert_eq!(
21517            f.run(&[
21518                b"FT.SEARCH",
21519                b"sx",
21520                b"alpha",
21521                b"NOCONTENT",
21522                b"LIMIT",
21523                b"999999",
21524                b"1000000"
21525            ]),
21526            "*1\r\n:3\r\n"
21527        );
21528    }
21529
21530    /// `RETURN 0` reads on the wire like `NOCONTENT` and is not the same
21531    /// thing, because a later `RETURN` puts the fields back and a later
21532    /// `RETURN` after a `NOCONTENT` does not.
21533    #[test]
21534    fn a_return_of_nothing_is_not_the_same_as_nocontent() {
21535        let mut f = Fixture::new();
21536        corpus(&mut f);
21537        let bare = "*2\r\n:1\r\n$3\r\nd:3\r\n";
21538        assert_eq!(
21539            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"0"]),
21540            bare
21541        );
21542        assert_eq!(
21543            f.run(&[
21544                b"FT.SEARCH",
21545                b"sx",
21546                b"delta",
21547                b"NOCONTENT",
21548                b"RETURN",
21549                b"1",
21550                b"t"
21551            ]),
21552            bare
21553        );
21554        assert_eq!(
21555            f.run(&[
21556                b"FT.SEARCH",
21557                b"sx",
21558                b"delta",
21559                b"RETURN",
21560                b"0",
21561                b"RETURN",
21562                b"1",
21563                b"t"
21564            ]),
21565            "*3\r\n:1\r\n$3\r\nd:3\r\n*2\r\n$1\r\nt\r\n$5\r\ndelta\r\n"
21566        );
21567    }
21568
21569    /// The count after `RETURN` counts words and not fields, so the `AS` and
21570    /// the name after it are two of them.
21571    #[test]
21572    fn the_count_after_return_counts_words() {
21573        let mut f = Fixture::new();
21574        corpus(&mut f);
21575        // Two words is one renamed field, and the name is the one it comes
21576        // back under.
21577        assert_eq!(
21578            f.run(&[
21579                b"FT.SEARCH",
21580                b"sx",
21581                b"delta",
21582                b"RETURN",
21583                b"3",
21584                b"t",
21585                b"AS",
21586                b"x"
21587            ]),
21588            "*3\r\n:1\r\n$3\r\nd:3\r\n*2\r\n$1\r\nx\r\n$5\r\ndelta\r\n"
21589        );
21590        // A count that stops on the `AS` has nothing to rename to, and one
21591        // that reaches past the last word is short an argument.
21592        assert_eq!(
21593            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"2", b"t", b"AS"]),
21594            "-SEARCH_PARSE_ARGS RETURN path AS name - must be accompanied with NAME\r\n"
21595        );
21596        assert_eq!(
21597            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"3", b"t", b"AS"]),
21598            "-SEARCH_PARSE_ARGS Bad arguments for RETURN: Expected an argument, but none provided\r\n"
21599        );
21600        // A count that stops before the `AS` asks for a field called `AS`,
21601        // which no key holds, and a field the key does not hold is left out
21602        // rather than sent empty.
21603        assert_eq!(
21604            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"AS"]),
21605            "*3\r\n:1\r\n$3\r\nd:3\r\n*0\r\n"
21606        );
21607    }
21608
21609    /// A `FILTER` is a numeric range written outside the query, and it is only
21610    /// the wrong way round on a field the schema holds as a number.
21611    #[test]
21612    fn a_filter_is_a_range_written_outside_the_query() {
21613        let mut f = Fixture::new();
21614        corpus(&mut f);
21615        assert_eq!(
21616            f.run(&[
21617                b"FT.SEARCH",
21618                b"sx",
21619                b"alpha",
21620                b"NOCONTENT",
21621                b"FILTER",
21622                b"n",
21623                b"2",
21624                b"4"
21625            ]),
21626            "*3\r\n:2\r\n$3\r\nd:2\r\n$3\r\nd:4\r\n"
21627        );
21628        assert_eq!(
21629            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2"]),
21630            "-SEARCH_PARSE_ARGS FILTER requires 3 arguments\r\n"
21631        );
21632        assert_eq!(
21633            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"x", b"1"]),
21634            "-SEARCH_PARSE_ARGS Bad lower range: x\r\n"
21635        );
21636        assert_eq!(
21637            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2", b"1"]),
21638            "-SEARCH_SYNTAX Invalid numeric range (min > max): @n:[2.000000 1.000000]\r\n"
21639        );
21640        // The same range on a field that is not a number at all, and on a
21641        // field that is not there, answers nothing rather than refusing.
21642        for field in [b"g".as_slice(), b"nope"] {
21643            assert_eq!(
21644                f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", field, b"2", b"1"]),
21645                "*1\r\n:0\r\n"
21646            );
21647        }
21648    }
21649
21650    /// The index is resolved before the arguments after it are read, so a name
21651    /// that is not there answers about the name whatever else is wrong.
21652    #[test]
21653    fn the_index_is_found_before_the_arguments_are_read() {
21654        let mut f = Fixture::new();
21655        corpus(&mut f);
21656        let missing = "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n";
21657        assert_eq!(f.run(&[b"FT.SEARCH", b"nope", b"alpha", b"BOGUS"]), missing);
21658        assert_eq!(
21659            f.run(&[b"FT.EXPLAIN", b"nope", b"alpha", b"BOGUS"]),
21660            missing
21661        );
21662        // And the arguments are read before the query is, so a query that
21663        // will not parse still answers about the argument.
21664        assert_eq!(
21665            f.run(&[b"FT.SEARCH", b"sx", b"@@@", b"BOGUS"]),
21666            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `BOGUS` at position 1 for <main>\r\n"
21667        );
21668    }
21669
21670    /// `INKEYS` filters the answer before the total is taken, which is not
21671    /// where a client would guess it happens.
21672    #[test]
21673    fn inkeys_comes_off_the_total() {
21674        let mut f = Fixture::new();
21675        corpus(&mut f);
21676        assert_eq!(
21677            f.run(&[
21678                b"FT.SEARCH",
21679                b"sx",
21680                b"alpha",
21681                b"NOCONTENT",
21682                b"INKEYS",
21683                b"1",
21684                b"d:1"
21685            ]),
21686            "*2\r\n:1\r\n$3\r\nd:1\r\n"
21687        );
21688        assert_eq!(
21689            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"NOCONTENT", b"INKEYS", b"0"]),
21690            "*1\r\n:0\r\n"
21691        );
21692    }
21693
21694    /// The fields come from the database the session is on, and a row whose
21695    /// key will not load there is dropped from the reply and taken off the
21696    /// total.
21697    ///
21698    /// Measured against a real server, which follows a key on every database
21699    /// and then loads it from one.
21700    #[test]
21701    fn the_fields_are_read_from_the_session_database() {
21702        let mut f = Fixture::new();
21703        corpus(&mut f);
21704        f.run(&[b"SELECT", b"1"]);
21705        f.run(&[b"HSET", b"d:9", b"t", b"delta", b"n", b"9"]);
21706        // Both documents are in the index, and only one of them is in this
21707        // database.
21708        assert_eq!(
21709            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
21710            "*3\r\n:2\r\n$3\r\nd:3\r\n$3\r\nd:9\r\n"
21711        );
21712        assert_eq!(
21713            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
21714            "*3\r\n:1\r\n$3\r\nd:9\r\n*2\r\n$1\r\nn\r\n$1\r\n9\r\n"
21715        );
21716    }
21717
21718    /// The deeper protocol answers a map of five rather than an array, with
21719    /// every row a map of its own.
21720    #[test]
21721    fn the_third_protocol_answers_a_map_of_five() {
21722        let mut f = Fixture::new();
21723        corpus(&mut f);
21724        f.out = Out::new(Proto::Resp3);
21725        assert_eq!(
21726            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
21727            concat!(
21728                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
21729                "%3\r\n+id\r\n$3\r\nd:3\r\n+extra_attributes\r\n%1\r\n$1\r\nn\r\n$1\r\n3\r\n",
21730                "+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
21731            )
21732        );
21733    }
21734
21735    /// A window of nothing is a client asking for the count on its own, and a
21736    /// window of nothing that starts somewhere else is a contradiction all
21737    /// three commands refuse in the same words.
21738    #[test]
21739    fn a_window_of_nothing_has_to_start_at_the_top() {
21740        let mut f = Fixture::new();
21741        corpus(&mut f);
21742        let refused = "-SEARCH_LIMIT_OVER The `offset` of the LIMIT must be 0 when `num` is 0\r\n";
21743        assert_eq!(
21744            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21745            refused
21746        );
21747        assert_eq!(
21748            f.run(&[b"FT.EXPLAIN", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21749            refused
21750        );
21751        assert_eq!(
21752            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21753            refused
21754        );
21755        assert_eq!(
21756            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
21757            "*1\r\n:3\r\n"
21758        );
21759    }
21760
21761    /// An aggregation answers a count and then a list of properties for every
21762    /// row, which is empty until something asks for a field.
21763    #[test]
21764    fn an_aggregation_answers_a_count_and_then_the_properties() {
21765        let mut f = Fixture::new();
21766        corpus(&mut f);
21767        assert_eq!(
21768            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha"]),
21769            "*4\r\n:1\r\n*0\r\n*0\r\n*0\r\n"
21770        );
21771        // Every row, and not the ten a search would have cut it down to.
21772        assert_eq!(
21773            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"1", b"@t"]),
21774            concat!(
21775                "*4\r\n:3\r\n*2\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
21776                "*2\r\n$1\r\nt\r\n$11\r\nalpha gamma\r\n",
21777                "*2\r\n$1\r\nt\r\n$16\r\nalpha beta gamma\r\n"
21778            )
21779        );
21780        // Ascending document number, because nothing sorts the answer. The
21781        // second and fourth documents are the ones the window lands on and the
21782        // best scoring one is not among them.
21783        assert_eq!(
21784            f.run(&[
21785                b"FT.AGGREGATE",
21786                b"sx",
21787                b"alpha",
21788                b"LOAD",
21789                b"1",
21790                b"@n",
21791                b"LIMIT",
21792                b"1",
21793                b"2"
21794            ]),
21795            "*3\r\n:3\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n*2\r\n$1\r\nn\r\n$1\r\n4\r\n"
21796        );
21797        // A query nothing answers is a count of nothing and no rows at all.
21798        assert_eq!(
21799            f.run(&[b"FT.AGGREGATE", b"sx", b"nope", b"LOAD", b"1", b"@t"]),
21800            "*1\r\n:0\r\n"
21801        );
21802    }
21803
21804    /// `LOAD` counts words rather than fields, names the property after the
21805    /// path unless an `AS` renames it, and reads everything the key holds when
21806    /// it is given a star.
21807    #[test]
21808    fn a_load_counts_words_and_can_rename_what_it_reads() {
21809        let mut f = Fixture::new();
21810        corpus(&mut f);
21811        // Three words, which are the path, the `AS` and the name.
21812        assert_eq!(
21813            f.run(&[
21814                b"FT.AGGREGATE",
21815                b"sx",
21816                b"alpha",
21817                b"LOAD",
21818                b"3",
21819                b"@t",
21820                b"AS",
21821                b"text"
21822            ]),
21823            concat!(
21824                "*4\r\n:3\r\n*2\r\n$4\r\ntext\r\n$10\r\nalpha beta\r\n",
21825                "*2\r\n$4\r\ntext\r\n$11\r\nalpha gamma\r\n",
21826                "*2\r\n$4\r\ntext\r\n$16\r\nalpha beta gamma\r\n"
21827            )
21828        );
21829        assert_eq!(
21830            f.run(&[
21831                b"FT.AGGREGATE",
21832                b"sx",
21833                b"alpha",
21834                b"LOAD",
21835                b"*",
21836                b"LIMIT",
21837                b"0",
21838                b"1"
21839            ]),
21840            concat!(
21841                "*2\r\n:3\r\n*6\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
21842                "$1\r\ng\r\n$5\r\naa,bb\r\n$1\r\nn\r\n$1\r\n1\r\n"
21843            )
21844        );
21845        // A field the key does not hold is left out rather than sent empty.
21846        assert_eq!(
21847            f.run(&[
21848                b"FT.AGGREGATE",
21849                b"sx",
21850                b"alpha",
21851                b"LOAD",
21852                b"2",
21853                b"@n",
21854                b"@nope",
21855                b"LIMIT",
21856                b"0",
21857                b"2"
21858            ]),
21859            "*3\r\n:3\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n"
21860        );
21861    }
21862
21863    /// The `LOAD` grammar, which has four ways to go wrong and one of them is
21864    /// only reported once the rest of the argument list has read cleanly.
21865    #[test]
21866    fn a_load_refuses_a_count_it_cannot_use() {
21867        let mut f = Fixture::new();
21868        corpus(&mut f);
21869        let head = "-SEARCH_PARSE_ARGS Bad arguments for LOAD: ";
21870        assert_eq!(
21871            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"x"]),
21872            format!("{head}Expected number of fields or `*`\r\n")
21873        );
21874        assert_eq!(
21875            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"-1", b"@t"]),
21876            format!("{head}Value is outside acceptable bounds\r\n")
21877        );
21878        assert_eq!(
21879            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"5", b"@t"]),
21880            format!("{head}Expected an argument, but none provided\r\n")
21881        );
21882        assert_eq!(
21883            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD"]),
21884            format!("{head}Expected an argument, but none provided\r\n")
21885        );
21886        // A count that runs out on the `AS` is held back, because the word
21887        // after it is read as an argument of its own and may be worth an error
21888        // of its own. Nothing follows here, so the held back line is the one.
21889        assert_eq!(
21890            f.run(&[
21891                b"FT.AGGREGATE",
21892                b"sx",
21893                b"alpha",
21894                b"LOAD",
21895                b"2",
21896                b"@t",
21897                b"AS"
21898            ]),
21899            "-SEARCH_PARSE_ARGS LOAD path AS name - must be accompanied with NAME\r\n"
21900        );
21901        // And here the word after it is one an aggregation stops taking once a
21902        // step has been read, so that is what the client hears about.
21903        assert_eq!(
21904            f.run(&[
21905                b"FT.AGGREGATE",
21906                b"sx",
21907                b"alpha",
21908                b"LOAD",
21909                b"2",
21910                b"@t",
21911                b"AS",
21912                b"VERBATIM"
21913            ]),
21914            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 5 for <main>\r\n"
21915        );
21916        // A `LOAD 0` is a step that names nothing. It shuts the same door
21917        // without becoming a loader, so the count stays the one a query with no
21918        // `LOAD` gets.
21919        assert_eq!(
21920            f.run(&[
21921                b"FT.AGGREGATE",
21922                b"sx",
21923                b"alpha",
21924                b"LOAD",
21925                b"0",
21926                b"LIMIT",
21927                b"0",
21928                b"1"
21929            ]),
21930            "*2\r\n:1\r\n*0\r\n"
21931        );
21932    }
21933
21934    /// Reading a step of the pipeline stops the words about the search itself
21935    /// being taken, and `LIMIT` and `TIMEOUT` are not steps.
21936    #[test]
21937    fn a_pipeline_step_closes_the_door_on_the_search_words() {
21938        let mut f = Fixture::new();
21939        corpus(&mut f);
21940        assert_eq!(
21941            f.run(&[
21942                b"FT.AGGREGATE",
21943                b"sx",
21944                b"alpha",
21945                b"LOAD",
21946                b"1",
21947                b"@t",
21948                b"VERBATIM"
21949            ]),
21950            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 4 for <main>\r\n"
21951        );
21952        assert_eq!(
21953            f.run(&[
21954                b"FT.AGGREGATE",
21955                b"sx",
21956                b"alpha",
21957                b"LIMIT",
21958                b"0",
21959                b"1",
21960                b"VERBATIM"
21961            ]),
21962            "*2\r\n:1\r\n*0\r\n"
21963        );
21964        // Three words a search takes that this command names in its refusal
21965        // rather than calling them unknown.
21966        for word in [b"RETURN".as_slice(), b"SUMMARIZE", b"HIGHLIGHT"] {
21967            let name = core::str::from_utf8(word).expect("the three words are text");
21968            assert_eq!(
21969                f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", word]),
21970                format!("-SEARCH_PARSE_ARGS {name} is not supported on FT.AGGREGATE\r\n")
21971            );
21972        }
21973    }
21974
21975    /// `ADDSCORES` writes the score as a property to twelve significant digits
21976    /// where `WITHSCORES` writes it beside the row in full.
21977    #[test]
21978    fn addscores_writes_a_shorter_score_than_withscores() {
21979        let mut f = Fixture::new();
21980        corpus(&mut f);
21981        assert_eq!(
21982            f.run(&[
21983                b"FT.AGGREGATE",
21984                b"sx",
21985                b"alpha",
21986                b"ADDSCORES",
21987                b"LOAD",
21988                b"1",
21989                b"@n",
21990                b"LIMIT",
21991                b"0",
21992                b"2"
21993            ]),
21994            concat!(
21995                "*3\r\n:3\r\n",
21996                "*4\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n$1\r\nn\r\n$1\r\n1\r\n",
21997                "*4\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n$1\r\nn\r\n$1\r\n2\r\n"
21998            )
21999        );
22000        // `NOCONTENT` takes the properties away and leaves whatever was asked
22001        // for beside them, and a sort key is always null because nothing sorts
22002        // by one yet.
22003        assert_eq!(
22004            f.run(&[
22005                b"FT.AGGREGATE",
22006                b"sx",
22007                b"alpha",
22008                b"NOCONTENT",
22009                b"WITHSCORES",
22010                b"LIMIT",
22011                b"0",
22012                b"2"
22013            ]),
22014            "*3\r\n:1\r\n$18\r\n0.3566749439387324\r\n$18\r\n0.3566749439387324\r\n"
22015        );
22016        assert_eq!(
22017            f.run(&[
22018                b"FT.AGGREGATE",
22019                b"sx",
22020                b"alpha",
22021                b"WITHSORTKEYS",
22022                b"LOAD",
22023                b"1",
22024                b"@n",
22025                b"LIMIT",
22026                b"0",
22027                b"1"
22028            ]),
22029            "*3\r\n:3\r\n$-1\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n"
22030        );
22031    }
22032
22033    /// The one scorer that has to see the whole answer first turns the count
22034    /// into the real total and hands the rows back backwards.
22035    #[test]
22036    fn a_normalising_scorer_answers_the_rows_backwards() {
22037        let mut f = Fixture::new();
22038        corpus(&mut f);
22039        assert_eq!(
22040            f.run(&[
22041                b"FT.AGGREGATE",
22042                b"sx",
22043                b"alpha",
22044                b"SCORER",
22045                b"BM25STD.NORM",
22046                b"ADDSCORES",
22047                b"LOAD",
22048                b"1",
22049                b"@n",
22050                b"LIMIT",
22051                b"1",
22052                b"2"
22053            ]),
22054            concat!(
22055                "*3\r\n:3\r\n",
22056                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n2\r\n",
22057                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n1\r\n"
22058            )
22059        );
22060        // Without `ADDSCORES` nothing on the row needs the score, so the rows
22061        // come back the way every other query answers them.
22062        assert_eq!(
22063            f.run(&[
22064                b"FT.AGGREGATE",
22065                b"sx",
22066                b"alpha",
22067                b"SCORER",
22068                b"BM25STD.NORM",
22069                b"LOAD",
22070                b"1",
22071                b"@n",
22072                b"LIMIT",
22073                b"1",
22074                b"2"
22075            ]),
22076            "*3\r\n:3\r\n*2\r\n$1\r\nn\r\n$1\r\n2\r\n*2\r\n$1\r\nn\r\n$1\r\n4\r\n"
22077        );
22078    }
22079
22080    /// The deeper protocol answers the same map of five a search answers, with
22081    /// the `id` gone because an aggregation is about the properties.
22082    #[test]
22083    fn an_aggregation_answers_a_map_of_five_as_well() {
22084        let mut f = Fixture::new();
22085        corpus(&mut f);
22086        f.out = Out::new(Proto::Resp3);
22087        assert_eq!(
22088            f.run(&[
22089                b"FT.AGGREGATE",
22090                b"sx",
22091                b"alpha",
22092                b"ADDSCORES",
22093                b"WITHSCORES",
22094                b"WITHSORTKEYS",
22095                b"LOAD",
22096                b"1",
22097                b"@n",
22098                b"LIMIT",
22099                b"0",
22100                b"1"
22101            ]),
22102            concat!(
22103                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
22104                "%4\r\n+score\r\n,0.3566749439387324\r\n+sortkey\r\n_\r\n",
22105                "+extra_attributes\r\n%2\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n",
22106                "$1\r\nn\r\n$1\r\n1\r\n+values\r\n*0\r\n",
22107                "+total_results\r\n:3\r\n+warning\r\n*0\r\n"
22108            )
22109        );
22110        // The count is worked out from the rows the reply reached under this
22111        // protocol, where under RESP2 it is worked out from the first of them.
22112        assert_eq!(
22113            f.run(&[
22114                b"FT.AGGREGATE",
22115                b"sx",
22116                b"alpha",
22117                b"NOCONTENT",
22118                b"LIMIT",
22119                b"0",
22120                b"1"
22121            ]),
22122            concat!(
22123                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
22124                "%1\r\n+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
22125            )
22126        );
22127    }
22128}