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::Store;
101use yo_kv::{Clock, Db, Keyspace};
102use yo_search::Registry;
103
104use search::cursor::Cursors;
105
106/// How many databases a server has.
107///
108/// Redis's default is sixteen and its `databases` setting can change it. Ours
109/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
110/// constant. Nothing in the design needs the number to be fixed; nothing yet
111/// needs it not to be.
112pub const DATABASES: usize = 16;
113
114/// Every database's bit in [`Server::dirty`], which is what a fresh server
115/// starts on so that the first maintenance turn asks all of them.
116///
117/// A `u64` holds sixteen bits with room to spare, and the assertion below is
118/// what turns raising [`DATABASES`] past sixty four into a build failure rather
119/// than a shift that silently drops the databases past the end.
120const ALL_DATABASES: u64 = if DATABASES == 64 {
121    u64::MAX
122} else {
123    (1u64 << DATABASES) - 1
124};
125const _: () = assert!(DATABASES <= 64);
126
127/// How many keys one command throws away before it leaves the rest to the next.
128///
129/// A bound and not a loop to the end, because this runs in front of a client
130/// that is waiting for its reply, and a server a long way over its limit would
131/// otherwise hold that client for as long as it took to walk all the way back
132/// under. Sixty four is a batch's worth of commands, so a server that went over
133/// by what one batch allocated comes back under in one command, and a server
134/// whose limit was just cut in half works through it over the next few thousand
135/// rather than in one long stall. Redis bounds the same loop by a time slice
136/// instead of a count and hands the rest to a timer; there is no timer here, so
137/// the rest goes to the next command that runs.
138const EVICT_BUDGET: usize = 64;
139
140/// The `maxstore` a server with no storage limit carries.
141///
142/// Sixteen exabytes, which is every disk there is and then some, so a server
143/// that set a limit this high and a server that set none behave the same way and
144/// the only difference is what `CONFIG GET maxstore` says. Zero cannot be the
145/// sentinel because zero is a limit with a meaning: nothing may live on the
146/// file.
147const NO_MAXSTORE: u64 = u64::MAX;
148
149/// What a server says to a command that would allocate when it has no room.
150///
151/// Redis's `shared.oomerr`, word for word including the full stop, because
152/// clients match on the `OOM` prefix and people match on the sentence.
153const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
154
155/// What the connection should do after a command.
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum Flow {
158    /// Read the next command.
159    Continue,
160    /// Write what is buffered and then close, which is what `QUIT` asks for.
161    Close,
162    /// Nothing was written and nothing is owed yet.
163    ///
164    /// The client is on the waiter list and its reply comes when a key it named
165    /// has something in it or when its deadline passes, whichever happens first.
166    /// Until then the connection stops reading commands, because a client that
167    /// is waiting for an answer is not a client that has sent another question.
168    Block,
169}
170
171/// A number one thread adds to and any thread may read.
172///
173/// The add is a load, an add and a store rather than a fetch and add, which on
174/// x86 is three ordinary instructions instead of one locked one. That is sound
175/// because every counter here has exactly one writer, which is what the slots
176/// below are for: two threads never hold the same counter, so nothing can be
177/// lost between the load and the store. A reader can be a command or two behind,
178/// and `INFO` on a running server is behind by the time the reply reaches the
179/// client anyway.
180#[derive(Debug, Default)]
181pub struct Counter(AtomicU64);
182
183impl Counter {
184    /// One more.
185    fn bump(&self) {
186        self.0.store(self.get().wrapping_add(1), Relaxed);
187    }
188
189    /// One fewer, stopping at zero.
190    ///
191    /// The floor is for the gauge, which is the number of open connections: a
192    /// close that arrives without its open, which nothing can do now and a
193    /// misplaced call could, is a number that stays at zero rather than one
194    /// that wraps to eighteen quintillion clients.
195    fn drop_one(&self) {
196        self.0.store(self.get().saturating_sub(1), Relaxed);
197    }
198
199    /// What it says.
200    fn get(&self) -> u64 {
201        self.0.load(Relaxed)
202    }
203
204    /// Back to zero, which is `CONFIG RESETSTAT`.
205    fn zero(&self) {
206        self.0.store(0, Relaxed);
207    }
208}
209
210/// The numbers `INFO` reports that this layer cannot see for itself.
211///
212/// The reactor owns the sockets, so the reactor is what knows how many clients
213/// there are. It counts them here and nothing else does anything with them
214/// except report them.
215#[derive(Debug, Default)]
216pub struct Stats {
217    /// Connections open right now.
218    clients: Counter,
219    /// Connections accepted since the server started.
220    connections: Counter,
221    /// Commands run since the server started, which this layer counts itself.
222    commands: Counter,
223}
224
225impl Stats {
226    /// A connection arrived.
227    pub fn opened(&self) {
228        self.clients.bump();
229        self.connections.bump();
230    }
231
232    /// A connection went away.
233    pub fn closed(&self) {
234        self.clients.drop_one();
235    }
236}
237
238/// Every thread's [`Stats`] added together, which is what `INFO` answers.
239#[derive(Debug, Clone, Copy, Default)]
240pub struct Totals {
241    /// Connections open right now.
242    pub clients: u64,
243    /// Connections accepted since the server started.
244    pub connections: u64,
245    /// Commands run since the server started.
246    pub commands: u64,
247}
248
249thread_local! {
250    /// Which set of counters the running thread writes into.
251    ///
252    /// Claimed the first time a thread counts anything and kept for as long as
253    /// the thread runs. It is a number rather than a pointer, so a thread that
254    /// has counted on one server and then counts on another lands in the same
255    /// place in both, and a process with two servers in it shares the numbering
256    /// between them. That is the tests and it is not `yodb`, which has one.
257    static SLOT: Cell<usize> = const { Cell::new(usize::MAX) };
258}
259
260/// What one thread keeps to itself.
261///
262/// One of these per thread and not one per server, because a number every
263/// thread writes to is a cache line every thread has to own to write to it, and
264/// at a few million commands a second that one line is the server. So each
265/// thread writes into its own and whoever needs the whole picture, which is
266/// `INFO` and the maintenance turn, puts the pieces together when it asks.
267///
268/// A cache line apart for the same reason, so that two threads writing at once
269/// are not two threads passing one line back and forth.
270#[derive(Debug)]
271#[repr(align(64))]
272struct Local {
273    /// What the reactor counts.
274    stats: Stats,
275    /// A counter per command, for `INFO commandstats`.
276    cmdstats: CommandStats,
277    /// Which databases this thread has run a command against since the
278    /// maintenance turn last took the mask.
279    ///
280    /// One bit per database. The thread ors into it and the turn takes the whole
281    /// of it with a swap, which is what keeps a mark that lands during the swap
282    /// from being lost: the worst that can happen is a bit the turn has already
283    /// taken being set again, and that costs one more look at a database with
284    /// nothing to collect.
285    dirty: AtomicU64,
286    /// The mask this thread's maintenance turn is working from.
287    ///
288    /// Its own and not a shared one, because a turn reads it in place and then
289    /// clears bits of it, and a shared mask cleared that way would lose whatever
290    /// another thread marked in between. Every thread turns a loop and every
291    /// loop maintains, so what stops the same work being done twice is not the
292    /// mask but the stripe lock underneath it: two threads that both look at
293    /// database nine take turns, and the second one finds nothing left to move.
294    ///
295    /// Starts with every database set, so a server that has just been built
296    /// looks at all of them once rather than waiting to be told about the ones
297    /// something was loaded into before any command ran.
298    turn: AtomicU64,
299}
300
301impl Default for Local {
302    fn default() -> Local {
303        Local {
304            stats: Stats::default(),
305            cmdstats: CommandStats::default(),
306            dirty: AtomicU64::new(0),
307            turn: AtomicU64::new(ALL_DATABASES),
308        }
309    }
310}
311
312impl Local {
313    /// Note that a command has run against these databases.
314    fn mark(&self, dbs: u64) {
315        self.dirty.store(self.dirty.load(Relaxed) | dbs, Relaxed);
316    }
317
318    /// Add `dbs` to what this thread's turn is going to look at.
319    fn note(&self, dbs: u64) {
320        self.turn.store(self.turn.load(Relaxed) | dbs, Relaxed);
321    }
322
323    /// Take `at` off the list of databases this thread's turn will look at.
324    fn done(&self, at: usize) {
325        self.turn
326            .store(self.turn.load(Relaxed) & !(1u64 << at), Relaxed);
327    }
328
329    /// Whether this thread's turn still has database `at` to look at.
330    fn wanted(&self, at: usize) -> bool {
331        self.turn.load(Relaxed) & (1u64 << at) != 0
332    }
333}
334
335/// Room for one thread, which is what a server starts with.
336fn one_thread() -> Box<[Local]> {
337    slots(1)
338}
339
340/// Room for `threads` of them.
341fn slots(threads: usize) -> Box<[Local]> {
342    (0..threads.max(1)).map(|_| Local::default()).collect()
343}
344
345/// Where the process was started, which is what `dir` defaults to.
346///
347/// A dot if the working directory cannot be read, which happens when it has
348/// been deleted out from under a running process. That is not a reason to
349/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
350/// from the filesystem if anybody asks for one.
351fn working_dir() -> PathBuf {
352    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
353}
354
355/// One command's counters, for `INFO commandstats`.
356///
357/// Three of Redis's five. `usec` and `usec_per_call` are not here because
358/// nothing times a command, and timing one means two clock reads around a call
359/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
360/// has room for it; this does not, and a zero under a name that says microseconds
361/// is worse than an absent field, which is the same rule the rest of `INFO`
362/// follows.
363#[derive(Debug, Clone, Copy, Default)]
364pub struct CommandStat {
365    /// Times the command ran, whatever it answered.
366    pub calls: u64,
367    /// Times it was turned away before it ran, which is the wrong number of
368    /// arguments or no room under `maxmemory`.
369    pub rejected: u64,
370    /// Times it ran and answered with an error.
371    pub failed: u64,
372}
373
374impl CommandStat {
375    /// Whether this command has ever been seen.
376    ///
377    /// A row that has not is left out of the reply, which is what Redis does and
378    /// is why the section is a handful of lines on a working server rather than
379    /// one line per command in the table.
380    const fn seen(&self) -> bool {
381        self.calls != 0 || self.rejected != 0 || self.failed != 0
382    }
383}
384
385/// One command's counters as one thread keeps them.
386///
387/// The same three numbers as [`CommandStat`], which is what they add up to when
388/// `INFO` asks. This is the written form and that is the read one.
389#[derive(Debug, Default)]
390struct Row {
391    /// Times the command ran.
392    calls: Counter,
393    /// Times it was turned away before it ran.
394    rejected: Counter,
395    /// Times it ran and answered with an error.
396    failed: Counter,
397}
398
399/// A counter per command, indexed the way [`table::index_of`] says.
400///
401/// A flat array and not a map, because the dispatcher is already holding the
402/// spec and the spec's position in the table is two addresses subtracted. That
403/// makes the counting a load, an add and a store on a row the previous command
404/// of the same name has already pulled into cache.
405#[derive(Debug)]
406struct CommandStats(Box<[Row]>);
407
408impl Default for CommandStats {
409    fn default() -> CommandStats {
410        CommandStats((0..table::count()).map(|_| Row::default()).collect())
411    }
412}
413
414impl CommandStats {
415    /// The row for one command.
416    fn at(&self, spec: &'static Spec) -> &Row {
417        &self.0[table::index_of(spec)]
418    }
419}
420
421/// Where a database gets its store from, asked by database number.
422///
423/// `None` means that database cannot have one. The caller owns whatever the
424/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
425/// database, and this crate never learns what any of that is.
426pub type StoreSource = dyn FnMut(usize) -> Option<Store> + Send;
427
428/// Every thread that runs commands here shares this server, so it has to be
429/// `Send` and `Sync`, and the check is here so that a type added to it that is
430/// neither is a compile error where it was added rather than an error in the
431/// code that starts the threads.
432const _: () = {
433    const fn shareable<T: Send + Sync>() {}
434    shareable::<Server>();
435};
436
437/// Everything a server holds.
438///
439/// One per process, however many threads are serving out of it. What is inside
440/// is either shared outright, which is the counters and the settings, or behind
441/// a lock, which is the stripes and the few pieces of state a command can
442/// change. What makes this a server rather than a shard is that it is the whole
443/// of what a connection can address.
444pub struct Server {
445    dbs: Vec<Db>,
446    /// How many stripes each database is cut into, the same for all of them.
447    ///
448    /// Kept here as well as in each database so that the flat slot arithmetic
449    /// below is a multiply and a divide against a field on the server rather
450    /// than a walk asking each database how wide it is.
451    width: usize,
452    clock: Clock,
453    started_ms: u64,
454    /// Where the next maintenance turn starts looking, so that a database
455    /// under constant write load cannot hold the other fifteen's space.
456    ///
457    /// Shared, because compaction is asked for from two places: the maintenance
458    /// turn, which is one thread, and a command that went over the memory limit
459    /// and is trying to get back under it, which is any thread. Two threads that
460    /// read the same cursor start on the same database, and what that costs is
461    /// one of them finding the other has already moved what was there.
462    next_db: AtomicUsize,
463    /// One bit per database, set when a command ran against it.
464    ///
465    /// The maintenance turn after every batch used to ask all sixteen
466    /// databases whether they had anything to collect, and asking costs a load
467    /// and a store in each one. Fifteen of those are cold lines on a server
468    /// where every client is on database zero, which is every server, and the
469    /// answer is no every time. This is the cheap half of the question: a
470    /// database nobody has touched since it last said no cannot have started
471    /// saying yes.
472    ///
473    /// What the connections are holding, kept by the engine.
474    ///
475    /// Shared, because every thread has connections and the memory total is one
476    /// total. Each thread adds and subtracts its own change rather than storing
477    /// a figure it worked out, so two threads whose buffers grew in the same
478    /// moment both count.
479    conn_bytes: AtomicUsize,
480    /// The `maxmemory` limit in bytes, zero when there is not one.
481    ///
482    /// Zero is the default and it is the whole reason the check in front of
483    /// every write is one comparison against a field that is already warm. It
484    /// is read by every command on every thread and written by a client that
485    /// sends `CONFIG SET`, so it is a number the threads can share rather than
486    /// a field one of them owns.
487    maxmemory: AtomicU64,
488    /// Where a database gets a store from the first time it needs one.
489    ///
490    /// A closure and not a store, because there are sixteen databases and a
491    /// server that fills memory on database zero should not have opened
492    /// anything for the other fifteen. Nothing is asked of this until a memory
493    /// limit is actually reached, so a server that never fills memory never
494    /// opens a file, and a server that has no file never has one of these.
495    ///
496    /// `None` from the closure means that database cannot have one, which is
497    /// how the caller says the file it opened has no more room for logs.
498    ///
499    /// Behind a lock because it is a closure the caller gave us and there is no
500    /// saying it can be run by two threads at once. It is asked once per
501    /// database, the first time that database has to move something, so a
502    /// server that has reached its memory limit takes this lock sixteen times
503    /// in its life.
504    store: Lock<Option<Box<StoreSource>>>,
505    /// The `maxstore` limit in bytes, `None` when there is not one.
506    ///
507    /// The storage limit, and the other half of the inversion `14` section 4.1
508    /// describes. `maxmemory` is a limit on memory and the right answer to a
509    /// memory limit on a system with a file under it is to move data to the
510    /// file, not to delete it. Deleting is the right answer to a limit on the
511    /// file, and this is that limit.
512    ///
513    /// Zero is not "no limit" here, which is the one place this reads
514    /// differently from `maxmemory` and is the difference that makes a drop in
515    /// cache possible. A storage budget of zero bytes means nothing may live on
516    /// the file, so migration cannot make room and eviction is the only thing
517    /// left, which is Redis exactly. `None` is no limit and is the default,
518    /// which with `noeviction` means the database grows until the disk is full
519    /// and then writes fail, which is what a database does.
520    ///
521    /// Shared between the threads the same way `maxmemory` is, and no limit is
522    /// [`NO_MAXSTORE`] rather than a second field saying whether the first one
523    /// counts. Two fields cannot be read as one, and a limit that was on when
524    /// the bytes were read and off by the time the number was is a limit that
525    /// answers from a server that never existed.
526    maxstore: AtomicU64,
527    /// What [`Server::memory_bytes`] said at the last maintenance turn.
528    ///
529    /// The reading is a walk over every collection in every database and cannot
530    /// go on a command path, so the command path reads this instead and is at
531    /// most one batch behind. What that costs is overshoot: a server can end a
532    /// batch holding one batch's worth of allocation more than its limit before
533    /// anything notices. A batch is 64 commands, so that is bounded by what 64
534    /// commands can allocate and not by how long the server runs.
535    ///
536    /// Only kept up to date when there is a limit to judge it against. A server
537    /// with no `maxmemory` never reads it and never pays for it.
538    ///
539    /// Shared, because it is read in front of every write on every thread and
540    /// written by whichever thread last took a reading. A reader that catches it
541    /// mid write gets one of the two readings and both of them were true a
542    /// moment ago, which is all this number ever claims to be.
543    used: AtomicUsize,
544    /// Which database the next eviction draws from.
545    ///
546    /// Its own cursor and not [`Server::next_db`], because eviction and
547    /// compaction move at different rates and sharing one would make the
548    /// database that gets compacted depend on how many keys were evicted.
549    ///
550    /// Shared for the same reason [`Server::next_db`] is, and with the same
551    /// answer: two threads evicting at once may pick the same database, and one
552    /// of them finds the other got there first and moves on.
553    evict_db: AtomicUsize,
554    /// Which database the next active expiry sweep starts at.
555    ///
556    /// A third cursor for the same reason there is a second one. A sweep runs on
557    /// every turn of the loop and compaction runs when there is dead space, so
558    /// sharing a cursor would make which database gets swept depend on which one
559    /// was last collected.
560    expire_db: AtomicUsize,
561    /// The millisecond the last active expiry sweep ran on, so the next one on
562    /// the same millisecond does not bother.
563    ///
564    /// One for the server and not one per thread, so the sweeping a server does
565    /// is a function of how long it has been running and not of how many threads
566    /// it was started with. Two threads that read the same millisecond can both
567    /// decide to sweep, which costs one extra sweep of a budget that is already
568    /// small and cannot happen twice for the same millisecond more than once per
569    /// thread.
570    expire_ms: AtomicU64,
571    /// Clients parked on a blocking command.
572    ///
573    /// Behind a lock because a client parks on the thread that ran its command
574    /// and is woken by whichever thread later puts something under a key it
575    /// named, and those are not the same thread. The lock is only ever taken to
576    /// park somebody, to serve somebody or to forget a connection that has gone,
577    /// so a command that does not block never touches it.
578    waiters: Lock<Waiters>,
579    /// How many clients are parked.
580    ///
581    /// Beside the list rather than read out of it, because every command asks
582    /// whether anybody is waiting and nearly every answer is no. Taking a lock
583    /// to be told no would be a cache line every thread has to own to ask, which
584    /// is the cost the list was put behind a lock to avoid.
585    ///
586    /// Written under the lock, by whoever changed the list, so the number and
587    /// the list agree except while a change is in progress. A reader that asks
588    /// during one is told about the moment before it, and the worst that costs
589    /// is a walk of the list that serves nobody or one that has not started yet
590    /// and happens on the next command instead.
591    parked: AtomicUsize,
592    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
593    ///
594    /// Empty on a server nobody has migrated a key out of, which is nearly all
595    /// of them, and it costs a vector's three words to be empty.
596    ///
597    /// Behind a lock because a socket cannot be written by two threads at once
598    /// and a cache of them cannot be searched by one while another is taking an
599    /// entry out. It is held for the whole of a migration, which is a round trip
600    /// to another server, so two threads migrating at the same time take turns.
601    /// That is the right way round: the alternative is a socket per thread per
602    /// peer, and a `MIGRATE` is not what a server spends its time on.
603    peers: Lock<migrate::Peers>,
604    /// What each thread that runs commands here keeps to itself.
605    ///
606    /// A fixed list, because a thread reading its own entry must not have the
607    /// list move under it, and how many threads there will be is known before
608    /// any of them starts. A server nobody told otherwise has one.
609    locals: Box<[Local]>,
610    /// How many entries have been handed out.
611    claimed: AtomicUsize,
612    /// The next client id, which is what `CLIENT ID` answers.
613    ///
614    /// On the server and not on a front, because CLIENT LIST and CLIENT KILL
615    /// name a client by this number across the whole server, and two threads
616    /// counting on their own would hand the same number to two clients. Starts
617    /// at one so that zero is never a client, which is what makes it usable as
618    /// the id of a command that came from nowhere.
619    next_client: AtomicU64,
620    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
621    ///
622    /// Absolute, and resolved once when the server is built rather than every
623    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
624    /// entitled to hand one of them to a copy tool, so a relative path that
625    /// meant something different after a `chdir` would be a path that stops
626    /// working for reasons nobody could see.
627    dir: PathBuf,
628    /// What backup is running, if one is.
629    ///
630    /// On the server and not on a session, because a backup outlives the
631    /// connection that asked for it and any other connection can seal it.
632    ///
633    /// Behind a lock because there is one backup at a time and any thread can be
634    /// the one that starts, seals or abandons it. It is held while the base file
635    /// is written, which is what keeps two `BACKUP START` commands from writing
636    /// over each other's files.
637    backup: Lock<backup::State>,
638    /// Whether a sealed backup is sitting on disk.
639    ///
640    /// Beside the state rather than read out of it, because every batch of
641    /// commands asks whether there is a backup old enough to sweep away and on
642    /// nearly every server the answer is that there is no backup at all. A load
643    /// answers that. Written under the lock by whoever moved the phase, so a
644    /// reader that asks mid-change sees the moment before and sweeps one batch
645    /// later, which is a file staying on disk for a few microseconds longer than
646    /// it had to.
647    sealed: AtomicBool,
648    /// The search indexes and the names pointing at them.
649    ///
650    /// On the server and not on a database, which is the one collection in this
651    /// build that is. A real server keeps its indexes in the search module, the
652    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
653    /// indexes made on database zero. `search.rs` has the rest of why.
654    ///
655    /// A server nobody has made an index on holds two empty vectors here, which
656    /// is six words and no allocation.
657    ///
658    /// Behind a lock because an index is made and dropped by whichever thread
659    /// ran the command, and the table it goes in is one table. Only the `FT`
660    /// commands take it, so nothing a working server spends its time on comes
661    /// through here.
662    search: Lock<Registry>,
663    /// The replies that came back in pieces and have pieces left.
664    ///
665    /// Beside the indexes rather than inside one, because a cursor is read
666    /// under its own number and a real server resolves the index name on a read
667    /// and then pays no attention to it, so a cursor made on one index reads
668    /// through the name of another. Behind a lock for the reason the registry is
669    /// behind one, and a server nobody has opened a cursor on holds an empty map
670    /// here.
671    cursors: Lock<Cursors>,
672    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
673    ///
674    /// A flag rather than an exit, because the command layer is not what owns
675    /// the process. It runs inside a batch that has other commands behind it
676    /// and inside a driver that has a socket file to take away and a file to
677    /// close, and a server that calls `exit` from a command handler skips all
678    /// of that. So the command says stop and the driver stops, on the same turn
679    /// and through the same door a signal uses.
680    stopping: AtomicBool,
681}
682
683impl Server {
684    /// A server with [`DATABASES`] empty databases on the system clock.
685    #[must_use]
686    pub fn new() -> Server {
687        let clock = Clock::system();
688        Server {
689            dbs: (0..DATABASES)
690                .map(|_| Db::with_clock(clock.clone(), 1))
691                .collect(),
692            width: 1,
693            started_ms: clock.now_ms(),
694            clock,
695            next_db: AtomicUsize::new(0),
696            conn_bytes: AtomicUsize::new(0),
697            maxmemory: AtomicU64::new(0),
698            store: Lock::new(None),
699            maxstore: AtomicU64::new(NO_MAXSTORE),
700            used: AtomicUsize::new(0),
701            evict_db: AtomicUsize::new(0),
702            expire_db: AtomicUsize::new(0),
703            expire_ms: AtomicU64::new(0),
704            waiters: Lock::default(),
705            parked: AtomicUsize::new(0),
706            peers: Lock::default(),
707            locals: one_thread(),
708            claimed: AtomicUsize::new(0),
709            next_client: AtomicU64::new(1),
710            dir: working_dir(),
711            backup: Lock::default(),
712            sealed: AtomicBool::new(false),
713            search: Lock::new(Registry::new()),
714            cursors: Lock::default(),
715            stopping: AtomicBool::new(false),
716        }
717    }
718
719    /// A server whose databases are cut into `width` stripes each.
720    ///
721    /// Not reachable from the command line yet. Every command group answers on
722    /// a server of any width now and so does everything that walks a whole
723    /// database, and the tests run each group at a width of one and a width of
724    /// eight and check the two agree.
725    ///
726    /// What is left before this is what `--threads` sets is the engine. A
727    /// database being several objects is what makes more than one thread
728    /// possible, and it is not what makes more than one thread happen.
729    #[must_use]
730    pub fn with_width(width: usize) -> Server {
731        let mut server = Server::new();
732        // The server's own clock and not a fresh one, because a database
733        // reading a different clock from the server it is on is a database
734        // whose keys expire against a time nobody set.
735        let clock = server.clock.clone();
736        server.dbs = (0..DATABASES)
737            .map(|_| Db::with_clock(clock.clone(), width))
738            .collect();
739        server.width = server.dbs[0].width();
740        server
741    }
742
743    /// A server on a clock the caller moves by hand, for tests.
744    #[must_use]
745    pub fn with_clock(clock: Clock) -> Server {
746        Server {
747            dbs: (0..DATABASES)
748                .map(|_| Db::with_clock(clock.clone(), 1))
749                .collect(),
750            width: 1,
751            started_ms: clock.now_ms(),
752            clock,
753            next_db: AtomicUsize::new(0),
754            conn_bytes: AtomicUsize::new(0),
755            maxmemory: AtomicU64::new(0),
756            store: Lock::new(None),
757            maxstore: AtomicU64::new(NO_MAXSTORE),
758            used: AtomicUsize::new(0),
759            evict_db: AtomicUsize::new(0),
760            expire_db: AtomicUsize::new(0),
761            expire_ms: AtomicU64::new(0),
762            waiters: Lock::default(),
763            parked: AtomicUsize::new(0),
764            peers: Lock::default(),
765            locals: one_thread(),
766            claimed: AtomicUsize::new(0),
767            next_client: AtomicU64::new(1),
768            dir: working_dir(),
769            backup: Lock::default(),
770            sealed: AtomicBool::new(false),
771            search: Lock::new(Registry::new()),
772            cursors: Lock::default(),
773            stopping: AtomicBool::new(false),
774        }
775    }
776
777    /// One database, by index.
778    ///
779    /// A caller that knows which key it wants names the one stripe the key is
780    /// on rather than working over the whole thing, which is what `at` and its
781    /// neighbours on [`Db`] are for. A caller that is about a database rather
782    /// than about a key, which is the snapshot walk and a setting, works over
783    /// all of them.
784    ///
785    /// The database is marked as having had something run against it, which is
786    /// what this does that [`Server::striped_ref`] does not. Anything that only
787    /// reads asks for that one and leaves the mark alone.
788    ///
789    /// The borrow is shared, and what makes that enough is that a database is
790    /// several stripes behind a lock each. A caller that wants to change
791    /// something holds the stripe it is changing, so two threads working on two
792    /// keys work at once and two working on one key take turns, which is the
793    /// whole point of cutting a database up.
794    ///
795    /// # Panics
796    ///
797    /// If `i` is not a database. `SELECT` is the only way a client changes the
798    /// index and it checks, so an index that is out of range here is a bug in
799    /// the caller and not something a client can ask for.
800    pub fn striped(&self, i: usize) -> &Db {
801        self.mine().mark(1u64 << i);
802        &self.dbs[i]
803    }
804
805    /// Every keyspace on the server, which is every stripe of every database.
806    ///
807    /// What the aggregates walk. A total over the whole server is a total over
808    /// all of these and the stripe boundaries do not appear in it, which is
809    /// what makes the numbers `INFO` reports the same numbers whatever the
810    /// server was cut into.
811    fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
812        self.dbs
813            .iter()
814            .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
815    }
816
817    /// How many keyspaces there are, counting every stripe of every database.
818    ///
819    /// The maintenance turns walk these rather than the databases, because a
820    /// stripe is the thing that holds an arena and a deadline heap and so it is
821    /// the thing that has anything to collect.
822    const fn slots(&self) -> usize {
823        DATABASES * self.width
824    }
825
826    /// Which database slot `i` belongs to.
827    const fn slot_db(&self, i: usize) -> usize {
828        i / self.width
829    }
830
831    /// Keyspace `i` of [`Server::slots`].
832    fn slot(&self, i: usize) -> Held<'_, Keyspace> {
833        let (db, stripe) = (i / self.width, i % self.width);
834        self.dbs[db].hold_stripe(stripe)
835    }
836
837    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
838    #[must_use]
839    pub fn dir(&self) -> &Path {
840        &self.dir
841    }
842
843    /// Point the server at a different directory, which `yodb serve --dir` does.
844    ///
845    /// Only before it is serving. There is no `CONFIG SET dir` here and there
846    /// is none on a real server either without turning protected configs on,
847    /// for the good reason that moving it out from under a running backup would
848    /// leave files nothing can find again.
849    pub fn set_dir(&mut self, dir: PathBuf) {
850        self.dir = dir;
851    }
852
853    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
854    ///
855    /// Once per batch, from the same maintenance turn that collects the arena.
856    /// It reads two fields and returns on a server that has never taken a
857    /// backup, which is nearly all of them.
858    pub fn backup_expire(&self) {
859        backup::expire(self);
860    }
861
862    /// Ask for the server to stop, which is what `SHUTDOWN` does.
863    ///
864    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
865    /// or ends the process, because none of those belong to this layer, and a
866    /// batch that is halfway through still has to finish and be written out.
867    pub fn stop(&self) {
868        self.stopping.store(true, Release);
869    }
870
871    /// Whether somebody has asked the server to stop.
872    ///
873    /// Read once per turn by the loop, next to the flag a signal sets. The two
874    /// mean the same thing and are separate only because one arrives from the
875    /// operating system and the other from a client.
876    #[must_use]
877    pub fn stopping(&self) -> bool {
878        self.stopping.load(Acquire)
879    }
880
881    /// One database, by index, without taking it mutably.
882    ///
883    /// What the prefetch stage needs. It runs for all 64 commands in a batch
884    /// before any of them executes, so it cannot hold the mutable borrow `run`
885    /// is about to want, and it does not need one: warming a cache line reads
886    /// nothing and changes nothing.
887    #[must_use]
888    pub fn striped_ref(&self, i: usize) -> &Db {
889        &self.dbs[i]
890    }
891
892    /// The stripe that answers for a database when a setting is read back.
893    ///
894    /// A ladder setting and an eviction policy are one number on a real server,
895    /// and the fact that every stripe of every database carries a copy of it is
896    /// ours rather than the client's problem. A write puts the same value on
897    /// every one of them, so any stripe answers for all of them and this is the
898    /// first one.
899    fn settings(&self) -> Held<'_, Keyspace> {
900        self.dbs[0].hold_stripe(0)
901    }
902
903    /// Take a new clock reading, which every database is looking at.
904    ///
905    /// Once per turn of the event loop, which is the only place time moves. A
906    /// command asking what the time is gets the answer the whole batch got, so
907    /// two keys written by the same batch expire together (`04` section 3).
908    ///
909    /// Every thread does this on every turn of its own loop and they do not
910    /// have to agree about when. The reading is only stored when the
911    /// millisecond has changed, so what the threads are sharing is a line that
912    /// is written about a thousand times a second and read millions.
913    pub fn refresh_clock(&self) {
914        self.clock.refresh();
915    }
916
917    /// Move every clock here on by `ms`, for tests about expiry.
918    ///
919    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
920    /// except that it moves from wherever the clock is rather than to a stated
921    /// moment, which is what a test that wants a key to have expired asks for.
922    pub fn advance_clock_ms(&self, ms: u64) {
923        let now = self.clock.now_ms() + ms;
924        self.set_clock_ms(now);
925    }
926
927    /// Move every clock here to `ms` by hand, for tests about expiry.
928    ///
929    /// A test cannot wait a hundred seconds and a test that waits a hundred
930    /// milliseconds is a test that fails on a loaded machine, so time moves on
931    /// request. The system clock underneath will overwrite this on the next
932    /// [`Server::refresh_clock`], which is why this is only useful in a test
933    /// that drives commands directly rather than through the event loop.
934    pub fn set_clock_ms(&self, ms: u64) {
935        self.clock.set(ms);
936    }
937
938    /// Seconds since this server was built.
939    #[must_use]
940    pub fn uptime_secs(&self) -> u64 {
941        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
942    }
943
944    /// Bytes held by every database's index and arena, plus the read and reply
945    /// buffers of every connection.
946    ///
947    /// The buffers are in here because they are real and because Redis counts
948    /// its own, so leaving them out would make the one number people compare
949    /// flattering rather than true. They are not a database, so nothing in the
950    /// keyspace can change them and the engine has to say when they move.
951    #[must_use]
952    pub fn memory_bytes(&self) -> usize {
953        self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes()
954    }
955
956    /// What the keyspace itself is holding, live records only.
957    ///
958    /// `used_memory` minus this is what the store costs to run: the index, the
959    /// space dead records are sitting in until compaction gets to them, and the
960    /// connections' buffers.
961    #[must_use]
962    pub fn dataset_bytes(&self) -> usize {
963        self.keyspaces()
964            .map(|db| db.map().arena().live_bytes() as usize)
965            .sum()
966    }
967
968    /// Bytes the arenas are holding, live and dead together.
969    #[must_use]
970    pub fn arena_bytes(&self) -> usize {
971        self.keyspaces()
972            .map(|db| db.map().arena().reserved_bytes() as usize)
973            .sum()
974    }
975
976    /// Bytes the indexes are holding.
977    #[must_use]
978    pub fn index_bytes(&self) -> usize {
979        self.keyspaces()
980            .map(|db| db.map().index().memory_bytes())
981            .sum()
982    }
983
984    /// What arena compaction has cost, across every database.
985    ///
986    /// The write amplification of value separation, which is invisible from the
987    /// outside otherwise: a client that writes a megabyte can leave the store
988    /// copying several more, and the only sign of it without these is that the
989    /// writes got slower.
990    #[must_use]
991    pub fn compaction(&self) -> yo_kv::Compaction {
992        self.keyspaces().map(|db| db.map().compaction()).fold(
993            yo_kv::Compaction::default(),
994            |a, b| yo_kv::Compaction {
995                walked: a.walked + b.walked,
996                moved: a.moved + b.moved,
997                bytes: a.bytes + b.bytes,
998            },
999        )
1000    }
1001
1002    /// Arena segments whose pages are real, across every database.
1003    #[must_use]
1004    pub fn segment_count(&self) -> usize {
1005        self.keyspaces()
1006            .map(|db| db.map().arena().resident_segments())
1007            .sum()
1008    }
1009
1010    /// What the connections' read and reply buffers are holding.
1011    #[must_use]
1012    pub fn conn_bytes(&self) -> usize {
1013        self.conn_bytes.load(Relaxed)
1014    }
1015
1016    /// Note that the connections are holding `delta` bytes more than they were,
1017    /// or fewer when it is negative.
1018    ///
1019    /// A delta and not a total because the alternative is a walk over every
1020    /// connection, and the walk would have to happen on a turn of the loop
1021    /// rather than when `INFO` asks, which puts the cost of a report on the
1022    /// command path of a server nobody is asking.
1023    pub fn note_conn_bytes(&self, delta: isize) {
1024        // A read and a write and not a fetch and add, because the number is a
1025        // sum of signed changes and the saturating part has to happen in the
1026        // middle. Two threads that change their buffers in the same instant can
1027        // lose one of the two changes, which is a report that is a few kilobytes
1028        // out until the next connection on either thread moves it again.
1029        self.conn_bytes
1030            .store(self.conn_bytes().saturating_add_signed(delta), Relaxed);
1031    }
1032
1033    /// Keys reclaimed by running into them after their deadline.
1034    #[must_use]
1035    pub fn expired_keys(&self) -> u64 {
1036        self.keyspaces().map(|db| db.expired_keys()).sum()
1037    }
1038
1039    /// Keys thrown away to make room, which is the other number entirely.
1040    #[must_use]
1041    pub fn evicted_keys(&self) -> u64 {
1042        self.keyspaces().map(|db| db.evicted_keys()).sum()
1043    }
1044
1045    /// Every command that has been seen, with its counters.
1046    ///
1047    /// Only the ones that have. A server reports a handful of lines rather than
1048    /// one per command in the table, which is what Redis does and is the
1049    /// difference between a section a person can read and one they cannot.
1050    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
1051        (0..table::count())
1052            .map(|at| (table::name_at(at), self.command_stat(at)))
1053            .filter(|(_, row)| row.seen())
1054    }
1055
1056    /// One command's counters, added up over every thread.
1057    fn command_stat(&self, at: usize) -> CommandStat {
1058        let mut sum = CommandStat::default();
1059        for thread in &self.locals {
1060            let row = &thread.cmdstats.0[at];
1061            sum.calls += row.calls.get();
1062            sum.rejected += row.rejected.get();
1063            sum.failed += row.failed.get();
1064        }
1065        sum
1066    }
1067
1068    /// The counters the calling thread writes into.
1069    ///
1070    /// The first call on a thread claims a set and every call after it is a
1071    /// thread local read and an index. A server asked to count from more threads
1072    /// than it was built for wraps round and shares a set, which loses the odd
1073    /// count between two threads and cannot happen to a server `yodb serve`
1074    /// built, because that one is told how many threads it will have before it
1075    /// starts any of them.
1076    pub fn counted(&self) -> &Stats {
1077        &self.mine().stats
1078    }
1079
1080    /// The next client id, taken.
1081    ///
1082    /// Every accept anywhere on this server comes through here, so no two
1083    /// clients share a number however many threads are accepting.
1084    pub fn next_client(&self) -> u64 {
1085        self.next_client.fetch_add(1, Relaxed)
1086    }
1087
1088    /// Which set of per thread state the calling thread is on.
1089    ///
1090    /// The number a blocked client is filed under, so that the thread holding
1091    /// that client's connection is the one that answers it. Claims a set on the
1092    /// first call the same way [`Server::counted`] does, and gives back the same
1093    /// number every time after.
1094    pub fn my_slot(&self) -> usize {
1095        self.mine_at()
1096    }
1097
1098    /// Everything the calling thread keeps to itself.
1099    fn mine(&self) -> &Local {
1100        &self.locals[self.mine_at()]
1101    }
1102
1103    /// The calling thread's place in `locals`, claiming one if it has none.
1104    ///
1105    /// Wraps round when more threads count here than the server was built for,
1106    /// which shares a set between two threads and loses the odd count. That
1107    /// cannot happen to the server `yodb serve` builds, because it is told how
1108    /// many threads it will have before it starts any of them.
1109    fn mine_at(&self) -> usize {
1110        let mut slot = SLOT.get();
1111        if slot == usize::MAX {
1112            slot = self.claimed.fetch_add(1, Relaxed);
1113            SLOT.set(slot);
1114        }
1115        slot % self.locals.len()
1116    }
1117
1118    /// Every thread's numbers added together, which is what `INFO` reports.
1119    #[must_use]
1120    pub fn totals(&self) -> Totals {
1121        let mut sum = Totals::default();
1122        for thread in &self.locals {
1123            sum.clients += thread.stats.clients.get();
1124            sum.connections += thread.stats.connections.get();
1125            sum.commands += thread.stats.commands.get();
1126        }
1127        sum
1128    }
1129
1130    /// Put the totals back to zero, which is `CONFIG RESETSTAT`.
1131    ///
1132    /// Every thread's set and not only the one asking, since the number the
1133    /// client is resetting is the sum it was just shown. The open connections
1134    /// are left alone because that is a gauge and not a total: the connections
1135    /// are still open.
1136    pub fn reset_stats(&self) {
1137        for thread in &self.locals {
1138            thread.stats.connections.zero();
1139            thread.stats.commands.zero();
1140        }
1141    }
1142
1143    /// Say how many threads will run commands here, before any of them does.
1144    ///
1145    /// What it changes is how many sets of counters there are. Called once at
1146    /// startup by whoever is about to start the threads, and calling it on a
1147    /// running server throws away what has been counted so far, which is why it
1148    /// wants the server to itself.
1149    pub fn set_threads(&mut self, threads: usize) {
1150        self.locals = slots(threads);
1151        self.claimed = AtomicUsize::new(0);
1152    }
1153
1154    /// The `maxmemory` limit in bytes, zero when there is not one.
1155    #[must_use]
1156    pub fn maxmemory(&self) -> u64 {
1157        self.maxmemory.load(Relaxed)
1158    }
1159
1160    /// Set the limit, and take a reading straight away.
1161    ///
1162    /// The reading is here rather than left to the next maintenance turn because
1163    /// a client that sets the limit and sends a write in the same batch expects
1164    /// the write to be judged against the limit it just set, and because the
1165    /// cached number is meaningless until the first time there is a limit to
1166    /// compare it with.
1167    ///
1168    /// Turning the limit on also turns on the running total every slab keeps of
1169    /// what its collections hold, and turning it off turns that back off, so a
1170    /// server with no limit is not paying to count something nobody reads. The
1171    /// first reading after switching it on is the walk that the total starts
1172    /// from, and it is the only walk.
1173    pub fn set_maxmemory(&self, bytes: u64) {
1174        self.maxmemory.store(bytes, Relaxed);
1175        for db in &self.dbs {
1176            db.track_memory(bytes != 0);
1177        }
1178        self.used.store(self.settled_memory(), Relaxed);
1179    }
1180
1181    /// Say where a database should get its store from when it needs one.
1182    ///
1183    /// This is what turns the eviction inversion on. Until it is called every
1184    /// database answers a memory limit by evicting, which is Redis, and after it
1185    /// is called a database under memory pressure moves values to whatever the
1186    /// closure hands back instead of throwing keys away.
1187    ///
1188    /// Called at most once per database and only under pressure, so a server
1189    /// that is given a file and never fills memory never touches it.
1190    pub fn set_store_source(
1191        &mut self,
1192        source: impl FnMut(usize) -> Option<Store> + Send + 'static,
1193    ) {
1194        *self.store.lock() = Some(Box::new(source));
1195    }
1196
1197    /// Whether this server has been given somewhere to put cold values.
1198    #[must_use]
1199    pub fn has_store_source(&self) -> bool {
1200        self.store.lock().is_some()
1201    }
1202
1203    /// Open database `at`'s store, if it has not got one and there is one to be
1204    /// had.
1205    ///
1206    /// A store that will not open leaves the database where it was, which is
1207    /// evicting, because a memory limit that cannot be answered by moving data
1208    /// still has to be answered.
1209    fn attach_store(&self, at: usize) {
1210        if self.slot(at).store_bytes().is_some() {
1211            return;
1212        }
1213        // The closure is run with its lock held and the keyspace is taken after
1214        // it has answered, so the file is opened once however many threads asked
1215        // for it and the stripe is not held while a file is being opened.
1216        let mut source = self.store.lock();
1217        let Some(source) = source.as_mut() else {
1218            return;
1219        };
1220        if let Some(blocks) = source(at) {
1221            self.slot(at).attach(blocks);
1222        }
1223    }
1224
1225    /// The `maxstore` limit in bytes, `None` when there is not one.
1226    #[must_use]
1227    pub fn maxstore(&self) -> Option<u64> {
1228        match self.maxstore.load(Relaxed) {
1229            NO_MAXSTORE => None,
1230            bytes => Some(bytes),
1231        }
1232    }
1233
1234    /// Set the storage limit, or clear it with `None`.
1235    ///
1236    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
1237    /// total, because this limit is compared against a number the store keeps
1238    /// and answers on demand, not against a walk.
1239    pub fn set_maxstore(&self, bytes: Option<u64>) {
1240        self.maxstore.store(bytes.unwrap_or(NO_MAXSTORE), Relaxed);
1241    }
1242
1243    /// What every attached store is holding, for `INFO memory`.
1244    ///
1245    /// Zero on a server with nothing attached, which is not the same as a server
1246    /// whose file is empty, and [`Server::regime`] is the field that tells those
1247    /// two apart.
1248    #[must_use]
1249    pub fn store_bytes(&self) -> u64 {
1250        self.keyspaces().filter_map(|db| db.store_bytes()).sum()
1251    }
1252
1253    /// What the file has been asked to do, added up over every database.
1254    ///
1255    /// Counters and not levels, so they only ever go up and a run is the
1256    /// difference between two readings. G9 is a ratio over these: the faults a
1257    /// run took, divided by the point reads it issued, has to come out at 1.05
1258    /// or less with a working set ten times memory. There is no way to work that
1259    /// out from outside the server, so it is reported rather than inferred.
1260    ///
1261    /// A fault is a read that went to the store. Whether it also went to the
1262    /// device depends on the store: a log serves a read out of a resident page
1263    /// without touching anything. At ten times memory almost every fault is a
1264    /// real read, which is why the gate is written against this number, but the
1265    /// two are not the same thing and a run tight against the bar should be
1266    /// checked against what the operating system says.
1267    #[must_use]
1268    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
1269        let mut total = yo_kv::tier::Stats::default();
1270        for db in self.keyspaces() {
1271            let Some(tier) = db.tier() else { continue };
1272            let s = tier.stats();
1273            total.demoted += s.demoted;
1274            total.promoted += s.promoted;
1275            total.faults += s.faults;
1276            total.served += s.served;
1277            total.bytes_out += s.bytes_out;
1278            total.bytes_in += s.bytes_in;
1279        }
1280        total
1281    }
1282
1283    /// Which way this server answers a memory limit, in one word for `INFO`.
1284    ///
1285    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
1286    /// inversion: a memory limit moves values to the file and nothing stored is
1287    /// lost. A server reports one word rather than leaving an operator to work
1288    /// it out from a limit, a setting and whether a file happens to be open.
1289    #[must_use]
1290    pub fn regime(&self) -> &'static str {
1291        if (0..self.slots()).any(|at| self.migrates(at)) {
1292            "migrate"
1293        } else {
1294            "evict"
1295        }
1296    }
1297
1298    /// Whether database `at` answers a memory limit by moving values to the
1299    /// file rather than by throwing keys away.
1300    ///
1301    /// Three things have to hold. There has to be somewhere to move them, which
1302    /// is a store attached to that database or a source that can open one, and
1303    /// on a server that was never given a file this is false everywhere and
1304    /// every database behaves exactly as it did.
1305    /// The storage budget has to be more than nothing, which is what
1306    /// `maxstore 0` says it is not. And the file has to be under that budget,
1307    /// because a full file is a storage limit reached and eviction is the right
1308    /// answer to a storage limit.
1309    fn migrates(&self, at: usize) -> bool {
1310        let cap = self.maxstore();
1311        if cap == Some(0) {
1312            return false;
1313        }
1314        // Out of the stripe first. A match keeps whatever it is looking at
1315        // alive for the whole of itself, and that would be this stripe held
1316        // across the arms for no reason.
1317        let bytes = self.slot(at).store_bytes();
1318        match bytes {
1319            Some(held) => cap.is_none_or(|cap| held < cap),
1320            // Nothing attached, but somewhere to get one from the moment this
1321            // database needs it, which is what makes the answer yes rather than
1322            // no. Opening it here would mean `INFO` opened files.
1323            None => self.store.lock().is_some(),
1324        }
1325    }
1326
1327    /// Take a fresh memory reading, which the maintenance turn does once a batch.
1328    ///
1329    /// Nothing at all when there is no limit, which is the default and is every
1330    /// server that has not asked for one.
1331    pub fn refresh_memory(&self) {
1332        if self.maxmemory() != 0 {
1333            self.used.store(self.settled_memory(), Relaxed);
1334        }
1335    }
1336
1337    /// [`Server::memory_bytes`], asked the cheap way.
1338    ///
1339    /// The same number. The difference is that this asks each database only
1340    /// about the collections that could have moved since the last time, which is
1341    /// what a batch touched rather than what the server holds, so it can be
1342    /// asked once a batch and again on every command that is over the limit.
1343    fn settled_memory(&self) -> usize {
1344        self.keyspaces()
1345            .map(|mut db| db.settled_memory_bytes())
1346            .sum::<usize>()
1347            + self.conn_bytes()
1348    }
1349
1350    /// Make room under the `maxmemory` limit, throwing keys away if that is what
1351    /// it takes. Answers whether there is anything left it could throw away.
1352    ///
1353    /// Redis runs the same thing from `processCommand` before every command and
1354    /// so does this: a client that writes has to be judged at the moment it
1355    /// writes, not a batch later, or the limit is a suggestion.
1356    ///
1357    /// Three things happen in the loop and all three are needed. Eviction picks
1358    /// a key and drops it. Compaction gives the pages back, because dropping a
1359    /// key marks its record dead and returns nothing on its own, so a loop that
1360    /// only evicted would throw the whole keyspace away and watch the number
1361    /// stay where it was. The reading is taken again each time round, because
1362    /// the two of them together are the only thing that moves it.
1363    ///
1364    /// # Why running out of budget is not a no
1365    ///
1366    /// `false` means there was nothing left to evict, which is `noeviction`, or
1367    /// a `volatile` policy on a database where nothing has a deadline, or a
1368    /// keyspace that is already empty. It does not mean the server is still over
1369    /// its limit, and that difference is Redis's: `performEvictions` answers
1370    /// `EVICT_FAIL` only when it has run out of things to delete, and
1371    /// `processCommand` refuses the client on that and on nothing else. Running
1372    /// out of time part way through a job it is doing well comes back as
1373    /// `EVICT_RUNNING` and the command goes through, because a server that is
1374    /// evicting steadily and refusing every write while it does it is worse for
1375    /// the client than a little overshoot.
1376    ///
1377    /// # What the limit is worth
1378    ///
1379    /// Space comes back a segment at a time and a segment is two megabytes, so
1380    /// this holds a server to its limit give or take a segment. A `maxmemory` of
1381    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
1382    /// megabytes is asking for a precision this store does not have.
1383    pub fn make_room(&self) -> bool {
1384        let limit = self.maxmemory();
1385        if limit == 0 || self.used.load(Relaxed) as u64 <= limit {
1386            return true;
1387        }
1388        // The cached reading is a batch old and the batch may have compacted
1389        // since, so take a fresh one before throwing anything away. It is the
1390        // settled reading and not the walk, so what this costs is the handful of
1391        // collections the last batch touched and not the whole database.
1392        let mut used = self.settled_memory();
1393        self.used.store(used, Relaxed);
1394        let mut budget = EVICT_BUDGET;
1395        while used as u64 > limit {
1396            let over = used - limit as usize;
1397            if !self.relieve_step(over) {
1398                return false;
1399            }
1400            self.compact_hard_step();
1401            used = self.settled_memory();
1402            self.used.store(used, Relaxed);
1403            budget -= 1;
1404            if budget == 0 {
1405                break;
1406            }
1407        }
1408        true
1409    }
1410
1411    /// Give back `over` bytes from whichever database can, by moving values to
1412    /// the file where there is one and by throwing keys away where there is not.
1413    ///
1414    /// The two answers are the eviction inversion and which one a database gets
1415    /// is [`Server::migrates`]. Answers whether anything was given back at all,
1416    /// and `false` is what refuses the client's write.
1417    ///
1418    /// A store that will not take the bytes counts as nothing given back, so the
1419    /// write is refused rather than turned into a deletion. A disk that is
1420    /// misbehaving is a reason to stop accepting writes and it is not a reason
1421    /// to start losing data that was accepted already.
1422    ///
1423    /// Round robin from a cursor rather than always starting at database zero,
1424    /// so a server using more than one of them does not empty the first before
1425    /// touching the second. Almost every server is on database zero only, where
1426    /// this is one call that answers and fifteen that say the map is empty.
1427    fn relieve_step(&self, over: usize) -> bool {
1428        let from = self.evict_db.load(Relaxed);
1429        for turn in 0..self.slots() {
1430            let i = (from + turn) % self.slots();
1431            // An empty keyspace has nothing to move and opening a log for one
1432            // would cost a resident page window to find that out.
1433            let used = !self.slot(i).is_empty();
1434            let gave = if used && self.migrates(i) {
1435                self.attach_store(i);
1436                // Whether it made room and not whether it moved a key. A round
1437                // that demoted nothing and handed back a segment is a round
1438                // that made room, and reading only the count refuses the write
1439                // that provoked it.
1440                self.slot(i)
1441                    .relieve(over)
1442                    .is_ok_and(yo_kv::tier::Relief::made_room)
1443            } else {
1444                self.slot(i).evict_one()
1445            };
1446            if gave {
1447                self.evict_db.store((i + 1) % self.slots(), Relaxed);
1448                self.mine().mark(1u64 << self.slot_db(i));
1449                return true;
1450            }
1451        }
1452        false
1453    }
1454
1455    /// The sweep the shard loop calls, at most once a millisecond.
1456    ///
1457    /// The gate is the whole difference between this and [`Server::expire_step`].
1458    /// A maintenance slice runs on every turn of the loop and a turn is a
1459    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
1460    /// thousand times per millisecond and spend a real share of the shard on
1461    /// looking for keys that cannot have died since the last look. Nothing in a
1462    /// database changes fast enough to be worth asking about more often than the
1463    /// clock can tell the difference, and the clock here is milliseconds.
1464    ///
1465    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
1466    /// hertz, so this is not the thing that decides how promptly memory comes
1467    /// back. What it decides is that an idle server sweeps a thousand times a
1468    /// second rather than a million.
1469    pub fn expire_slice(&self, budget: usize) -> usize {
1470        let now = self.clock.now_ms();
1471        if now == self.expire_ms.load(Relaxed) {
1472            return 0;
1473        }
1474        self.expire_ms.store(now, Relaxed);
1475        self.expire_step(budget)
1476    }
1477
1478    /// Sweep dead keys out of the databases, spending at most `budget` looks.
1479    ///
1480    /// Answers what it spent, so the caller can charge its maintenance slice for
1481    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
1482    ///
1483    /// Round robin from its own cursor, and every database gets offered whatever
1484    /// is left of the budget rather than a sixteenth of it each, so a server on
1485    /// database zero only, which is nearly every server, spends the whole slice
1486    /// where the keys are. The fifteen empty ones cost a comparison apiece
1487    /// because a database with no key carrying a deadline says so without
1488    /// drawing anything.
1489    ///
1490    /// The cursor moves to the database after whichever one did the work, so two
1491    /// busy databases take turns instead of the lower numbered one starving the
1492    /// other.
1493    pub fn expire_step(&self, budget: usize) -> usize {
1494        let mut spent = 0;
1495        let from = self.expire_db.load(Relaxed);
1496        for turn in 0..self.slots() {
1497            if spent >= budget {
1498                break;
1499            }
1500            let i = (from + turn) % self.slots();
1501            let c = self.slot(i).expire_cycle(budget - spent);
1502            spent += c.examined;
1503            if c.expired > 0 {
1504                self.expire_db.store((i + 1) % self.slots(), Relaxed);
1505                self.mine().note(1u64 << self.slot_db(i));
1506            }
1507        }
1508        spent
1509    }
1510
1511    /// One slice of compaction for a server that is over its limit.
1512    ///
1513    /// Takes the databases in the same order [`Server::compact_step`] does and
1514    /// stops at the first one that had something to move, and it asks with the
1515    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
1516    fn compact_hard_step(&self) -> Option<usize> {
1517        let from = self.next_db.load(Relaxed);
1518        for turn in 0..self.slots() {
1519            let i = (from + turn) % self.slots();
1520            if let Some(moved) = self.slot(i).compact_hard() {
1521                self.next_db.store((i + 1) % self.slots(), Relaxed);
1522                return Some(moved);
1523            }
1524        }
1525        None
1526    }
1527
1528    /// Take what every thread has marked and add it to the turn's own mask.
1529    ///
1530    /// The mask the turn works from is its own and not a shared one, because a
1531    /// mask it read in place and then cleared a bit of would be a mask that lost
1532    /// whatever another thread marked in between. A swap cannot lose a mark: a
1533    /// thread that ors while the swap happens either gets its bit in before the
1534    /// swap or leaves it there afterwards, and the second one costs one look at
1535    /// a database the turn has already been through.
1536    fn collect_marks(&self) {
1537        let mut marked = 0;
1538        for thread in &self.locals {
1539            marked |= thread.dirty.swap(0, Relaxed);
1540        }
1541        self.mine().note(marked);
1542    }
1543
1544    /// Give one database's dead space back, if any database has enough of it to
1545    /// be worth the move. `None` when no database had a candidate.
1546    ///
1547    /// Once per batch, next to the clock. Overwriting a key writes a new record
1548    /// and counts the old one dead, so without this a server holds everything
1549    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
1550    /// a key against Redis at 144 for the same load, and the whole difference
1551    /// was dead records nothing ever came back for.
1552    ///
1553    /// At most one segment moves per call and the search starts one database
1554    /// further along each time, so the cost of asking is a comparison per
1555    /// database and the cost of acting is bounded by a segment.
1556    pub fn compact_step(&self) -> Option<usize> {
1557        self.collect_marks();
1558        let mine = self.mine();
1559        let from = self.next_db.load(Relaxed);
1560        for turn in 0..self.slots() {
1561            let i = (from + turn) % self.slots();
1562            // Nothing has run against this database since it last said it had
1563            // nothing to collect, so it still has nothing to collect and the
1564            // line it lives on stays where it is.
1565            let at = self.slot_db(i);
1566            if !mine.wanted(at) {
1567                continue;
1568            }
1569            if let Some(moved) = self.slot(i).compact_step() {
1570                self.next_db.store((i + 1) % self.slots(), Relaxed);
1571                return Some(moved);
1572            }
1573            // Only once every stripe of the database has said it has nothing,
1574            // since the bit is per database and one stripe answering for all of
1575            // them would stop the others being asked at all.
1576            if i % self.width == self.width - 1 {
1577                mine.done(at);
1578            }
1579        }
1580        None
1581    }
1582}
1583
1584impl Default for Server {
1585    fn default() -> Server {
1586        Server::new()
1587    }
1588}
1589
1590/// What one connection has chosen.
1591pub struct Session {
1592    db: usize,
1593    id: u64,
1594    name: Vec<u8>,
1595    /// The `HIMPORT` fieldsets this connection has prepared.
1596    ///
1597    /// Connection state and not keyspace state, which is the reference's design
1598    /// and not a shortcut: a fieldset is invisible to every other connection and
1599    /// the keys built from one outlive it.
1600    sets: himport::Fieldsets,
1601}
1602
1603impl Session {
1604    /// A new connection, on database zero with no name.
1605    #[must_use]
1606    pub fn new(id: u64) -> Session {
1607        Session {
1608            db: 0,
1609            id,
1610            name: Vec::new(),
1611            sets: himport::Fieldsets::default(),
1612        }
1613    }
1614
1615    /// The connection id, which `HELLO` reports and `CLIENT` will.
1616    #[must_use]
1617    pub const fn id(&self) -> u64 {
1618        self.id
1619    }
1620
1621    /// Which database this connection is working in.
1622    #[must_use]
1623    pub const fn db(&self) -> usize {
1624        self.db
1625    }
1626
1627    /// The name the client gave itself, empty if it gave none.
1628    #[must_use]
1629    pub fn name(&self) -> &[u8] {
1630        &self.name
1631    }
1632
1633    /// Put everything back the way it was when the connection was opened.
1634    ///
1635    /// The protocol is not here because it is not here: it lives in the reply
1636    /// buffer, and `RESET` sets it back there.
1637    pub fn reset(&mut self) {
1638        self.db = 0;
1639        self.name.clear();
1640        // `SELECT` leaves these alone and `RESET` does not, both checked
1641        // against 8.10.1, which is the one pair of answers you could not guess
1642        // from what the command is for.
1643        self.sets.clear();
1644    }
1645
1646    /// Record the name from `HELLO ... SETNAME`.
1647    fn set_name(&mut self, name: &[u8]) {
1648        yo_alloc::allow(|| {
1649            self.name.clear();
1650            self.name.extend_from_slice(name);
1651        });
1652    }
1653}
1654
1655/// Run one command and write its reply.
1656///
1657/// The name is looked up and the arity is checked here, once, so that no body
1658/// has to. Everything after that is the command's own.
1659pub fn execute(server: &Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1660    // The decoder never produces a command with no name. If one ever arrives,
1661    // it is not something to answer.
1662    if args.is_empty() {
1663        return Flow::Continue;
1664    }
1665    resolved(server, session, lookup(args.name()), args, out)
1666}
1667
1668/// The same, for a caller that has already found the command.
1669///
1670/// The engine frames a command before it runs it, and between those two it also
1671/// asks which key the command touches so the record can be prefetched. That is
1672/// two more chances to look the name up, and looking it up three times to run it
1673/// once is three times the cost of the cheapest thing in the path. So the engine
1674/// resolves the name where it frames the command, carries the answer on the
1675/// framed command, and both the other two take it from there.
1676///
1677/// `spec` is `None` for a name that is not a command, which is the same thing
1678/// [`lookup`] says and lands in the same reply.
1679pub fn resolved(
1680    server: &Server,
1681    session: &mut Session,
1682    spec: Option<&'static Spec>,
1683    args: Args<'_>,
1684    out: &mut Out,
1685) -> Flow {
1686    if args.is_empty() {
1687        return Flow::Continue;
1688    }
1689    server.mine().stats.commands.bump();
1690
1691    let Some(spec) = spec else {
1692        write_error(out, &args::unknown_command(args));
1693        return Flow::Continue;
1694    };
1695    if !arity_ok(spec, args.len()) {
1696        server.mine().cmdstats.at(spec).rejected.bump();
1697        write_error(out, &args::wrong_arity(spec.name));
1698        return Flow::Continue;
1699    }
1700
1701    // The limit first, so a server with no `maxmemory`, which is the default and
1702    // is nearly all of them, pays one comparison against a field that is already
1703    // warm. Every command and not only the writes, because that is where Redis
1704    // puts it: making room is the server's job whatever the client asked for,
1705    // and the flag only decides who gets told no when there is no room to make.
1706    //
1707    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1708    // Redis's list, so a command that only frees is let through with nothing
1709    // left, which is what lets a client dig itself out with `DEL`.
1710    if server.maxmemory() != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1711        server.mine().cmdstats.at(spec).rejected.bump();
1712        out.error_line(b"OOM ", OOM);
1713        return Flow::Continue;
1714    }
1715
1716    // Which databases the maintenance turn after this batch has to ask. Marked
1717    // for every command and not only for the writes, because a read can make
1718    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1719    // record it dropped is exactly the kind of thing the collector is for.
1720    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1721    // two groups that hold them mark all of them rather than the session's.
1722    server.mine().mark(match spec.group {
1723        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1724        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1725            1u64 << session.db
1726        }
1727        _ => ALL_DATABASES,
1728    });
1729
1730    let mark = out.len();
1731    // Before the group, because the five that block are list commands and would
1732    // otherwise land in `lists`, which is handed one database and nothing that
1733    // could park a client. The flag is the right thing to branch on rather than
1734    // a list of names: it is what `COMMAND INFO` reports about exactly these
1735    // commands, and the sorted set and stream ones that arrive later carry it
1736    // too.
1737    let done = if spec.flags.contains(&"blocking") {
1738        blocking::execute(server, session, spec, args, out)
1739    } else {
1740        match spec.group {
1741            "string" => {
1742                let db = session.db;
1743                strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1744            }
1745            // Its own group and its own file, and the same values underneath:
1746            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1747            // something a `SET` left behind works.
1748            "bitmap" => {
1749                let db = session.db;
1750                bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1751            }
1752            // The same again: a sketch is a string with a documented layout, so
1753            // `GET` hands one to a client and `SET` takes it back.
1754            "hyperloglog" => {
1755                let db = session.db;
1756                hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1757            }
1758            "set" => {
1759                let db = session.db;
1760                sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1761            }
1762            // The one hash command whose state is not in the keyspace. A
1763            // fieldset belongs to the connection, so this is handed the session
1764            // as well as the database, the same exception `MIGRATE` gets in the
1765            // keyspace group for the socket it keeps.
1766            "hash" if spec.name == "himport" => {
1767                let db = session.db;
1768                himport::execute(&server.dbs[db], &mut session.sets, args, out)
1769                    .map(|()| Flow::Continue)
1770            }
1771            // The one group that reaches back into the server after it has
1772            // written its reply, because a hash is what a search index is
1773            // made of. What comes back is what the indexes have to be told,
1774            // which is not the same as whether the command was a write.
1775            "hash" => {
1776                let db = session.db;
1777                let changed = hashes::execute(&server.dbs[db], spec, args, out);
1778                changed.map(|changed| {
1779                    indexing::changed(server, db, args.get(1), changed);
1780                    Flow::Continue
1781                })
1782            }
1783            "list" => {
1784                let db = session.db;
1785                lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1786            }
1787            "zset" => {
1788                let db = session.db;
1789                zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1790            }
1791            // A geo key is a sorted set and these are sorted set commands with
1792            // arithmetic on the way in and on the way out, so a client can ZREM
1793            // a place out of one and ZCARD it to count them.
1794            "geo" => {
1795                let db = session.db;
1796                geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1797            }
1798            "array" => {
1799                let db = session.db;
1800                arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1801            }
1802            "graph" => {
1803                let db = session.db;
1804                graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1805            }
1806            // A document under a key, reached by a path. The group is Redis's
1807            // module surface and the storage is ours, the same trade the vector
1808            // set group makes.
1809            "json" => {
1810                let db = session.db;
1811                json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1812            }
1813            "vector" => {
1814                let db = session.db;
1815                vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1816            }
1817            "bloom" => {
1818                let db = session.db;
1819                bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1820            }
1821            "cuckoo" => {
1822                let db = session.db;
1823                cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1824            }
1825            "cms" => {
1826                let db = session.db;
1827                cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1828            }
1829            "topk" => {
1830                let db = session.db;
1831                topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1832            }
1833            "tdigest" => {
1834                let db = session.db;
1835                tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1836            }
1837            "ts" => {
1838                let db = session.db;
1839                ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1840            }
1841            // The clock is read before the database is borrowed, because every
1842            // stream command needs the time and it lives on the server. An
1843            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1844            // `XINFO` reporting it all have to agree about what moment this is.
1845            "stream" => {
1846                let db = session.db;
1847                let now = server.now_ms();
1848                streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1849            }
1850            // The one keyspace command that needs more than the databases,
1851            // because the socket it talks down is held on the server between
1852            // commands and not opened again for each one.
1853            "keyspace" if spec.name == "migrate" => {
1854                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1855            }
1856            // Every database and not the one the session is on, because `COPY` takes
1857            // a `DB n` and writes into a database nobody selected. The other group
1858            // that reaches back into the server afterwards, and it hands back a list
1859            // rather than one answer, because `DEL a b c` is three keys and a rename
1860            // is two.
1861            "keyspace" => {
1862                let mut touched = indexing::Touched::new(server);
1863                let done =
1864                    keyspace::execute(&server.dbs, session.db, spec, args, out, &mut touched);
1865                done.map(|()| {
1866                    indexing::touched(server, &touched);
1867                    Flow::Continue
1868                })
1869            }
1870            // No database at all, because an index is not a key. The registry
1871            // is the whole of what these sixteen commands touch, and then
1872            // `FT.CREATE` hands back the name it made so the keys that
1873            // already match its prefix can be read into it. The lock goes
1874            // before the scan runs, since the scan takes it again for every
1875            // key it reads.
1876            "search" if spec.name == "FT.SEARCH" => {
1877                // The two search commands that read documents, and so the two
1878                // that need the keyspace as well as the registry. They take and
1879                // let go of the registry themselves, because they cannot hold
1880                // that and a stripe at the same time.
1881                search::find(server, session.db, args, out).map(|()| Flow::Continue)
1882            }
1883            "search" if spec.name == "FT.AGGREGATE" => {
1884                search::roll(server, session.db, args, out).map(|()| Flow::Continue)
1885            }
1886            "search" if spec.name == "FT.CURSOR" => {
1887                // Its own arm because the cursors are not in the registry, and
1888                // it takes and lets go of the registry itself to look up the
1889                // index name it is given.
1890                search::cursor::execute(server, args, out).map(|()| Flow::Continue)
1891            }
1892            "search" => {
1893                let db = session.db;
1894                let made = search::execute(server, &mut server.search.lock(), db, spec, args, out);
1895                made.map(|made| {
1896                    if let Some(name) = made {
1897                        indexing::scan(server, db, name);
1898                    }
1899                    Flow::Continue
1900                })
1901            }
1902            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1903            _ => server::execute(server, session, spec, args, out),
1904        }
1905    };
1906    let flow = match done {
1907        Ok(flow) => flow,
1908        Err(e) => {
1909            out.truncate(mark);
1910            write_error(out, &e);
1911            Flow::Continue
1912        }
1913    };
1914
1915    // Counted here and not before the call, which is where Redis counts it, so
1916    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1917    // same way theirs does.
1918    //
1919    // Failure is read off the reply rather than off the `Result`, because the
1920    // two are not the same set. A command that ran out of arguments comes back
1921    // as an `Err` and a command that was sent the wrong password writes its own
1922    // error line and comes back `Ok`, and both of those are a call that failed.
1923    // The first byte at the mark is what a client would branch on, and it is `-`
1924    // for an error on either protocol and `!` for RESP3's long form.
1925    let row = server.mine().cmdstats.at(spec);
1926    row.calls.bump();
1927    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1928        row.failed.bump();
1929    }
1930    flow
1931}
1932
1933/// The error line for an error value.
1934///
1935/// The prefix is what a client branches on, and there are three of them:
1936/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1937/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1938/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1939/// than routed through here. `OOM` is not a [`Code`] of its own because
1940/// [`Code::Full`] already covers the string that is too long for
1941/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1942fn write_error(out: &mut Out, e: &Error) {
1943    let prefix: &[u8] = match e.code() {
1944        Code::WrongType => b"WRONGTYPE ",
1945        // Only the HyperLogLog commands answer this one, and the prefix is the
1946        // sentence a client branches on to tell a sketch it cannot read from a
1947        // sketch it sent wrong.
1948        Code::Corrupt => b"INVALIDOBJ ",
1949        _ => b"ERR ",
1950    };
1951    out.error_line(prefix, e.message().as_bytes());
1952}
1953
1954#[cfg(test)]
1955mod tests {
1956    use super::*;
1957    use crate::proto::{Limits, Proto};
1958    use crate::request::Argv;
1959
1960    /// Build the wire bytes for a command.
1961    ///
1962    /// Tests go through the codec rather than around it, so an argument in a
1963    /// test is the same borrowed slice a connection produces.
1964    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1965        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1966        for p in parts {
1967            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1968            wire.extend_from_slice(p);
1969            wire.extend_from_slice(b"\r\n");
1970        }
1971        wire
1972    }
1973
1974    /// A server, a connection and a buffer, driven the way the reactor will.
1975    struct Fixture {
1976        server: Server,
1977        session: Session,
1978        argv: Argv,
1979        out: Out,
1980    }
1981
1982    impl Fixture {
1983        fn new() -> Fixture {
1984            Fixture::on(Server::new())
1985        }
1986
1987        /// The same, on a server whose databases are cut into `width` stripes.
1988        fn striped(width: usize) -> Fixture {
1989            Fixture::on(Server::with_width(width))
1990        }
1991
1992        fn on(server: Server) -> Fixture {
1993            Fixture {
1994                server,
1995                session: Session::new(7),
1996                argv: Argv::new(),
1997                out: Out::new(Proto::Resp2),
1998            }
1999        }
2000
2001        /// Run one command and answer with the bytes it wrote.
2002        fn run(&mut self, parts: &[&[u8]]) -> String {
2003            self.flow(parts).1
2004        }
2005
2006        /// Run one command and answer with the bytes exactly as written.
2007        ///
2008        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
2009        /// every reply that is text and destroys a `DUMP` payload, since a
2010        /// payload is arbitrary bytes and a checksum on the end of them.
2011        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
2012            let wire = encode(parts);
2013            self.argv.decode(&wire, &Limits::default()).unwrap();
2014            self.out.clear();
2015            execute(
2016                &self.server,
2017                &mut self.session,
2018                Args::new(&self.argv, &wire),
2019                &mut self.out,
2020            );
2021            self.out.as_slice().to_vec()
2022        }
2023
2024        /// Move every clock in the server on by `ms`.
2025        fn advance(&mut self, ms: u64) {
2026            self.server.advance_clock_ms(ms);
2027        }
2028
2029        /// The same, with what the connection should do next.
2030        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
2031            let wire = encode(parts);
2032            self.argv.decode(&wire, &Limits::default()).unwrap();
2033            self.out.clear();
2034            let flow = execute(
2035                &self.server,
2036                &mut self.session,
2037                Args::new(&self.argv, &wire),
2038                &mut self.out,
2039            );
2040            (
2041                flow,
2042                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
2043            )
2044        }
2045    }
2046
2047    /// What a client does all day: write the same keys again and again. Every
2048    /// one of those writes leaves the previous record behind, so a server that
2049    /// never compacts holds every version of every key it has ever been sent.
2050    #[test]
2051    fn rewriting_the_same_keys_does_not_grow_the_server() {
2052        let mut f = Fixture::new();
2053        let val = vec![b'v'; 1024];
2054        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
2055
2056        for k in &keys {
2057            f.run(&[b"SET", k, &val]);
2058        }
2059        f.server.compact_step();
2060        let after_first = f.server.memory_bytes();
2061
2062        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
2063        // of it. Thirty two megabytes written to hold sixty four kilobytes,
2064        // which is the shape of a real workload and is enough churn to fill
2065        // sixteen segments if nothing ever comes back.
2066        for _ in 0..500 {
2067            for k in &keys {
2068                f.run(&[b"SET", k, &val]);
2069            }
2070            f.server.compact_step();
2071        }
2072
2073        assert!(
2074            f.server.memory_bytes() <= after_first * 2,
2075            "held {} after five hundred passes against {after_first} after one",
2076            f.server.memory_bytes()
2077        );
2078        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
2079        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
2080    }
2081
2082    /// The same churn on a database nobody starts on, either side of a quiet
2083    /// spell long enough for the maintenance turn to stop asking about it.
2084    ///
2085    /// The turn after each batch skips a database that has already said it has
2086    /// nothing to collect and has not been touched since, which is what keeps a
2087    /// server whose clients are all on database zero from loading and storing
2088    /// in the other fifteen every batch to be told no. Two things could go
2089    /// wrong with that. A database might never be marked at all, so this uses
2090    /// database nine, which nothing marks by accident. And a database whose
2091    /// mark was cleared might never get it back, so this drains the collector
2092    /// until it says there is nothing left, checks the mark really is gone, and
2093    /// then writes another thirty two megabytes through the same sixty four
2094    /// keys. If either went wrong the server would hold all of it.
2095    #[test]
2096    fn a_database_nobody_started_on_is_still_collected() {
2097        let mut f = Fixture::new();
2098        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
2099        let val = vec![b'v'; 1024];
2100        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
2101
2102        for k in &keys {
2103            f.run(&[b"SET", k, &val]);
2104        }
2105        while f.server.compact_step().is_some() {}
2106        assert!(
2107            !f.server.mine().wanted(9),
2108            "database nine was drained and should not be asked again until it is written to"
2109        );
2110        let after_first = f.server.memory_bytes();
2111
2112        for _ in 0..500 {
2113            for k in &keys {
2114                f.run(&[b"SET", k, &val]);
2115            }
2116            f.server.compact_step();
2117        }
2118
2119        assert!(
2120            f.server.memory_bytes() <= after_first * 2,
2121            "held {} after five hundred passes against {after_first} after one",
2122            f.server.memory_bytes()
2123        );
2124        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
2125        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
2126        // And nothing landed anywhere else on the way.
2127        f.run(&[b"SELECT", b"0"]);
2128        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2129    }
2130
2131    #[test]
2132    fn a_command_goes_from_bytes_to_bytes() {
2133        let mut f = Fixture::new();
2134        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2135        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
2136        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2137        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
2138        // The name is matched whatever case it came in, and so are the options.
2139        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
2140        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
2141    }
2142
2143    #[test]
2144    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
2145        let mut f = Fixture::new();
2146        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
2147        // A key named twice exists twice and can only be deleted once, and both
2148        // of those are Redis's answers rather than tidier ones.
2149        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
2150        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
2151        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2152        // UNLINK is the same body and reports the same way.
2153        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
2154        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2155    }
2156
2157    #[test]
2158    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
2159        let mut f = Fixture::new();
2160        f.run(&[b"SET", b"k", b"v"]);
2161        // A simple string on both protocols, which is unusual: most replies
2162        // that carry a word are bulk strings.
2163        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
2164        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
2165    }
2166
2167    #[test]
2168    fn touch_counts_the_way_exists_counts() {
2169        let mut f = Fixture::new();
2170        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2171        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
2172        assert_eq!(
2173            f.run(&[b"TOUCH", b"a", b"a"]),
2174            ":2\r\n",
2175            "twice counts twice"
2176        );
2177        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
2178        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
2179    }
2180
2181    #[test]
2182    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
2183        let mut f = Fixture::new();
2184        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2185        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
2186
2187        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
2188        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2189        assert_eq!(
2190            f.run(&[b"TTL", b"b"]),
2191            ":100\r\n",
2192            "the source's and not b's"
2193        );
2194        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
2195    }
2196
2197    #[test]
2198    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
2199        let mut f = Fixture::new();
2200        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
2201        // The source is checked before the destination, so this is the error
2202        // and not the zero RENAMENX would otherwise answer for a taken name.
2203        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
2204    }
2205
2206    #[test]
2207    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
2208        let mut f = Fixture::new();
2209        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
2210
2211        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
2212        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2213        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
2214        // one call the two disagree about and neither does any work for.
2215        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
2216        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
2217        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
2218        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
2219    }
2220
2221    #[test]
2222    fn renaming_a_set_does_not_touch_a_member() {
2223        let mut f = Fixture::new();
2224        for i in 0..300 {
2225            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
2226        }
2227        let before = f.server.memory_bytes();
2228
2229        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
2230        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
2231        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
2232        assert!(
2233            f.server.memory_bytes().abs_diff(before) < 256,
2234            "the members were copied: {} against {before}",
2235            f.server.memory_bytes()
2236        );
2237    }
2238
2239    #[test]
2240    fn a_copy_is_a_second_value_and_not_a_second_name() {
2241        let mut f = Fixture::new();
2242        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
2243
2244        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
2245        f.run(&[b"SADD", b"t", b"m3"]);
2246        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
2247        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
2248    }
2249
2250    /// Every type a key can hold, copied, because two of them used to panic.
2251    ///
2252    /// `COPY` reads the value out of the source through one match on the type
2253    /// tag, and that match had a catch all at the bottom from back when a set
2254    /// and a hash were the only bodies. The list and the sorted set landed after
2255    /// it and nobody came back, so `COPY mylist other` took the shard down. It
2256    /// is an ordinary command against a type the server supports everywhere
2257    /// else, so this walks all five rather than the two that were broken: the
2258    /// point is that the next type cannot land the same way.
2259    #[test]
2260    fn every_type_can_be_copied() {
2261        let mut f = Fixture::new();
2262        f.run(&[b"SET", b"str", b"v1"]);
2263        f.run(&[b"SADD", b"set", b"m1"]);
2264        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2265        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
2266        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
2267
2268        for name in [
2269            &b"str"[..],
2270            &b"set"[..],
2271            &b"hash"[..],
2272            &b"list"[..],
2273            &b"zset"[..],
2274        ] {
2275            let dst = [name, b":copy"].concat();
2276            assert_eq!(
2277                f.run(&[b"COPY", name, &dst]),
2278                ":1\r\n",
2279                "copying {}",
2280                String::from_utf8_lossy(name)
2281            );
2282            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
2283        }
2284
2285        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
2286            let mut want = String::from("*2\r\n");
2287            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
2288            want
2289        });
2290        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
2291
2292        // And the copy is its own value, not a second name for the source.
2293        f.run(&[b"RPUSH", b"list:copy", b"c"]);
2294        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
2295        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
2296    }
2297
2298    #[test]
2299    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
2300        let mut f = Fixture::new();
2301        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
2302        f.run(&[b"SET", b"b", b"v2"]);
2303
2304        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
2305        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
2306        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
2307        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
2308        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
2309        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
2310    }
2311
2312    #[test]
2313    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
2314        let mut f = Fixture::new();
2315        f.run(&[b"SET", b"a", b"v1"]);
2316
2317        // Same key, different database, so this is not the same object and is
2318        // an ordinary copy. Same key in the same database is the error below.
2319        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
2320        f.run(&[b"SELECT", b"1"]);
2321        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
2322        assert_eq!(
2323            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
2324            ":0\r\n",
2325            "taken"
2326        );
2327        assert_eq!(
2328            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
2329            ":1\r\n"
2330        );
2331    }
2332
2333    #[test]
2334    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
2335        let mut f = Fixture::new();
2336        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2337        assert_eq!(
2338            f.run(&[b"SORT", b"l"]),
2339            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2340        );
2341        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
2342        assert_eq!(
2343            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
2344            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2345        );
2346        assert_eq!(
2347            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
2348            "*1\r\n$1\r\n2\r\n"
2349        );
2350    }
2351
2352    #[test]
2353    fn sort_reads_a_key_per_element_for_by_and_for_get() {
2354        let mut f = Fixture::new();
2355        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
2356        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
2357        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
2358        // misses, which is a nil in the middle of the array and not a short one.
2359        assert_eq!(
2360            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
2361            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
2362        );
2363    }
2364
2365    #[test]
2366    fn sort_store_writes_a_list_and_answers_its_length() {
2367        let mut f = Fixture::new();
2368        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
2369        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
2370        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
2371        assert_eq!(
2372            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
2373            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
2374        );
2375        // An empty result takes the destination with it rather than leaving a
2376        // list that holds nothing.
2377        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
2378        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
2379    }
2380
2381    #[test]
2382    fn sort_ro_does_not_know_the_word_store() {
2383        let mut f = Fixture::new();
2384        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
2385        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
2386        assert_eq!(
2387            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
2388            "-ERR syntax error\r\n"
2389        );
2390        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2391    }
2392
2393    #[test]
2394    fn sort_refuses_what_it_cannot_sort() {
2395        let mut f = Fixture::new();
2396        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
2397        f.run(&[b"SET", b"s", b"x"]);
2398        assert_eq!(
2399            f.run(&[b"SORT", b"s"]),
2400            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
2401        );
2402        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
2403        assert_eq!(
2404            f.run(&[b"SORT", b"words"]),
2405            "-ERR One or more scores can't be converted into double\r\n"
2406        );
2407        assert_eq!(
2408            f.run(&[b"SORT", b"words", b"ALPHA"]),
2409            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
2410        );
2411        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
2412    }
2413
2414    #[test]
2415    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
2416        let mut f = Fixture::new();
2417        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
2418        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
2419        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2420        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2421        assert_eq!(
2422            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
2423            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2424        );
2425        // And back, which proves the body survived the trip rather than being
2426        // rebuilt from a copy that happened to look the same.
2427        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
2428        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
2429    }
2430
2431    #[test]
2432    fn move_answers_zero_when_either_end_says_no() {
2433        let mut f = Fixture::new();
2434        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
2435        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
2436        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2437        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
2438        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2439        // The destination is taken, so nothing moves and the source is still
2440        // there with what it had.
2441        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
2442        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
2443        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2444        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
2445    }
2446
2447    #[test]
2448    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
2449        let mut f = Fixture::new();
2450        assert_eq!(
2451            f.run(&[b"MOVE", b"a", b"0"]),
2452            "-ERR source and destination objects are the same\r\n"
2453        );
2454        assert_eq!(
2455            f.run(&[b"MOVE", b"a", b"99"]),
2456            "-ERR DB index is out of range\r\n"
2457        );
2458        assert_eq!(
2459            f.run(&[b"MOVE", b"a", b"-1"]),
2460            "-ERR DB index is out of range\r\n"
2461        );
2462        assert_eq!(
2463            f.run(&[b"MOVE", b"a", b"x"]),
2464            "-ERR value is not an integer or out of range\r\n"
2465        );
2466    }
2467
2468    #[test]
2469    fn swapdb_swaps_what_two_connections_would_see() {
2470        let mut f = Fixture::new();
2471        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
2472        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2473        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
2474        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2475
2476        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2477        // Still on database zero, and database zero is a different database.
2478        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
2479        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2480        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2481        // A database swapped with itself is fine and changes nothing.
2482        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
2483        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2484    }
2485
2486    /// Every database on a server reads the server's clock and not one of its
2487    /// own. They used to be told the time one at a time and now they share the
2488    /// reading, so a server that built its databases from a second clock would
2489    /// answer a deadline worked out against a time nobody had set.
2490    #[test]
2491    fn a_wide_server_puts_its_databases_on_its_own_clock() {
2492        let mut f = Fixture::striped(8);
2493        f.server.set_clock_ms(1_700_000_000_000);
2494        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"100"]), "+OK\r\n");
2495        assert_eq!(f.run(&[b"EXPIRETIME", b"k"]), ":1700000100\r\n");
2496        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2497        f.server.set_clock_ms(1_700_000_050_000);
2498        assert_eq!(f.run(&[b"TTL", b"k"]), ":50\r\n");
2499    }
2500
2501    /// The swap is stripe by stripe, so a database cut into more than one
2502    /// stripe is the case that would catch it exchanging some of the keys and
2503    /// leaving the rest. Sixteen keys over four stripes is enough that every
2504    /// stripe has something in it whatever the hashes come out as.
2505    #[test]
2506    fn swapdb_swaps_every_stripe_of_a_wide_database() {
2507        let mut f = Fixture::striped(4);
2508        for i in 0..16u32 {
2509            let key = format!("k{i}");
2510            assert_eq!(f.run(&[b"SET", key.as_bytes(), b"zero"]), "+OK\r\n");
2511        }
2512        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2513        assert_eq!(f.run(&[b"SET", b"only", b"one"]), "+OK\r\n");
2514        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2515
2516        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
2517        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2518        assert_eq!(f.run(&[b"GET", b"only"]), "$3\r\none\r\n");
2519        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
2520        assert_eq!(f.run(&[b"DBSIZE"]), ":16\r\n");
2521        for i in 0..16u32 {
2522            let key = format!("k{i}");
2523            assert_eq!(f.run(&[b"GET", key.as_bytes()]), "$4\r\nzero\r\n");
2524        }
2525    }
2526
2527    #[test]
2528    fn swapdb_says_which_index_it_could_not_read() {
2529        let mut f = Fixture::new();
2530        assert_eq!(
2531            f.run(&[b"SWAPDB", b"x", b"1"]),
2532            "-ERR invalid first DB index\r\n"
2533        );
2534        assert_eq!(
2535            f.run(&[b"SWAPDB", b"0", b"y"]),
2536            "-ERR invalid second DB index\r\n"
2537        );
2538        // A number too big to be an index on a server that keeps one in an int
2539        // is the same complaint, and a plausible one that is not ours is the
2540        // range complaint instead. The split is Redis's.
2541        assert_eq!(
2542            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2543            "-ERR invalid first DB index\r\n"
2544        );
2545        assert_eq!(
2546            f.run(&[b"SWAPDB", b"0", b"99"]),
2547            "-ERR DB index is out of range\r\n"
2548        );
2549        assert_eq!(
2550            f.run(&[b"SWAPDB", b"-1", b"0"]),
2551            "-ERR DB index is out of range\r\n"
2552        );
2553    }
2554
2555    #[test]
2556    fn wait_answers_zero_replicas_without_waiting() {
2557        let mut f = Fixture::new();
2558        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2559        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2560        // A replica that is never going to arrive, and a timeout that would be
2561        // a real wait on a server that had one.
2562        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2563        // Negative replicas is not an error, because zero is already more than
2564        // it asked for.
2565        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2566        assert_eq!(
2567            f.run(&[b"WAIT", b"x", b"0"]),
2568            "-ERR value is not an integer or out of range\r\n"
2569        );
2570        assert_eq!(
2571            f.run(&[b"WAIT", b"0", b"-1"]),
2572            "-ERR timeout is negative\r\n"
2573        );
2574        assert_eq!(
2575            f.run(&[b"WAIT", b"0", b"1.5"]),
2576            "-ERR timeout is not an integer or out of range\r\n"
2577        );
2578    }
2579
2580    #[test]
2581    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2582        let mut f = Fixture::new();
2583        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2584        assert_eq!(
2585            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2586            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2587        );
2588        assert_eq!(
2589            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2590            "-ERR value is out of range, value must between 0 and 1\r\n"
2591        );
2592        assert_eq!(
2593            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2594            "-ERR value is out of range, must be positive\r\n"
2595        );
2596        // The arguments are all read before the server looks at itself, so a
2597        // bad timeout beats the append only complaint even with numlocal set.
2598        assert_eq!(
2599            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2600            "-ERR timeout is negative\r\n"
2601        );
2602    }
2603
2604    /// The bytes inside a bulk reply, with the header and the trailing break
2605    /// taken off. Every `DUMP` test needs this and none of them care how the
2606    /// length was written.
2607    fn payload(reply: &[u8]) -> Vec<u8> {
2608        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2609        reply[head + 2..reply.len() - 2].to_vec()
2610    }
2611
2612    #[test]
2613    fn a_value_survives_a_dump_and_a_restore() {
2614        let mut f = Fixture::new();
2615        f.run(&[b"SET", b"s", b"hello"]);
2616        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2617        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2618        f.run(&[b"SADD", b"u", b"x", b"y"]);
2619        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2620        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2621
2622        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2623            let mut copy = key.to_vec();
2624            copy.push(b'2');
2625            let bytes = payload(&f.raw(&[b"DUMP", key]));
2626            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
2627            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
2628        }
2629
2630        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2631        assert_eq!(
2632            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2633            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2634        );
2635        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2636        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2637        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2638        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2639        // The encoding survives too, since the payload names the plainest legal
2640        // type and the loader puts the value back on the rung it belongs on.
2641        assert_eq!(
2642            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2643            f.run(&[b"OBJECT", b"ENCODING", b"t"])
2644        );
2645    }
2646
2647    #[test]
2648    fn a_dumped_hash_keeps_its_field_deadlines() {
2649        let mut f = Fixture::new();
2650        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2651        assert_eq!(
2652            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2653            "*1\r\n:1\r\n"
2654        );
2655        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2656        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2657        assert_eq!(
2658            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2659            "*2\r\n:-1\r\n:100\r\n"
2660        );
2661    }
2662
2663    #[test]
2664    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2665        let mut f = Fixture::new();
2666        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2667        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2668        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2669        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2670        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2671        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2672        // An absolute deadline that has already gone is not an error. The key is
2673        // not created and the reply is the same OK a live one gets.
2674        assert_eq!(
2675            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2676            "+OK\r\n"
2677        );
2678        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2679    }
2680
2681    #[test]
2682    fn dump_answers_nothing_for_a_key_that_is_not_there() {
2683        let mut f = Fixture::new();
2684        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2685        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2686        f.advance(50);
2687        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2688    }
2689
2690    #[test]
2691    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2692        let mut f = Fixture::new();
2693        f.run(&[b"SET", b"a", b"first"]);
2694        f.run(&[b"SET", b"b", b"second"]);
2695        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2696        assert_eq!(
2697            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2698            "-BUSYKEY Target key name already exists.\r\n"
2699        );
2700        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2701        assert_eq!(
2702            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2703            "+OK\r\n"
2704        );
2705        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2706    }
2707
2708    /// The busy key comes before the payload, which is not the order the
2709    /// arguments read in. Whether a key is taken should not depend on whether
2710    /// the bytes behind it happened to be good.
2711    #[test]
2712    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2713        let mut f = Fixture::new();
2714        f.run(&[b"SET", b"a", b"v"]);
2715        assert_eq!(
2716            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2717            "-BUSYKEY Target key name already exists.\r\n"
2718        );
2719        // And the options come before even that, so a bad FREQ beats the busy
2720        // key the same way a bad DB beats a missing source in COPY.
2721        assert_eq!(
2722            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2723            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2724        );
2725    }
2726
2727    #[test]
2728    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2729        let mut f = Fixture::new();
2730        f.run(&[b"SET", b"a", b"hello"]);
2731        let good = payload(&f.raw(&[b"DUMP", b"a"]));
2732
2733        let mut flipped = good.clone();
2734        flipped[2] ^= 0x40;
2735        assert_eq!(
2736            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2737            "-ERR DUMP payload version or checksum are wrong\r\n"
2738        );
2739        assert_eq!(
2740            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2741            "-ERR DUMP payload version or checksum are wrong\r\n"
2742        );
2743        // A footer that is right over a body that is not. The type byte says
2744        // string and there is nothing behind it, so the checksum agrees and the
2745        // value does not exist.
2746        let mut truncated = good[..1].to_vec();
2747        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2748        let crc = yo_common::crc::crc64(0, &truncated);
2749        truncated.extend_from_slice(&crc.to_le_bytes());
2750        assert_eq!(
2751            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2752            "-ERR Bad data format\r\n"
2753        );
2754        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2755    }
2756
2757    #[test]
2758    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2759        let mut f = Fixture::new();
2760        f.run(&[b"SET", b"a", b"v"]);
2761        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2762        assert_eq!(
2763            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2764            "-ERR Invalid TTL value, must be >= 0\r\n"
2765        );
2766        assert_eq!(
2767            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2768            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2769        );
2770        assert_eq!(
2771            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2772            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2773        );
2774        // Both are accepted and both are then dropped, which is D-26.
2775        assert_eq!(
2776            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2777            "+OK\r\n"
2778        );
2779        assert_eq!(
2780            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2781            "+OK\r\n"
2782        );
2783    }
2784
2785    /// Neither word is refused for being the wrong one. Each is only accepted
2786    /// while the other is unset, so the second of the two falls through to the
2787    /// plain syntax error rather than getting a message of its own.
2788    #[test]
2789    fn restore_takes_idletime_or_freq_and_not_both() {
2790        let mut f = Fixture::new();
2791        f.run(&[b"SET", b"a", b"v"]);
2792        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2793        assert_eq!(
2794            f.run(&[
2795                b"RESTORE",
2796                b"b",
2797                b"0",
2798                &bytes,
2799                b"IDLETIME",
2800                b"1",
2801                b"FREQ",
2802                b"2"
2803            ]),
2804            "-ERR syntax error\r\n"
2805        );
2806        assert_eq!(
2807            f.run(&[
2808                b"RESTORE",
2809                b"b",
2810                b"0",
2811                &bytes,
2812                b"FREQ",
2813                b"2",
2814                b"IDLETIME",
2815                b"1"
2816            ]),
2817            "-ERR syntax error\r\n"
2818        );
2819        assert_eq!(
2820            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2821            "-ERR syntax error\r\n"
2822        );
2823        assert_eq!(
2824            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2825            "-ERR syntax error\r\n"
2826        );
2827    }
2828
2829    #[test]
2830    fn copy_checks_its_options_before_it_looks_for_anything() {
2831        let mut f = Fixture::new();
2832        // No key exists at all, and every one of these is still the option
2833        // complaint rather than a zero, which is the order a real server uses.
2834        assert_eq!(
2835            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2836            "-ERR DB index is out of range\r\n"
2837        );
2838        assert_eq!(
2839            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2840            "-ERR DB index is out of range\r\n"
2841        );
2842        assert_eq!(
2843            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2844            "-ERR value is not an integer or out of range\r\n"
2845        );
2846        assert_eq!(
2847            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2848            "-ERR syntax error\r\n"
2849        );
2850        assert_eq!(
2851            f.run(&[b"COPY", b"a", b"a"]),
2852            "-ERR source and destination objects are the same\r\n"
2853        );
2854        // Repeated, reordered and lowercased, and the last DB wins.
2855        assert_eq!(
2856            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2857            ":0\r\n"
2858        );
2859    }
2860
2861    #[test]
2862    fn time_is_two_bulk_strings_and_moves() {
2863        let mut f = Fixture::new();
2864        let first = f.run(&[b"TIME"]);
2865        assert!(first.starts_with("*2\r\n$"), "got {first}");
2866        let parts: Vec<&str> = first.split("\r\n").collect();
2867        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2868        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2869        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2870        assert!((0..1_000_000).contains(&micros), "got {micros}");
2871        // The coarse clock the keyspace uses is a cached millisecond that a
2872        // background tick refreshes, so a TIME built on it would answer the
2873        // same microsecond twice in a row here.
2874        assert_ne!(first, f.run(&[b"TIME"]));
2875    }
2876
2877    #[test]
2878    fn a_keyspace_scan_walks_every_key_once() {
2879        let mut f = Fixture::new();
2880        for i in 0..500 {
2881            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2882        }
2883
2884        let mut seen: Vec<String> = Vec::new();
2885        let mut cursor = "0".to_owned();
2886        let mut calls = 0;
2887        loop {
2888            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2889            seen.extend(keys);
2890            cursor = next;
2891            calls += 1;
2892            assert!(calls < 10_000, "the cursor is not advancing");
2893            if cursor == "0" {
2894                break;
2895            }
2896        }
2897
2898        seen.sort();
2899        seen.dedup();
2900        assert_eq!(seen.len(), 500, "every key once and only once");
2901        // And more than one call to get them, or the COUNT is being ignored and
2902        // the loop above proved nothing about resuming.
2903        assert!(calls > 1, "500 keys came back in one batch");
2904    }
2905
2906    #[test]
2907    fn a_scan_narrows_by_pattern_and_by_type() {
2908        let mut f = Fixture::new();
2909        f.run(&[b"SET", b"str", b"v"]);
2910        f.run(&[b"SADD", b"members", b"a"]);
2911        f.run(&[b"HSET", b"fields", b"f", b"v"]);
2912
2913        let all = |f: &mut Fixture, args: &[&[u8]]| {
2914            let mut out: Vec<String> = Vec::new();
2915            let mut cursor = "0".to_owned();
2916            loop {
2917                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2918                line.extend_from_slice(args);
2919                let (next, keys) = scan_reply(&f.run(&line));
2920                out.extend(keys);
2921                cursor = next;
2922                if cursor == "0" {
2923                    break;
2924                }
2925            }
2926            out.sort();
2927            out
2928        };
2929
2930        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2931        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2932        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2933        // Case insensitive, the same as Redis's own comparison.
2934        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2935        // A type nothing can hold is not an error, it just matches nothing.
2936        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2937        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2938        // Both filters at once, and they are an and rather than an or.
2939        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2940    }
2941
2942    #[test]
2943    fn a_scan_says_what_is_wrong_with_it() {
2944        let mut f = Fixture::new();
2945        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2946        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2947        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2948        assert_eq!(
2949            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2950            "-ERR syntax error\r\n"
2951        );
2952        assert_eq!(
2953            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2954            "-ERR value is not an integer or out of range\r\n"
2955        );
2956        assert_eq!(
2957            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2958            "-ERR syntax error\r\n"
2959        );
2960        // A cursor the client made up is a cursor. It resumes somewhere
2961        // arbitrary and answers whatever is there, which is what Redis does and
2962        // is the only behaviour that does not need the server to remember every
2963        // cursor it has handed out.
2964        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2965    }
2966
2967    #[test]
2968    fn keys_and_randomkey_look_at_the_whole_database() {
2969        let mut f = Fixture::new();
2970        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2971        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2972
2973        for name in ["one", "two", "three"] {
2974            f.run(&[b"SET", name.as_bytes(), b"v"]);
2975        }
2976        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2977        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2978        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2979
2980        for _ in 0..50 {
2981            let got = f.run(&[b"RANDOMKEY"]);
2982            assert!(
2983                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2984                "got {got}"
2985            );
2986        }
2987    }
2988
2989    #[test]
2990    fn a_walk_does_not_answer_keys_that_have_expired() {
2991        let mut f = Fixture::new();
2992        f.run(&[b"SET", b"alive", b"v"]);
2993        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2994        f.server.advance_clock_ms(2);
2995        assert_eq!(
2996            f.run(&[b"DBSIZE"]),
2997            ":2\r\n",
2998            "nothing has collected it yet"
2999        );
3000
3001        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
3002        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
3003        assert_eq!(keys, ["alive"]);
3004        for _ in 0..20 {
3005            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
3006        }
3007        // The walk collected it on the way past, which is what makes DBSIZE
3008        // here answer what Redis answers once its own cycle has been round.
3009        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3010    }
3011
3012    #[test]
3013    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
3014        let mut f = Fixture::new();
3015        f.run(&[b"SET", b"k", b"v"]);
3016        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
3017        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
3018
3019        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
3020        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
3021        let ms = int(&f.run(&[b"PTTL", b"k"]));
3022        assert!((99_000..=100_000).contains(&ms), "got {ms}");
3023
3024        // The absolute pair, derived from the same one number the store kept.
3025        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
3026        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
3027        assert_eq!(at, (at_ms + 500) / 1000);
3028        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
3029
3030        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
3031        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
3032        assert_eq!(
3033            f.run(&[b"PERSIST", b"k"]),
3034            ":0\r\n",
3035            "nothing to take off the second time"
3036        );
3037        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
3038        assert_eq!(
3039            f.run(&[b"GET", b"k"]),
3040            "$1\r\nv\r\n",
3041            "and the value went through all of that untouched"
3042        );
3043    }
3044
3045    #[test]
3046    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
3047        let mut f = Fixture::new();
3048        f.run(&[b"SET", b"str", b"v"]);
3049        f.run(&[b"SADD", b"set", b"a", b"b"]);
3050        f.run(&[b"HSET", b"hash", b"f", b"v"]);
3051
3052        for key in [b"str".as_slice(), b"set", b"hash"] {
3053            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
3054            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
3055        }
3056        // The body is not touched by any of that, which is the whole reason the
3057        // deadline lives in the record and the body lives somewhere else.
3058        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
3059        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
3060        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
3061    }
3062
3063    #[test]
3064    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
3065        let mut f = Fixture::new();
3066        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
3067            f.run(&[b"SET", key, b"v"]);
3068        }
3069        // Four ways of naming a moment that has passed, and all four are a
3070        // delete answering 1 rather than an error. Zero is a moment, minus one
3071        // is a moment, and the hash field commands refuse the negative one.
3072        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
3073        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
3074        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
3075        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
3076        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3077        assert_eq!(
3078            f.run(&[b"EXPIRE", b"a", b"100"]),
3079            ":0\r\n",
3080            "and the key really went, so there is nothing to put a deadline on"
3081        );
3082    }
3083
3084    #[test]
3085    fn the_four_conditions_decide_whether_the_deadline_moves() {
3086        let mut f = Fixture::new();
3087        f.run(&[b"SET", b"k", b"v"]);
3088
3089        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
3090        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
3091        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
3092        assert_eq!(
3093            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
3094            ":1\r\n",
3095            "no deadline reads as infinitely far away, so LT passes where GT fails"
3096        );
3097
3098        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
3099        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
3100        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
3101        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
3102        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
3103        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
3104
3105        // The condition is answered before the past check, so this is a 0 and
3106        // the key survives. The other order would delete it.
3107        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
3108        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
3109        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
3110        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
3111    }
3112
3113    #[test]
3114    fn the_conditions_are_a_set_and_not_a_keyword() {
3115        let mut f = Fixture::new();
3116        f.run(&[b"SET", b"k", b"v"]);
3117
3118        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
3119        assert_eq!(
3120            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
3121            ":0\r\n",
3122            "the same keyword twice means it once, and NX now has a deadline to fail on"
3123        );
3124
3125        // XX with LT is the one pair that is not either of them on its own: LT
3126        // alone would accept a key with no deadline and this does not.
3127        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
3128        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
3129        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
3130        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
3131        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
3132        f.run(&[b"PERSIST", b"k"]);
3133        assert_eq!(
3134            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
3135            ":0\r\n",
3136            "where LT on its own would have taken it"
3137        );
3138        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
3139    }
3140
3141    #[test]
3142    fn a_key_is_gone_once_its_moment_passes() {
3143        let mut f = Fixture::new();
3144        f.run(&[b"SET", b"k", b"v"]);
3145        f.run(&[b"EXPIRE", b"k", b"100"]);
3146
3147        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
3148        f.server.set_clock_ms(at as u64 + 1);
3149        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3150        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
3151        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
3152        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3153    }
3154
3155    #[test]
3156    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
3157        let mut f = Fixture::new();
3158        f.run(&[b"SET", b"k", b"v"]);
3159        for (bad, want) in [
3160            (
3161                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
3162                "-ERR value is not an integer or out of range\r\n",
3163            ),
3164            (
3165                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
3166                "-ERR Unsupported option MAYBE\r\n",
3167            ),
3168            (
3169                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
3170                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
3171            ),
3172            (
3173                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
3174                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
3175            ),
3176            (
3177                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
3178                "-ERR GT and LT options at the same time are not compatible\r\n",
3179            ),
3180            // Seconds that overflow when multiplied into milliseconds. Every
3181            // message names the command it came from.
3182            (
3183                &[b"EXPIRE", b"k", b"9223372036854775807"],
3184                "-ERR invalid expire time in 'expire' command\r\n",
3185            ),
3186            (
3187                &[b"EXPIREAT", b"k", b"9223372036854775807"],
3188                "-ERR invalid expire time in 'expireat' command\r\n",
3189            ),
3190            (
3191                &[b"PEXPIRE", b"k", b"9223372036854775807"],
3192                "-ERR invalid expire time in 'pexpire' command\r\n",
3193            ),
3194        ] {
3195            assert_eq!(f.run(bad), want, "for {bad:?}");
3196        }
3197        assert_eq!(
3198            f.run(&[b"TTL", b"k"]),
3199            ":-1\r\n",
3200            "and none of those put a deadline on anything"
3201        );
3202
3203        // The one of the four that has no arithmetic to overflow. Redis takes
3204        // it and holds the number as given, and a record here holds forty six
3205        // bits, so it lands in the year 4199 instead. D-17.
3206        assert_eq!(
3207            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
3208            ":1\r\n"
3209        );
3210        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
3211    }
3212
3213    #[test]
3214    fn flushing_empties_this_database_or_every_one_of_them() {
3215        let mut f = Fixture::new();
3216        f.run(&[b"SELECT", b"0"]);
3217        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3218        f.run(&[b"SELECT", b"1"]);
3219        f.run(&[b"SET", b"c", b"3"]);
3220        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3221        // ASYNC and SYNC are both taken and neither changes anything, since the
3222        // keyspace is empty before the OK goes out either way.
3223        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
3224        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3225        // Only database one was emptied.
3226        f.run(&[b"SELECT", b"0"]);
3227        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
3228        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
3229        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3230        f.run(&[b"SELECT", b"1"]);
3231        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3232        // Anything else after the name is a syntax error, and so is a third
3233        // argument even when the second one is a word we take.
3234        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
3235        assert_eq!(
3236            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
3237            "-ERR syntax error\r\n"
3238        );
3239    }
3240
3241    #[test]
3242    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
3243        let mut f = Fixture::new();
3244        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
3245        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
3246        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
3247        // Nothing is cached, so nothing is there, one answer per hash asked
3248        // about.
3249        assert_eq!(
3250            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
3251            "*2\r\n:0\r\n:0\r\n"
3252        );
3253        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
3254        assert_eq!(
3255            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
3256            "*0\r\n"
3257        );
3258        assert_eq!(
3259            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
3260            "-ERR Library not found\r\n"
3261        );
3262
3263        // Redis's two messages here are its own, one per container, and one of
3264        // them reads like a typo.
3265        assert_eq!(
3266            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
3267            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
3268        );
3269        assert_eq!(
3270            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
3271            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
3272        );
3273        // A second argument after the mode is the generic one instead, because
3274        // the count is checked before the word is looked at.
3275        assert_eq!(
3276            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
3277            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
3278        );
3279        assert_eq!(
3280            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
3281            "-ERR Unknown argument bogus\r\n"
3282        );
3283        assert_eq!(
3284            f.run(&[b"SCRIPT", b"EXISTS"]),
3285            "-ERR wrong number of arguments for 'script|exists' command\r\n"
3286        );
3287
3288        // The ones that need an interpreter are not here, and say so rather
3289        // than answering OK to a load that loaded nothing.
3290        assert_eq!(
3291            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
3292            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
3293        );
3294        assert_eq!(
3295            f.run(&[b"FUNCTION", b"STATS"]),
3296            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
3297        );
3298    }
3299
3300    #[test]
3301    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
3302        let mut f = Fixture::new();
3303        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
3304        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
3305        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
3306        // Read back as a string it is still an integer, written out as digits
3307        // only because somebody asked for them.
3308        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
3309        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
3310        // A counter that is not a number is the error the store raises and this
3311        // layer only spells, which is the whole point of the split.
3312        f.run(&[b"SET", b"k", b"hello"]);
3313        assert_eq!(
3314            f.run(&[b"INCR", b"k"]),
3315            "-ERR value is not an integer or out of range\r\n"
3316        );
3317        assert_eq!(
3318            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
3319            "-ERR increment would produce NaN or Infinity\r\n"
3320        );
3321    }
3322
3323    /// Every one of these was read off a running 8.8. They are the answers a
3324    /// client library's own test suite checks, and the shapes are not
3325    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
3326    /// integer, `INCREX` is a pair.
3327    #[test]
3328    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
3329        let mut f = Fixture::new();
3330        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
3331        // The same digest a real 8.8 answers for the same five bytes, which is
3332        // what makes `IFDEQ` usable against a mixed deployment.
3333        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
3334        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
3335        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
3336        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
3337        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
3338        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
3339        assert_eq!(
3340            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
3341            "*2\r\n:1\r\n:0\r\n",
3342            "a refused increment reports the value it left alone and applied nothing"
3343        );
3344        assert_eq!(
3345            f.run(&[
3346                b"INCREX",
3347                b"n",
3348                b"BYINT",
3349                b"5",
3350                b"UBOUND",
3351                b"3",
3352                b"SATURATE"
3353            ]),
3354            "*2\r\n:3\r\n:2\r\n"
3355        );
3356        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
3357        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
3358    }
3359
3360    #[test]
3361    fn the_same_answers_come_out_in_resp3_spelling() {
3362        let mut f = Fixture::new();
3363        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
3364        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
3365        // A float counter is a double on RESP3 and the digits in a bulk string
3366        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
3367        assert_eq!(
3368            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
3369            "*2\r\n,1.5\r\n,1.5\r\n"
3370        );
3371        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
3372        // `RESET` puts the protocol back, which is the part that is easy to
3373        // miss and leaves a pooled connection speaking the wrong one.
3374        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
3375        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
3376    }
3377
3378    #[test]
3379    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
3380        let mut f = Fixture::new();
3381        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
3382        assert_eq!(flow, Flow::Continue);
3383        assert_eq!(
3384            reply,
3385            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
3386        );
3387        // A name with a line ending in it cannot write its own frame into the
3388        // stream, which is the reason the error writer maps them to spaces.
3389        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
3390        assert_eq!(reply.matches("\r\n").count(), 1);
3391    }
3392
3393    #[test]
3394    fn arity_is_checked_before_the_command_is() {
3395        let mut f = Fixture::new();
3396        assert_eq!(
3397            f.run(&[b"GET"]),
3398            "-ERR wrong number of arguments for 'get' command\r\n"
3399        );
3400        assert_eq!(
3401            f.run(&[b"MSET", b"k"]),
3402            "-ERR wrong number of arguments for 'mset' command\r\n"
3403        );
3404        // The table says `PING` takes one or more and a real server then
3405        // refuses three, which is the sort of thing that only shows up against
3406        // the real thing.
3407        assert_eq!(
3408            f.run(&[b"PING", b"a", b"b"]),
3409            "-ERR wrong number of arguments for 'ping' command\r\n"
3410        );
3411        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
3412        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
3413        // `DELEX` takes two or four and nothing between.
3414        assert_eq!(
3415            f.run(&[b"DELEX", b"k", b"IFEQ"]),
3416            "-ERR wrong number of arguments for 'delex' command\r\n"
3417        );
3418    }
3419
3420    /// The option rules, all of them measured against 8.8 rather than read off
3421    /// the documentation. The surprising one is that `SET` accepts the same
3422    /// keyword twice and `INCREX` does not.
3423    #[test]
3424    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
3425        let mut f = Fixture::new();
3426        let syntax = "-ERR syntax error\r\n";
3427        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
3428        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
3429        assert_eq!(
3430            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
3431            syntax
3432        );
3433        assert_eq!(
3434            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
3435            syntax
3436        );
3437        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
3438        // Twice is fine, and the last one wins.
3439        assert_eq!(
3440            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
3441            "+OK\r\n"
3442        );
3443        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
3444        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
3445        // `INCREX` refuses what `SET` allows.
3446        assert_eq!(
3447            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
3448            syntax
3449        );
3450        assert_eq!(
3451            f.run(&[b"INCREX", b"n", b"ENX"]),
3452            "-ERR ENX flag requires an expiration\r\n"
3453        );
3454        assert_eq!(
3455            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
3456            "-ERR UBOUND is not an integer or out of range\r\n"
3457        );
3458        assert_eq!(
3459            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
3460            "-ERR LBOUND can't be greater than UBOUND\r\n"
3461        );
3462        assert_eq!(
3463            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
3464            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
3465        );
3466    }
3467
3468    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
3469    /// key that is not there, which answers null without ever looking at the
3470    /// expiration it was given.
3471    #[test]
3472    fn the_expiry_rules_are_redis_own() {
3473        let mut f = Fixture::new();
3474        let bad = "-ERR invalid expire time in 'set' command\r\n";
3475        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
3476        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
3477        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
3478        assert_eq!(
3479            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
3480            bad
3481        );
3482        assert_eq!(
3483            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
3484            "-ERR value is not an integer or out of range\r\n"
3485        );
3486        assert_eq!(
3487            f.run(&[b"SETEX", b"k", b"0", b"v"]),
3488            "-ERR invalid expire time in 'setex' command\r\n"
3489        );
3490        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
3491        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
3492        assert_eq!(
3493            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
3494            "-ERR syntax error\r\n",
3495            "the option list is still checked before the key is looked up"
3496        );
3497        // A deadline in the past is accepted and the key goes with it.
3498        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3499        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
3500        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3501    }
3502
3503    #[test]
3504    fn mset_takes_its_pairs_from_the_read_buffer() {
3505        let mut f = Fixture::new();
3506        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
3507        assert_eq!(
3508            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
3509            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
3510        );
3511        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
3512        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
3513        assert_eq!(
3514            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
3515            "-ERR wrong number of key-value pairs\r\n"
3516        );
3517        assert_eq!(
3518            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
3519            "-ERR invalid numkeys value\r\n"
3520        );
3521        assert_eq!(
3522            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
3523            "-ERR invalid numkeys value\r\n"
3524        );
3525    }
3526
3527    #[test]
3528    fn lcs_answers_the_length_the_string_and_the_runs() {
3529        let mut f = Fixture::new();
3530        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
3531        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3532        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3533        assert_eq!(
3534            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3535            "*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"
3536        );
3537        // Without `IDX` the two options that only mean something with it are
3538        // accepted and ignored, which is what a real server does.
3539        assert_eq!(
3540            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3541            "$6\r\nmytext\r\n"
3542        );
3543    }
3544
3545    #[test]
3546    fn select_moves_the_connection_and_the_databases_stay_apart() {
3547        let mut f = Fixture::new();
3548        f.run(&[b"SET", b"k", b"zero"]);
3549        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3550        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3551        f.run(&[b"SET", b"k", b"four"]);
3552        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3553        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3554        assert_eq!(
3555            f.run(&[b"SELECT", b"99"]),
3556            "-ERR DB index is out of range\r\n"
3557        );
3558        assert_eq!(
3559            f.run(&[b"SELECT", b"-1"]),
3560            "-ERR DB index is out of range\r\n"
3561        );
3562        assert_eq!(
3563            f.run(&[b"SELECT", b"abc"]),
3564            "-ERR value is not an integer or out of range\r\n"
3565        );
3566        // `RESET` brings it back to zero.
3567        f.run(&[b"SELECT", b"4"]);
3568        f.run(&[b"RESET"]);
3569        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3570    }
3571
3572    #[test]
3573    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3574        let mut f = Fixture::new();
3575        let reply = f.run(&[b"HELLO"]);
3576        assert!(reply.starts_with("*14\r\n"), "{reply}");
3577        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3578        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3579        assert!(
3580            reply.contains(":7\r\n"),
3581            "the connection id is in there: {reply}"
3582        );
3583        assert_eq!(
3584            f.run(&[b"HELLO", b"4"]),
3585            "-NOPROTO unsupported protocol version\r\n"
3586        );
3587        assert_eq!(
3588            f.run(&[b"HELLO", b"abc"]),
3589            "-ERR Protocol version is not an integer or out of range\r\n"
3590        );
3591        assert_eq!(
3592            f.run(&[b"HELLO", b"3", b"SETNAME"]),
3593            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3594        );
3595        assert!(
3596            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3597                .starts_with("%7\r\n")
3598        );
3599        assert_eq!(f.session.name(), b"bob");
3600        f.run(&[b"RESET"]);
3601        assert_eq!(f.session.name(), b"");
3602    }
3603
3604    #[test]
3605    fn command_describes_this_server_in_the_shape_a_driver_reads() {
3606        let mut f = Fixture::new();
3607        let count = format!(":{}\r\n", COMMANDS.len());
3608        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3609        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3610        assert_eq!(
3611            info,
3612            "*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\
3613             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3614        );
3615        // A null in the list, and the plain one: `$-1` and not `*-1`.
3616        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3617        assert_eq!(
3618            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3619            "*1\r\n$8\r\ngetrange\r\n"
3620        );
3621        assert_eq!(
3622            f.run(&[b"COMMAND", b"NOPE"]),
3623            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3624        );
3625    }
3626
3627    /// A cluster aware client asks this question and then routes on the
3628    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
3629    /// that matters.
3630    #[test]
3631    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3632        let mut f = Fixture::new();
3633        assert_eq!(
3634            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3635            "*1\r\n$1\r\nk\r\n"
3636        );
3637        assert_eq!(
3638            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3639            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3640        );
3641        assert_eq!(
3642            f.run(&[
3643                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3644            ]),
3645            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3646        );
3647        assert_eq!(
3648            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3649            "-ERR The command has no key arguments\r\n"
3650        );
3651        assert_eq!(
3652            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3653            "-ERR Invalid number of arguments specified for command\r\n"
3654        );
3655    }
3656
3657    #[test]
3658    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3659        let mut f = Fixture::new();
3660        assert_eq!(
3661            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3662            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3663        );
3664        // A pattern matches more than one, and a setting two patterns both ask
3665        // for is still sent once.
3666        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3667        assert!(both.starts_with("*6\r\n"), "{both}");
3668        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3669        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3670        assert_eq!(
3671            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3672            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3673        );
3674        assert_eq!(
3675            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3676            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3677        );
3678        assert_eq!(
3679            f.run(&[b"CONFIG", b"GET"]),
3680            "-ERR wrong number of arguments for 'config|get' command\r\n"
3681        );
3682        // Too few arguments and an odd number of them are different
3683        // complaints, which is the sort of thing only the real server tells
3684        // you.
3685        assert_eq!(
3686            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3687            "-ERR wrong number of arguments for 'config|set' command\r\n"
3688        );
3689        assert_eq!(
3690            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3691            "-ERR syntax error\r\n"
3692        );
3693        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3694        assert_eq!(
3695            f.run(&[b"CONFIG", b"REWRITE"]),
3696            "-ERR The server is running without a config file\r\n"
3697        );
3698    }
3699
3700    #[test]
3701    fn the_eviction_policy_reads_back_what_was_written_to_it() {
3702        let mut f = Fixture::new();
3703        assert_eq!(
3704            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3705            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3706        );
3707        assert_eq!(
3708            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3709            "+OK\r\n",
3710            "the name is matched without regard to case, like every other one"
3711        );
3712        assert_eq!(
3713            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3714            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3715        );
3716        // And INFO agrees with CONFIG, which it did not when it was a literal.
3717        assert!(
3718            f.run(&[b"INFO", b"memory"])
3719                .contains("maxmemory_policy:allkeys-lfu"),
3720            "INFO and CONFIG disagree about the policy"
3721        );
3722        // The refusal names every legal value in the order the real server's
3723        // enum table lists them, because a client comparing the message compares
3724        // the whole string.
3725        assert_eq!(
3726            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3727            "-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"
3728        );
3729        // A bad pair leaves the good one in the same command alone, and the
3730        // policy is checked by the same pass that checks the numbers.
3731        assert_eq!(
3732            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3733            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3734        );
3735        f.run(&[
3736            b"CONFIG",
3737            b"SET",
3738            b"hash-max-listpack-entries",
3739            b"7",
3740            b"maxmemory-policy",
3741            b"nonsense",
3742        ]);
3743        assert_eq!(
3744            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3745            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3746        );
3747    }
3748
3749    #[test]
3750    fn the_three_eviction_numbers_read_back_too() {
3751        let mut f = Fixture::new();
3752        for (name, default, set) in [
3753            ("maxmemory-samples", "5", "12"),
3754            ("lfu-log-factor", "10", "3"),
3755            ("lfu-decay-time", "1", "60"),
3756        ] {
3757            let get = || {
3758                format!(
3759                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3760                    name.len(),
3761                    default.len()
3762                )
3763            };
3764            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3765            assert_eq!(
3766                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3767                "+OK\r\n"
3768            );
3769            assert_eq!(
3770                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3771                format!(
3772                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3773                    name.len(),
3774                    set.len()
3775                )
3776            );
3777            // A number that is not a number is refused with the same sentence
3778            // every other number gets, which names the setting the client typed.
3779            assert_eq!(
3780                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3781                format!(
3782                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3783                )
3784            );
3785        }
3786    }
3787
3788    #[test]
3789    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3790        let mut f = Fixture::new();
3791        assert_eq!(
3792            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3793            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3794            "no limit is the default"
3795        );
3796        // The pairing is Redis's and it is a trap: the bare letter is a power of
3797        // ten and the one with the b is a power of two.
3798        for (typed, bytes) in [
3799            (&b"1024"[..], "1024"),
3800            (b"1k", "1000"),
3801            (b"1kb", "1024"),
3802            (b"1M", "1000000"),
3803            (b"1Mb", "1048576"),
3804            (b"1gb", "1073741824"),
3805            (b"100mb", "104857600"),
3806        ] {
3807            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3808            assert_eq!(
3809                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3810                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3811                "set {}",
3812                String::from_utf8_lossy(typed)
3813            );
3814        }
3815        assert!(
3816            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3817            "the report agrees with the setting"
3818        );
3819
3820        // A unit nobody has heard of, and a negative number, which is not a very
3821        // large one however it is spelled.
3822        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3823            assert_eq!(
3824                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3825                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3826                "refused {}",
3827                String::from_utf8_lossy(bad)
3828            );
3829        }
3830        assert!(
3831            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3832            "and the refusal left the old one alone"
3833        );
3834    }
3835
3836    #[test]
3837    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3838        let mut f = Fixture::new();
3839        f.run(&[b"SET", b"here", b"already"]);
3840        // A byte, which is under what an empty server holds, so nothing this
3841        // command could do would get it under. The default policy is
3842        // `noeviction`, so nothing is what it does.
3843        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3844        assert_eq!(
3845            f.run(&[b"SET", b"k", b"v"]),
3846            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3847        );
3848        assert_eq!(
3849            f.run(&[b"LPUSH", b"l", b"v"]),
3850            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3851        );
3852        // Reading is allowed, and so is the one thing that would help.
3853        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3854        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3855        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3856
3857        // Taking the limit away lets the write through again.
3858        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3859        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3860    }
3861
3862    #[test]
3863    fn an_allkeys_policy_makes_room_instead_of_refusing() {
3864        let mut f = Fixture::new();
3865        let val = vec![b'v'; 256];
3866        for i in 0..24000u32 {
3867            let k = format!("key:{i:08}");
3868            f.run(&[b"SET", k.as_bytes(), &val]);
3869        }
3870        let full = f.server.memory_bytes();
3871        assert!(
3872            full > 3 * 1024 * 1024,
3873            "the arena is several segments: {full}"
3874        );
3875
3876        // Two megabytes under what it is holding, which is one segment's worth,
3877        // so getting there means giving a whole segment back and not just
3878        // dropping a few records.
3879        let limit = full - 2 * 1024 * 1024;
3880        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3881        f.run(&[
3882            b"CONFIG",
3883            b"SET",
3884            b"maxmemory",
3885            limit.to_string().as_bytes(),
3886        ]);
3887
3888        // Writes keep working the whole way down. The budget means one command
3889        // does not do it all, so this runs until the server has settled and
3890        // checks that nothing was refused on the way.
3891        for i in 0..2000u32 {
3892            let k = format!("new:{i:08}");
3893            assert_eq!(
3894                f.run(&[b"SET", k.as_bytes(), &val]),
3895                "+OK\r\n",
3896                "write {i} was refused"
3897            );
3898            f.server.refresh_memory();
3899            if f.server.memory_bytes() <= limit {
3900                break;
3901            }
3902        }
3903        assert!(
3904            f.server.memory_bytes() <= limit,
3905            "it never got under: {} against {limit}",
3906            f.server.memory_bytes()
3907        );
3908        let info = f.run(&[b"INFO", b"stats"]);
3909        assert!(!info.contains("evicted_keys:0"), "{info}");
3910        assert!(
3911            f.run(&[b"DBSIZE"]) != ":0\r\n",
3912            "and it did not empty the database to get there"
3913        );
3914    }
3915
3916    #[test]
3917    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3918        // The limit is judged against a number kept as the collections move,
3919        // rather than found by asking all of them, and the two have to be the
3920        // same number or the limit is enforced against a fiction. This does the
3921        // things that move it, which is growing a collection, shrinking one,
3922        // changing its representation, deleting it and reusing its slot, across
3923        // all five types, and checks the two against each other as it goes.
3924        let mut f = Fixture::new();
3925        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3926        let big = vec![b'v'; 200];
3927
3928        for i in 0..400u32 {
3929            let n = i.to_string();
3930            let n = n.as_bytes();
3931            f.run(&[b"SADD", b"s", n]);
3932            f.run(&[b"SADD", b"s2", &big]);
3933            f.run(&[b"HSET", b"h", n, &big]);
3934            f.run(&[b"RPUSH", b"l", &big]);
3935            f.run(&[b"ZADD", b"z", n, n]);
3936            f.run(&[b"ARSET", b"a", n, &big]);
3937            if i % 7 == 0 {
3938                f.run(&[b"SREM", b"s", n]);
3939                f.run(&[b"HDEL", b"h", n]);
3940                f.run(&[b"LPOP", b"l"]);
3941                f.run(&[b"ZREM", b"z", n]);
3942                f.run(&[b"ARDEL", b"a", n]);
3943            }
3944            if i % 53 == 0 {
3945                // Every type deleted and made again, so a slot goes on the free
3946                // list and comes back holding something else.
3947                f.run(&[b"DEL", b"s2"]);
3948            }
3949            assert_eq!(
3950                f.server.settled_memory(),
3951                f.server.memory_bytes(),
3952                "after round {i}"
3953            );
3954        }
3955
3956        // The run has to have built something, or the two numbers agreeing is
3957        // two zeroes agreeing.
3958        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3959        assert!(
3960            f.server.memory_bytes() > 512 * 1024,
3961            "{}",
3962            f.server.memory_bytes()
3963        );
3964
3965        // And it survives the collections going away entirely.
3966        f.run(&[b"FLUSHALL"]);
3967        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3968    }
3969
3970    #[test]
3971    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3972        // A server with no limit does not keep the running total, so setting a
3973        // limit on a database that is already full has to start it from a walk.
3974        // If it did not, the first reading would be zero and the server would
3975        // think it had all the room in the world.
3976        let mut f = Fixture::new();
3977        for i in 0..200u32 {
3978            let n = i.to_string();
3979            f.run(&[b"SADD", b"s", n.as_bytes()]);
3980            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3981        }
3982        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3983        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3984
3985        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3986        for i in 200..400u32 {
3987            let n = i.to_string();
3988            f.run(&[b"SADD", b"s", n.as_bytes()]);
3989        }
3990        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3991        assert_eq!(
3992            f.server.settled_memory(),
3993            f.server.memory_bytes(),
3994            "the writes it was not watching are in the number it started from"
3995        );
3996    }
3997
3998    #[test]
3999    fn evicted_keys_and_expired_keys_are_different_numbers() {
4000        let mut f = Fixture::new();
4001        // Nothing has been evicted and nothing can be under the default policy,
4002        // so this stays at zero while the other one moves.
4003        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
4004        f.server.advance_clock_ms(20);
4005        f.run(&[b"GET", b"gone"]);
4006        let info = f.run(&[b"INFO", b"stats"]);
4007        assert!(info.contains("expired_keys:1"), "{info}");
4008        assert!(info.contains("evicted_keys:0"), "{info}");
4009    }
4010
4011    #[test]
4012    fn the_object_subcommands_follow_the_policy() {
4013        let mut f = Fixture::new();
4014        f.run(&[b"SET", b"s", b"v"]);
4015        // Under the default the clock is kept and the counter is not, and under
4016        // an LFU policy it is the other way round. Each subcommand refuses on
4017        // the side where its reading of the three bytes means nothing.
4018        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
4019        assert!(
4020            f.run(&[b"OBJECT", b"FREQ", b"s"])
4021                .starts_with("-ERR An LFU maxmemory policy is not selected"),
4022        );
4023
4024        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
4025        assert!(
4026            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
4027                .starts_with("-ERR An LFU maxmemory policy is selected"),
4028        );
4029        // The key was written under a clock policy, so what comes back is that
4030        // clock read as a counter. It is a number and not an error, which is the
4031        // point: switching at runtime does not invalidate anything, it only makes
4032        // the old field mean something else until the key is used again.
4033        assert!(
4034            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
4035            "FREQ should answer under an LFU policy"
4036        );
4037    }
4038
4039    #[test]
4040    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
4041        let mut f = Fixture::new();
4042        f.run(&[b"SET", b"s", b"hello"]);
4043        f.run(&[b"SET", b"n", b"123"]);
4044        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
4045        f.run(&[b"SADD", b"ss", b"a", b"b"]);
4046        f.run(&[b"HSET", b"h", b"f", b"v"]);
4047        for (key, want) in [
4048            (b"s".as_slice(), "embstr"),
4049            (b"n", "int"),
4050            (b"si", "intset"),
4051            (b"ss", "listpack"),
4052            (b"h", "listpack"),
4053        ] {
4054            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
4055            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
4056        }
4057
4058        // A field deadline widens the blob rather than promoting it, and this
4059        // is the only place a client can see that happen.
4060        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
4061        assert_eq!(
4062            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4063            "$10\r\nlistpackex\r\n"
4064        );
4065
4066        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
4067        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
4068        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
4069    }
4070
4071    #[test]
4072    fn object_answers_nil_for_a_key_that_is_not_there() {
4073        let mut f = Fixture::new();
4074        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
4075            assert_eq!(
4076                f.run(&[b"OBJECT", sub, b"nokey"]),
4077                "$-1\r\n",
4078                "a nil and not an error, which is what 8.10.1 does"
4079            );
4080        }
4081        // And the key is looked up before FREQ has its complaint, so the
4082        // complaint only reaches a key that exists.
4083        f.run(&[b"SET", b"s", b"v"]);
4084        assert!(
4085            f.run(&[b"OBJECT", b"FREQ", b"s"])
4086                .starts_with("-ERR An LFU maxmemory policy is not"),
4087        );
4088        assert_eq!(
4089            f.run(&[b"OBJECT", b"NOPE", b"s"]),
4090            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
4091        );
4092        assert_eq!(
4093            f.run(&[b"OBJECT", b"ENCODING"]),
4094            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
4095        );
4096        assert_eq!(
4097            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
4098            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
4099        );
4100        assert_eq!(
4101            f.run(&[b"OBJECT"]),
4102            "-ERR wrong number of arguments for 'object' command\r\n"
4103        );
4104    }
4105
4106    #[test]
4107    fn config_moves_the_ladder_and_object_encoding_agrees() {
4108        let mut f = Fixture::new();
4109        assert_eq!(
4110            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
4111            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
4112            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
4113        );
4114        // The old spelling is the same number under a different name, and a
4115        // glob that catches both sends both.
4116        assert_eq!(
4117            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
4118            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
4119        );
4120        assert!(
4121            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
4122                .starts_with("*8\r\n")
4123        );
4124        assert!(
4125            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
4126                .starts_with("*6\r\n")
4127        );
4128
4129        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
4130        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
4131
4132        assert_eq!(
4133            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
4134            "+OK\r\n",
4135            "written under the old name and read back under the new one"
4136        );
4137        assert_eq!(
4138            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
4139            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
4140        );
4141        assert_eq!(
4142            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4143            "$8\r\nlistpack\r\n",
4144            "the hash that already exists is left exactly where it was"
4145        );
4146        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
4147        assert_eq!(
4148            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
4149            "$9\r\nhashtable\r\n",
4150            "and the next one built goes straight to a table"
4151        );
4152
4153        // The set has three of these and all three move.
4154        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
4155        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
4156        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
4157        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
4158        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
4159        assert_eq!(
4160            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
4161            "$9\r\nhashtable\r\n"
4162        );
4163    }
4164
4165    #[test]
4166    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
4167        let mut f = Fixture::new();
4168        assert_eq!(
4169            f.run(&[
4170                b"CONFIG",
4171                b"SET",
4172                b"hash-max-listpack-entries",
4173                b"7",
4174                b"set-max-listpack-entries",
4175                b"abc"
4176            ]),
4177            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
4178        );
4179        assert_eq!(
4180            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
4181            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
4182            "the pair in front of the bad one did not go in"
4183        );
4184        // The name in the complaint is the one that was typed, so the old
4185        // spelling comes back as the old spelling.
4186        assert_eq!(
4187            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
4188            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
4189        );
4190        assert_eq!(
4191            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
4192            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
4193        );
4194        // A number past what an i64 holds is the parse complaint and not the
4195        // range one, which is upstream reading it before it checks it.
4196        assert_eq!(
4197            f.run(&[
4198                b"CONFIG",
4199                b"SET",
4200                b"set-max-intset-entries",
4201                b"99999999999999999999"
4202            ]),
4203            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
4204        );
4205        assert_eq!(
4206            f.run(&[
4207                b"CONFIG",
4208                b"SET",
4209                b"set-max-intset-entries",
4210                b"9223372036854775807"
4211            ]),
4212            "+OK\r\n"
4213        );
4214    }
4215
4216    #[test]
4217    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
4218        let mut f = Fixture::new();
4219        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
4220        f.run(&[b"SELECT", b"3"]);
4221        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4222        assert_eq!(
4223            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
4224            "$9\r\nhashtable\r\n",
4225            "these are one server wide number in Redis, whatever a Keyspace carries"
4226        );
4227    }
4228
4229    #[test]
4230    fn info_reports_the_numbers_it_can_stand_behind() {
4231        let mut f = Fixture::new();
4232        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
4233        let all = f.run(&[b"INFO"]);
4234        assert!(all.contains("redis_version:8.8.0"), "{all}");
4235        assert!(
4236            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
4237            "{all}"
4238        );
4239        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
4240        assert!(all.contains("role:master"), "{all}");
4241        // One section is one section.
4242        let clients = f.run(&[b"INFO", b"clients"]);
4243        assert!(clients.contains("connected_clients:0"), "{clients}");
4244        assert!(!clients.contains("redis_version"), "{clients}");
4245        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
4246    }
4247
4248    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
4249    ///
4250    /// This is Redis's `unit/info-command` written against the fixture. Every
4251    /// assertion in it is one of theirs, in their order, and the two fields it
4252    /// turns on are the two that suite was failing on: `master_repl_offset`,
4253    /// which is in the default set, and `rejected_calls`, which is not.
4254    #[test]
4255    fn commandstats_is_asked_for_and_replication_is_not() {
4256        let mut f = Fixture::new();
4257        for arg in ["", "all", "default", "everything"] {
4258            let info = if arg.is_empty() {
4259                f.run(&[b"INFO"])
4260            } else {
4261                f.run(&[b"INFO", arg.as_bytes()])
4262            };
4263            assert!(info.contains("redis_version"), "{arg}: {info}");
4264            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
4265            assert!(info.contains("used_memory"), "{arg}: {info}");
4266            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
4267            let asked = arg == "all" || arg == "everything";
4268            assert_eq!(
4269                info.contains("rejected_calls"),
4270                asked,
4271                "{arg} should{} carry the command counters: {info}",
4272                if asked { "" } else { " not" }
4273            );
4274        }
4275
4276        let cpu = f.run(&[b"INFO", b"cpu"]);
4277        assert!(cpu.contains("used_cpu_user"), "{cpu}");
4278        assert!(!cpu.contains("used_memory"), "{cpu}");
4279
4280        // Their case, to make the point that a section name is not case
4281        // sensitive any more than a command name is.
4282        let stats = f.run(&[b"INFO", b"commandSTATS"]);
4283        assert!(!stats.contains("used_memory"), "{stats}");
4284        assert!(stats.contains("rejected_calls"), "{stats}");
4285
4286        // Two sections named, and neither of them pulls in a third.
4287        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
4288        assert!(pair.contains("used_cpu_user"), "{pair}");
4289        assert!(!pair.contains("master_repl_offset"), "{pair}");
4290
4291        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
4292        assert!(with_all.contains("used_memory"), "{with_all}");
4293        assert!(with_all.contains("master_repl_offset"), "{with_all}");
4294        assert!(with_all.contains("rejected_calls"), "{with_all}");
4295        // A section named twice is still written once.
4296        assert_eq!(
4297            with_all.matches("used_cpu_user_children").count(),
4298            1,
4299            "{with_all}"
4300        );
4301
4302        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
4303        assert!(with_default.contains("used_memory"), "{with_default}");
4304        assert!(
4305            with_default.contains("master_repl_offset"),
4306            "{with_default}"
4307        );
4308        assert!(!with_default.contains("rejected_calls"), "{with_default}");
4309        assert_eq!(
4310            with_default.matches("used_cpu_user_children").count(),
4311            1,
4312            "{with_default}"
4313        );
4314    }
4315
4316    /// The memory section says what this process may use, not what the machine
4317    /// has.
4318    ///
4319    /// The distinction is the whole point of it. A server inside a container
4320    /// that reports the host's memory is a server whose operator sizes it for
4321    /// memory it will be killed for touching, so all three numbers are there:
4322    /// what the machine has, what the cgroup allows, and the quarter of the
4323    /// tighter one that pools are sized from.
4324    #[test]
4325    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
4326        let mut f = Fixture::new();
4327        let info = f.run(&[b"INFO", b"memory"]);
4328        for field in [
4329            "total_system_memory:",
4330            "mem_cgroup_limit:",
4331            "mem_limit:",
4332            "mem_budget:",
4333        ] {
4334            assert!(info.contains(field), "no {field} in {info}");
4335        }
4336
4337        let field = |name: &str| -> u64 {
4338            info.lines()
4339                .find_map(|l| l.strip_prefix(name))
4340                .unwrap_or_else(|| panic!("no {name} in {info}"))
4341                .trim()
4342                .parse()
4343                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
4344        };
4345        let limit = field("mem_limit:");
4346        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
4347        // Zero means there is no limit to report, which is a real answer on a
4348        // machine with no cgroups and no way to ask how big it is.
4349        if limit != 0 {
4350            let host = field("total_system_memory:");
4351            let cgroup = field("mem_cgroup_limit:");
4352            assert!(
4353                limit == host || limit == cgroup,
4354                "the limit came from neither number: {info}"
4355            );
4356        }
4357    }
4358
4359    /// The three counters, each on the path that raises it.
4360    ///
4361    /// `calls` on a command that worked, `failed_calls` on one that ran and
4362    /// answered with an error, and `rejected_calls` on one that never ran at
4363    /// all. The last two are the pair that is easy to collapse into one number
4364    /// and that Redis keeps apart, because a client sending the wrong number of
4365    /// arguments and a client asking for a list element that is not there are
4366    /// not the same problem.
4367    #[test]
4368    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
4369        let mut f = Fixture::new();
4370        f.run(&[b"SET", b"k", b"v"]);
4371        f.run(&[b"SET", b"k", b"w"]);
4372        // Ran, and answered with an error, because `k` is not a list.
4373        f.run(&[b"LPUSH", b"k", b"x"]);
4374        // Never ran: `LPUSH` takes at least three arguments.
4375        f.run(&[b"LPUSH", b"k"]);
4376
4377        let stats = f.run(&[b"INFO", b"commandstats"]);
4378        assert!(
4379            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
4380            "{stats}"
4381        );
4382        assert!(
4383            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
4384            "{stats}"
4385        );
4386        assert!(
4387            !stats.contains("cmdstat_zadd"),
4388            "a command nobody has sent has no row: {stats}"
4389        );
4390    }
4391
4392    /// A cache that writes with a deadline and never reads back used to hold
4393    /// every key it had ever written, because lazy expiry needs somebody to walk
4394    /// past a key before it can reclaim it and nobody ever did.
4395    #[test]
4396    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
4397        let mut f = Fixture::new();
4398        for i in 0..3_000u32 {
4399            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4400        }
4401        for i in 0..1_000u32 {
4402            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4403        }
4404        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
4405        f.advance(100);
4406        assert_eq!(
4407            f.run(&[b"DBSIZE"]),
4408            ":4000\r\n",
4409            "DBSIZE counts records and nothing has read past the dead ones yet"
4410        );
4411
4412        // What the shard loop does, one slice at a time.
4413        let mut spent = 0;
4414        for _ in 0..2_000 {
4415            spent += f.server.expire_step(4096);
4416            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
4417                break;
4418            }
4419        }
4420        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
4421        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
4422        for i in 0..1_000u32 {
4423            assert_eq!(
4424                f.run(&[b"GET", format!("k{i}").as_bytes()]),
4425                "$1\r\nv\r\n",
4426                "it took a key that had no deadline"
4427            );
4428        }
4429    }
4430
4431    #[test]
4432    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
4433        let mut f = Fixture::new();
4434        for i in 0..2_000u32 {
4435            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
4436        }
4437        assert_eq!(f.server.expire_step(4096), 0);
4438        // And one database having them does not make the other fifteen pay.
4439        f.run(&[b"SELECT", b"3"]);
4440        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
4441        f.advance(100);
4442        for _ in 0..64 {
4443            f.server.expire_step(4096);
4444        }
4445        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4446        f.run(&[b"SELECT", b"0"]);
4447        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
4448        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
4449    }
4450
4451    /// The gate, which is what stops a maintenance slice that runs every hundred
4452    /// nanoseconds from drawing a sample every hundred nanoseconds.
4453    #[test]
4454    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
4455        let mut f = Fixture::new();
4456        for i in 0..500u32 {
4457            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
4458        }
4459        f.advance(100);
4460        let at = f.server.striped(0).now_ms();
4461        f.server.set_clock_ms(at);
4462        // A small budget, so that one slice cannot finish the job and a second
4463        // one having nothing to do would mean the gate and not an empty
4464        // database.
4465        assert!(f.server.expire_slice(8) > 0, "the first one works");
4466        for _ in 0..1_000 {
4467            assert_eq!(
4468                f.server.expire_slice(8),
4469                0,
4470                "the millisecond has not moved and neither should this"
4471            );
4472        }
4473        assert!(
4474            f.server.striped(0).expires() > 400,
4475            "there is plenty left to take"
4476        );
4477        f.server.set_clock_ms(at + 1);
4478        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
4479    }
4480
4481    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
4482    /// how much of a cache is volatile was reading a constant.
4483    #[test]
4484    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
4485        let mut f = Fixture::new();
4486        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
4487        assert!(
4488            f.run(&[b"INFO", b"keyspace"])
4489                .contains("db0:keys=3,expires=0"),
4490            "none of them has one yet"
4491        );
4492        f.run(&[b"EXPIRE", b"a", b"1000"]);
4493        f.run(&[b"EXPIRE", b"b", b"1000"]);
4494        let two = f.run(&[b"INFO", b"keyspace"]);
4495        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
4496        f.run(&[b"PERSIST", b"a"]);
4497        f.run(&[b"DEL", b"b"]);
4498        let none = f.run(&[b"INFO", b"keyspace"]);
4499        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
4500
4501        // Each database answers for itself, the way Redis reports it.
4502        f.run(&[b"SELECT", b"1"]);
4503        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
4504        let both = f.run(&[b"INFO", b"keyspace"]);
4505        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
4506        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
4507    }
4508
4509    #[cfg(unix)]
4510    #[test]
4511    fn info_cpu_reports_processor_time_that_was_really_measured() {
4512        let mut f = Fixture::new();
4513        let cpu = f.run(&[b"INFO", b"cpu"]);
4514        assert!(cpu.contains("# CPU"), "{cpu}");
4515        // Redis's unit/info-command asks for this one by name in three tests.
4516        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
4517        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
4518        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
4519        assert!(!cpu.contains("redis_version"), "{cpu}");
4520
4521        // It is a measurement and not a constant, so it goes up when work
4522        // happens. A tight loop rather than a sleep, because sleeping is the
4523        // one thing that does not move this number.
4524        let before = used_cpu_user(&cpu);
4525        let mut n = 0u64;
4526        let mut rounds = 0;
4527        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
4528            for i in 0..1_000_000u64 {
4529                n = n.wrapping_add(i.wrapping_mul(i));
4530            }
4531            rounds += 1;
4532            // A bound rather than a spin, so a platform where this number does
4533            // not move fails here instead of hanging. Even a clock with whole
4534            // millisecond granularity gets there in the first round or two.
4535            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4536        }
4537    }
4538
4539    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
4540    #[cfg(unix)]
4541    fn used_cpu_user(info: &str) -> f64 {
4542        info.lines()
4543            .find_map(|l| l.strip_prefix("used_cpu_user:"))
4544            .expect("no used_cpu_user in the reply")
4545            .trim()
4546            .parse()
4547            .expect("used_cpu_user is not a number")
4548    }
4549
4550    /// The safety net under the rule that a body checks its arguments before
4551    /// it writes anything. `MGET` writes its array header first and then reads
4552    /// each key, so if a later argument could fail the header would already be
4553    /// out. Nothing in the string group does that today and this is what would
4554    /// catch the first one that did.
4555    #[test]
4556    fn a_command_that_fails_leaves_nothing_half_written() {
4557        let mut f = Fixture::new();
4558        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4559        assert_eq!(reply, "-ERR offset is out of range\r\n");
4560        assert!(!reply.contains(':'), "no integer went out in front of it");
4561    }
4562
4563    #[test]
4564    fn quit_answers_first_and_closes_after() {
4565        let mut f = Fixture::new();
4566        let (flow, reply) = f.flow(&[b"QUIT"]);
4567        assert_eq!(reply, "+OK\r\n");
4568        assert_eq!(flow, Flow::Close);
4569    }
4570
4571    /// A server that has not been asked to stop is not stopping, and one that
4572    /// has says so without writing anything back.
4573    ///
4574    /// The empty reply is the point. Redis answers nothing at all here and the
4575    /// client sees the socket close, and an `OK` would be a promise from a
4576    /// process that is about to not exist.
4577    #[test]
4578    fn shutdown_writes_nothing_and_sets_the_flag() {
4579        let mut f = Fixture::new();
4580        assert!(!f.server.stopping(), "nobody has asked yet");
4581
4582        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4583        assert_eq!(reply, "");
4584        assert_eq!(flow, Flow::Close);
4585        assert!(f.server.stopping());
4586    }
4587
4588    /// Every flag combination 8.10.1 takes, and every one it refuses.
4589    ///
4590    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
4591    /// contradict each other, `ABORT` says to do nothing so it cannot be
4592    /// combined with a word about how to do it, and repeating any one of them
4593    /// is fine. All of it was read off a running 8.10.1 rather than worked out
4594    /// from the documentation, which does not say.
4595    #[test]
4596    fn shutdown_takes_the_flags_redis_takes() {
4597        for flags in [
4598            &[b"NOSAVE".as_slice()][..],
4599            &[b"SAVE"],
4600            &[b"NOW"],
4601            &[b"FORCE"],
4602            &[b"nosave"],
4603            &[b"NOW", b"NOW"],
4604            &[b"SAVE", b"SAVE"],
4605            &[b"NOSAVE", b"NOW", b"FORCE"],
4606        ] {
4607            let mut f = Fixture::new();
4608            let mut parts = vec![b"SHUTDOWN".as_slice()];
4609            parts.extend_from_slice(flags);
4610            let (flow, reply) = f.flow(&parts);
4611            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4612            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4613            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4614        }
4615
4616        for flags in [
4617            &[b"BOGUS".as_slice()][..],
4618            &[b"SAVE", b"NOSAVE"],
4619            &[b"NOSAVE", b"SAVE"],
4620            &[b"ABORT", b"NOW"],
4621            &[b"NOSAVE", b"ABORT"],
4622            &[b"NOW", b"FORCE", b"ABORT"],
4623        ] {
4624            let mut f = Fixture::new();
4625            let mut parts = vec![b"SHUTDOWN".as_slice()];
4626            parts.extend_from_slice(flags);
4627            assert_eq!(
4628                f.run(&parts),
4629                "-ERR syntax error\r\n",
4630                "SHUTDOWN {flags:?} was accepted"
4631            );
4632            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4633        }
4634    }
4635
4636    /// `ABORT` has nothing to call off, ever.
4637    ///
4638    /// A shutdown here is decided and done inside one turn of the loop, so
4639    /// there is no window in which one is in progress. That makes Redis's
4640    /// message for a cancel with nothing to cancel the right answer every time
4641    /// rather than only when nothing happens to be pending. Two `ABORT`s is
4642    /// still one `ABORT`, which is what 8.10.1 does.
4643    #[test]
4644    fn shutdown_abort_never_has_anything_to_abort() {
4645        let mut f = Fixture::new();
4646        for parts in [
4647            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4648            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4649        ] {
4650            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4651            assert!(!f.server.stopping(), "an abort stopped the server");
4652        }
4653    }
4654
4655    /// A fixture whose server writes into a directory of its own.
4656    ///
4657    /// Every test here really writes files, because the whole point of the
4658    /// command is the files and a backup that is only a state machine would
4659    /// pass a test suite and fail the first person who tried to restore one.
4660    /// The directory carries the test's name so that the suite can run its
4661    /// tests in parallel the way it always does.
4662    struct Backups {
4663        f: Fixture,
4664        dir: PathBuf,
4665    }
4666
4667    impl Backups {
4668        fn new(name: &str) -> Backups {
4669            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4670            let _ = std::fs::remove_dir_all(&dir);
4671            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4672            let mut f = Fixture::new();
4673            f.server.set_dir(dir.clone());
4674            Backups { f, dir }
4675        }
4676
4677        fn run(&mut self, parts: &[&[u8]]) -> String {
4678            self.f.run(parts)
4679        }
4680
4681        /// The names in `backupdir`, sorted, so a test can say what is on disk.
4682        fn files(&self) -> Vec<String> {
4683            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4684                Ok(entries) => entries
4685                    .filter_map(|e| e.ok())
4686                    .map(|e| e.file_name().to_string_lossy().into_owned())
4687                    .collect(),
4688                Err(_) => Vec::new(),
4689            };
4690            names.sort();
4691            names
4692        }
4693
4694        fn read(&self, name: &str) -> Vec<u8> {
4695            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4696        }
4697    }
4698
4699    impl Drop for Backups {
4700        fn drop(&mut self) {
4701            let _ = std::fs::remove_dir_all(&self.dir);
4702        }
4703    }
4704
4705    /// The four states and the moves between them, in the order a client walks
4706    /// them, with the files checked at every step.
4707    #[test]
4708    fn backup_walks_the_states_the_reference_walks() {
4709        let mut b = Backups::new("states");
4710        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4711
4712        assert!(status(&mut b).contains("idle"));
4713        assert!(b.files().is_empty(), "an idle server has written a backup");
4714
4715        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4716        assert!(status(&mut b).contains("incrementing"));
4717        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4718
4719        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4720        assert!(status(&mut b).contains("sealed"));
4721        assert_eq!(
4722            b.files(),
4723            [
4724                "appendonly.aof.1.base.rdb",
4725                "appendonly.aof.1.incr.aof",
4726                "appendonly.aof.manifest",
4727            ]
4728        );
4729
4730        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4731        assert!(status(&mut b).contains("idle"));
4732        assert!(b.files().is_empty(), "cleanup left something behind");
4733    }
4734
4735    /// Every move that is refused, in the reference's words.
4736    #[test]
4737    fn backup_refuses_the_moves_the_reference_refuses() {
4738        let mut b = Backups::new("refusals");
4739
4740        assert_eq!(
4741            b.run(&[b"BACKUP", b"SEAL"]),
4742            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4743        );
4744        assert_eq!(
4745            b.run(&[b"BACKUP", b"ABORT"]),
4746            "-ERR No backup in progress\r\n"
4747        );
4748        // Cleanup from idle is not an error, it is a way of saying there was
4749        // nothing to clean up.
4750        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4751
4752        b.run(&[b"BACKUP", b"START"]);
4753        assert_eq!(
4754            b.run(&[b"BACKUP", b"START"]),
4755            "-ERR A backup is already in progress, ABORT it first\r\n"
4756        );
4757        assert_eq!(
4758            b.run(&[b"BACKUP", b"CLEANUP"]),
4759            "-ERR Backup is in progress\r\n"
4760        );
4761
4762        b.run(&[b"BACKUP", b"SEAL"]);
4763        assert_eq!(
4764            b.run(&[b"BACKUP", b"START"]),
4765            "-ERR A sealed backup exists, CLEANUP it first\r\n"
4766        );
4767        assert_eq!(
4768            b.run(&[b"BACKUP", b"SEAL"]),
4769            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4770        );
4771        assert_eq!(
4772            b.run(&[b"BACKUP", b"ABORT"]),
4773            "-ERR No backup in progress\r\n"
4774        );
4775    }
4776
4777    /// An abort takes the base file away and leaves a state saying who did it.
4778    ///
4779    /// The next backup takes the next sequence number rather than reusing the
4780    /// one whose files were just thrown away, so a directory somebody copied a
4781    /// half finished backup out of cannot end up with two different files under
4782    /// one name.
4783    #[test]
4784    fn backup_abort_removes_the_file_and_says_who_did_it() {
4785        let mut b = Backups::new("abort");
4786        b.run(&[b"BACKUP", b"START"]);
4787        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4788
4789        let status = b.run(&[b"BACKUP", b"STATUS"]);
4790        assert!(status.contains("failed"), "{status}");
4791        assert!(status.contains("aborted by user"), "{status}");
4792        assert!(b.files().is_empty(), "abort left the base file behind");
4793        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4794
4795        // A start from failed works, and is the second backup.
4796        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4797        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4798        let status = b.run(&[b"BACKUP", b"STATUS"]);
4799        assert!(status.contains("incrementing"), "{status}");
4800        assert!(!status.contains("aborted"), "the old error was kept");
4801    }
4802
4803    /// `LIST` names nothing, then one file, then three, and they are absolute.
4804    #[test]
4805    fn backup_list_names_the_files_that_are_pinned_so_far() {
4806        let mut b = Backups::new("list");
4807        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4808
4809        b.run(&[b"BACKUP", b"START"]);
4810        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4811        let base = base.to_string_lossy().into_owned();
4812        assert_eq!(
4813            b.run(&[b"BACKUP", b"LIST"]),
4814            format!("*1\r\n${}\r\n{base}\r\n", base.len())
4815        );
4816
4817        b.run(&[b"BACKUP", b"SEAL"]);
4818        let listed = b.run(&[b"BACKUP", b"LIST"]);
4819        assert!(listed.starts_with("*3\r\n"), "{listed}");
4820        // The order is the manifest's order, base then incremental then the
4821        // manifest itself, which is the order a restore needs them in.
4822        let names: Vec<&str> = listed
4823            .lines()
4824            .filter(|l| l.starts_with('/') || l.contains(":\\"))
4825            .collect();
4826        assert_eq!(names.len(), 3, "{listed}");
4827        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4828        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4829        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4830    }
4831
4832    /// The base file is the dataset as it was at `START` and not at `SEAL`.
4833    ///
4834    /// That is D-46 and it is the one thing about this a client can notice, so
4835    /// it is pinned here rather than left to be discovered by whoever restores
4836    /// one. The incremental file is empty for the same reason: there is no
4837    /// append only log underneath this server to copy the writes in between out
4838    /// of.
4839    #[test]
4840    fn a_backup_holds_the_dataset_as_it_was_at_start() {
4841        let mut b = Backups::new("contents");
4842        b.run(&[b"SET", b"bk", b"v1"]);
4843        b.run(&[b"BACKUP", b"START"]);
4844        b.run(&[b"SET", b"bk", b"v2"]);
4845        b.run(&[b"BACKUP", b"SEAL"]);
4846
4847        let base = b.read("appendonly.aof.1.base.rdb");
4848        assert!(base.starts_with(b"REDIS"), "not an RDB file");
4849        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4850        assert!(
4851            !base.windows(2).any(|w| w == b"v2"),
4852            "the base file moved on after START"
4853        );
4854        // The aux field a loader acts on, and the one that says this file is
4855        // the base of an append only file rather than a standalone dump. Its
4856        // value is the one byte string 1, which the encoder writes as an
4857        // integer the way a real server writes it.
4858        let at = base
4859            .windows(8)
4860            .position(|w| w == b"aof-base")
4861            .expect("no aof-base aux field");
4862        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4863
4864        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4865        assert_eq!(
4866            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4867            "file appendonly.aof.1.base.rdb seq 1 type b\n\
4868             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4869        );
4870    }
4871
4872    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
4873    /// RESP2, which is what every other map shaped reply in this server does.
4874    #[test]
4875    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4876        let mut b = Backups::new("status");
4877        b.f.server.set_clock_ms(1_700_000_000_000);
4878
4879        assert_eq!(
4880            b.run(&[b"BACKUP", b"STATUS"]),
4881            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4882             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4883        );
4884
4885        b.f.out = Out::new(Proto::Resp3);
4886        b.run(&[b"BACKUP", b"START"]);
4887        assert_eq!(
4888            b.run(&[b"BACKUP", b"STATUS"]),
4889            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4890             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4891        );
4892
4893        b.run(&[b"BACKUP", b"SEAL"]);
4894        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4895        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4896    }
4897
4898    /// A sealed backup that nobody cleans up goes away on its own once
4899    /// `backup-sealed-ttl` seconds have passed since the seal.
4900    #[test]
4901    fn a_sealed_backup_is_swept_away_after_the_timeout() {
4902        let mut b = Backups::new("ttl");
4903        b.f.server.set_clock_ms(1_000_000);
4904        assert_eq!(
4905            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4906            "+OK\r\n"
4907        );
4908        b.run(&[b"BACKUP", b"START"]);
4909        b.run(&[b"BACKUP", b"SEAL"]);
4910
4911        // A minute short of the deadline, nothing happens.
4912        b.f.server.set_clock_ms(1_000_000 + 59_000);
4913        b.f.server.backup_expire();
4914        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4915        assert_eq!(b.files().len(), 3);
4916
4917        b.f.server.set_clock_ms(1_000_000 + 60_000);
4918        b.f.server.backup_expire();
4919        let status = b.run(&[b"BACKUP", b"STATUS"]);
4920        assert!(status.contains("idle"), "{status}");
4921        assert!(b.files().is_empty(), "the timeout left the files behind");
4922
4923        // Zero is the default and means a sealed backup is kept for ever.
4924        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4925        b.run(&[b"BACKUP", b"START"]);
4926        b.run(&[b"BACKUP", b"SEAL"]);
4927        b.f.server.set_clock_ms(9_000_000_000);
4928        b.f.server.backup_expire();
4929        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4930    }
4931
4932    /// The three settings around the command, read and written the way 8.10.1
4933    /// reads and writes them.
4934    #[test]
4935    fn the_backup_settings_behave_the_way_the_reference_does() {
4936        let mut b = Backups::new("config");
4937        let dir = b.dir.to_string_lossy().into_owned();
4938
4939        assert_eq!(
4940            b.run(&[b"CONFIG", b"GET", b"dir"]),
4941            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4942        );
4943        assert_eq!(
4944            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4945            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4946        );
4947        assert_eq!(
4948            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4949            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4950        );
4951
4952        // `dir` is a protected config, so it is refused even for the value it
4953        // already holds, and `backupdirname` is immutable.
4954        assert_eq!(
4955            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4956            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4957        );
4958        assert_eq!(
4959            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4960            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4961        );
4962        assert!(
4963            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4964                .contains("argument couldn't be parsed into an integer")
4965        );
4966        assert!(
4967            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4968                .contains("argument must be between 0 and 9223372036854775807 inclusive")
4969        );
4970    }
4971
4972    /// The help text, which has `HELP` in it twice because the reference's does.
4973    #[test]
4974    fn backup_help_is_the_text_the_reference_sends() {
4975        let mut f = Fixture::new();
4976        let help = f.run(&[b"BACKUP", b"HELP"]);
4977        assert!(help.starts_with("*17\r\n"), "{help}");
4978        assert!(
4979            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4980        );
4981        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
4982        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
4983        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
4984    }
4985
4986    /// What a mistyped `BACKUP` gets told.
4987    ///
4988    /// The arity error names `backup` where the reference names `backup|start`,
4989    /// which is D-46: the table reports one arity for the container the way the
4990    /// reference does, and the per subcommand table that would carry the better
4991    /// name is not built yet. Every subcommand is exactly two words, so nothing
4992    /// legal is refused by it.
4993    #[test]
4994    fn backup_refuses_what_it_cannot_read() {
4995        let mut f = Fixture::new();
4996        assert_eq!(
4997            f.run(&[b"BACKUP"]),
4998            "-ERR wrong number of arguments for 'backup' command\r\n"
4999        );
5000        assert_eq!(
5001            f.run(&[b"BACKUP", b"START", b"x"]),
5002            "-ERR wrong number of arguments for 'backup' command\r\n"
5003        );
5004        assert_eq!(
5005            f.run(&[b"BACKUP", b"NOPE"]),
5006            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
5007        );
5008    }
5009
5010    #[test]
5011    fn the_command_counter_counts_every_command_including_the_bad_ones() {
5012        let mut f = Fixture::new();
5013        f.run(&[b"PING"]);
5014        f.run(&[b"NOPE"]);
5015        f.run(&[b"GET"]);
5016        assert_eq!(f.server.totals().commands, 3);
5017    }
5018
5019    #[test]
5020    fn what_a_thread_marked_is_taken_by_the_maintenance_turn() {
5021        let mut server = Server::new();
5022        server.set_threads(2);
5023        // A fresh server has every database on the turn's list, so start from
5024        // nothing to see the one mark arrive.
5025        server.mine().turn.store(0, Relaxed);
5026        server.locals[1].mark(1 << 9);
5027        server.collect_marks();
5028        assert!(server.mine().wanted(9));
5029        // And taken once rather than left to be taken again next turn.
5030        assert_eq!(server.locals[1].dirty.load(Relaxed), 0);
5031    }
5032
5033    #[test]
5034    fn what_two_threads_counted_is_added_up_when_info_asks() {
5035        let mut server = Server::new();
5036        server.set_threads(2);
5037        // Written into the two sets by hand, because what is under test is the
5038        // adding up and not the claiming, and one test thread can only ever
5039        // claim one set.
5040        let ping = lookup(b"PING").expect("PING is a command");
5041        for (at, calls) in [(0, 2), (1, 3)] {
5042            let counters = &server.locals[at];
5043            for _ in 0..calls {
5044                counters.stats.commands.bump();
5045                counters.cmdstats.at(ping).calls.bump();
5046            }
5047            counters.stats.opened();
5048        }
5049        assert_eq!(server.totals().commands, 5);
5050        assert_eq!(server.totals().clients, 2);
5051        assert_eq!(server.totals().connections, 2);
5052        let rows: Vec<_> = server.command_stats().collect();
5053        assert_eq!(rows.len(), 1);
5054        assert_eq!(rows[0].0, "ping");
5055        assert_eq!(rows[0].1.calls, 5);
5056        // A reset takes the totals and leaves the open connections, which are
5057        // still open.
5058        server.reset_stats();
5059        assert_eq!(server.totals().commands, 0);
5060        assert_eq!(server.totals().connections, 0);
5061        assert_eq!(server.totals().clients, 2);
5062    }
5063
5064    #[test]
5065    fn the_parked_count_says_what_the_waiter_list_says() {
5066        let mut f = Fixture::new();
5067        assert_eq!(f.server.parked(), 0);
5068        for client in 1..=3u64 {
5069            f.session = Session::new(client);
5070            assert_eq!(f.flow(&[b"BLPOP", b"q", b"0"]).0, Flow::Block);
5071        }
5072        assert_eq!(f.server.parked(), 3);
5073        assert_eq!(f.server.waiters().len(), 3);
5074
5075        // The three ways the list gets shorter, each of which has to move the
5076        // number with it, because a number left behind is either a walk of the
5077        // list that never happens or one that runs off the end of it.
5078        f.server.forget_waiters(2);
5079        assert_eq!(f.server.parked(), f.server.waiters().len());
5080        f.server.forget_waiters(1);
5081        assert_eq!(f.server.parked(), f.server.waiters().len());
5082        f.run(&[b"RPUSH", b"q", b"v"]);
5083        let mut out = Out::new(Proto::Resp2);
5084        assert!(f.server.serve_waiter(3, 0, &mut out));
5085        f.server.forget_waiters(3);
5086        assert_eq!(f.server.parked(), 0);
5087        assert!(f.server.waiters().is_empty());
5088    }
5089
5090    #[test]
5091    fn a_set_goes_from_bytes_to_bytes() {
5092        let mut f = Fixture::new();
5093        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
5094        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
5095        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
5096        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
5097        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
5098        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
5099        assert_eq!(
5100            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
5101            "*3\r\n:1\r\n:0\r\n:1\r\n"
5102        );
5103        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
5104        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
5105    }
5106
5107    #[test]
5108    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
5109        let mut f = Fixture::new();
5110        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
5111        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
5112        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
5113        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
5114        assert_eq!(
5115            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
5116            "*2\r\n:0\r\n:0\r\n"
5117        );
5118        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
5119    }
5120
5121    #[test]
5122    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
5123        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
5124        // and one that gets a `*` hands it a list, without either of them being
5125        // told which command was sent.
5126        let mut f = Fixture::new();
5127        f.run(&[b"SADD", b"s", b"one"]);
5128        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
5129
5130        f.run(&[b"HELLO", b"3"]);
5131        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
5132    }
5133
5134    #[test]
5135    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
5136        // An intset holds the number, so these digits exist for the first time
5137        // in the reply buffer.
5138        let mut f = Fixture::new();
5139        f.run(&[b"SADD", b"s", b"42"]);
5140        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
5141        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
5142        assert_eq!(
5143            f.run(&[b"SISMEMBER", b"s", b"042"]),
5144            ":0\r\n",
5145            "the member is the bytes and not the number they parse to"
5146        );
5147    }
5148
5149    #[test]
5150    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
5151        let mut f = Fixture::new();
5152        f.run(&[b"SET", b"str", b"v"]);
5153        f.run(&[b"SADD", b"set", b"a"]);
5154
5155        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5156        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
5157        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
5158        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
5159        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
5160        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
5161        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
5162        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
5163        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
5164
5165        // MGET is the one that does not, because Redis gives nil for the odd
5166        // key out rather than failing the good keys next to it.
5167        assert_eq!(
5168            f.run(&[b"MGET", b"str", b"set", b"nope"]),
5169            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
5170        );
5171        // And plain SET overwrites any type, which takes the body with it.
5172        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
5173        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
5174    }
5175
5176    #[test]
5177    fn a_wrongtype_leaves_nothing_half_written() {
5178        // SMISMEMBER writes an array header and then one reply per member, so
5179        // it is the first command in the server that could get a header out in
5180        // front of an error if it checked its key in the wrong order.
5181        let mut f = Fixture::new();
5182        f.run(&[b"SET", b"k", b"v"]);
5183        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
5184        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
5185        assert!(!reply.contains('*'), "an array header went out in front");
5186    }
5187
5188    #[test]
5189    fn emptying_a_set_takes_the_key_with_it() {
5190        let mut f = Fixture::new();
5191        f.run(&[b"SADD", b"s", b"a", b"b"]);
5192        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
5193        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
5194        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5195        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
5196        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5197    }
5198
5199    /// Pull the cursor and the members out of one `SSCAN` reply.
5200    ///
5201    /// Crude on purpose. A test that walked a set through a real client would
5202    /// be testing the client, and what these tests are about is the shape of
5203    /// the bytes and the fact that a walk sees every member once.
5204    fn split_scan(reply: &str) -> (String, Vec<String>) {
5205        let mut lines = reply.split("\r\n");
5206        assert_eq!(lines.next(), Some("*2"), "got {reply}");
5207        lines.next().expect("the cursor header");
5208        let cursor = lines.next().expect("the cursor").to_owned();
5209        let header = lines.next().expect("the member header");
5210        let n: usize = header[1..].parse().expect("a member count");
5211        let mut members = Vec::with_capacity(n);
5212        for _ in 0..n {
5213            lines.next().expect("a member header");
5214            members.push(lines.next().expect("a member").to_owned());
5215        }
5216        (cursor, members)
5217    }
5218
5219    #[test]
5220    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
5221        let mut f = Fixture::new();
5222        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
5223
5224        let one = f.run(&[b"SPOP", b"s"]);
5225        assert!(
5226            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
5227            "got {one}"
5228        );
5229        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
5230
5231        // A count takes that many, and the last one takes the key with it.
5232        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
5233        assert!(rest.starts_with("*3\r\n"), "got {rest}");
5234        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
5235        // And a pop at a key that is not there is a nil, not an empty bulk.
5236        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
5237        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
5238    }
5239
5240    #[test]
5241    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
5242        // The one place in the server where the reply type carries something
5243        // the command name does not. SPOP's members are distinct so a RESP3
5244        // client can build a set out of them. SRANDMEMBER with a negative count
5245        // can hand back the same member three times, and a set would lose two.
5246        let mut f = Fixture::new();
5247        f.run(&[b"HELLO", b"3"]);
5248        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
5249
5250        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
5251        // And a positive count is an array too, since Redis makes it one.
5252        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
5253
5254        // A negative count against a set of one is where the difference bites:
5255        // the same member three times, which is a three element reply and would
5256        // have been a one element reply if it had gone out as a set.
5257        f.run(&[b"SADD", b"one", b"z"]);
5258        assert_eq!(
5259            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
5260            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
5261        );
5262    }
5263
5264    #[test]
5265    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
5266        let mut f = Fixture::new();
5267        f.run(&[b"SADD", b"s", b"only"]);
5268        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5269        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
5270        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
5271
5272        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
5273        // The count form answers an empty array rather than a nil, which is the
5274        // pair of answers Redis gives and is not the pair it looks like.
5275        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
5276        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
5277        // Asking for more than is there answers all of it once and not padding.
5278        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
5279    }
5280
5281    #[test]
5282    fn a_pop_count_that_is_not_a_positive_number_says_so() {
5283        let mut f = Fixture::new();
5284        f.run(&[b"SADD", b"s", b"a"]);
5285        let bad = "-ERR value is out of range, must be positive\r\n";
5286        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
5287        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
5288        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
5289        // Zero is allowed and is a real answer rather than an error.
5290        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
5291        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
5292    }
5293
5294    #[test]
5295    fn a_scan_walks_a_set_of_any_size_exactly_once() {
5296        let mut f = Fixture::new();
5297        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
5298        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
5299            .into_iter()
5300            .chain(members.iter().map(Vec::as_slice))
5301            .collect();
5302        f.run(&args);
5303
5304        let mut seen = Vec::new();
5305        let mut cursor = "0".to_owned();
5306        loop {
5307            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
5308            let (next, got) = split_scan(&reply);
5309            seen.extend(got);
5310            cursor = next;
5311            if cursor == "0" {
5312                break;
5313            }
5314        }
5315        seen.sort();
5316        seen.dedup();
5317        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
5318
5319        // A set small enough to be a listpack answers in one call whatever
5320        // cursor it was handed, which is what Redis does for that encoding.
5321        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
5322        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
5323        assert_eq!(cursor, "0");
5324        assert_eq!(got.len(), 3);
5325        // And a key that is not there is a finished scan of nothing.
5326        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
5327    }
5328
5329    #[test]
5330    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
5331        let mut f = Fixture::new();
5332        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
5333
5334        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
5335        let mut got = got;
5336        got.sort();
5337        assert_eq!(got, ["aa", "ab"]);
5338
5339        // An integer member has no digits stored anywhere, so MATCH is the one
5340        // place a scan pays to write some.
5341        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
5342        let mut got = got;
5343        got.sort();
5344        assert_eq!(got, ["12", "13"]);
5345
5346        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
5347        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
5348        assert_eq!(
5349            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
5350            "-ERR syntax error\r\n"
5351        );
5352        // A count under one is a syntax error and not a range error, which is
5353        // the odder of Redis's two answers and the reason it is copied exactly.
5354        assert_eq!(
5355            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
5356            "-ERR syntax error\r\n"
5357        );
5358    }
5359
5360    #[test]
5361    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
5362        let mut f = Fixture::new();
5363        f.run(&[b"SADD", b"src", b"a", b"b"]);
5364        f.run(&[b"SADD", b"dst", b"c"]);
5365
5366        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
5367        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
5368        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
5369        // A member that is not in the source is a zero and moves nothing.
5370        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
5371        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
5372
5373        // A destination that does not exist gets made, and a source that runs
5374        // out goes away.
5375        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
5376        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
5377        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
5378    }
5379
5380    #[test]
5381    fn moving_checks_the_types_in_the_order_redis_checks_them() {
5382        // Not the order it looks like it should be. A source that is not there
5383        // answers zero without ever looking at the destination, so this is a
5384        // zero and not a WRONGTYPE even though the destination is a string.
5385        let mut f = Fixture::new();
5386        f.run(&[b"SET", b"str", b"v"]);
5387        f.run(&[b"SADD", b"set", b"a"]);
5388
5389        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5390        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
5391        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
5392        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
5393        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
5394        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
5395        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
5396        assert_eq!(
5397            f.run(&[b"SISMEMBER", b"set", b"a"]),
5398            ":1\r\n",
5399            "and none of that moved anything"
5400        );
5401    }
5402
5403    #[test]
5404    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5405        // SSCAN writes an outer array header before it walks, so it is the
5406        // command most likely to get bytes out in front of an error.
5407        let mut f = Fixture::new();
5408        f.run(&[b"SADD", b"s", b"a"]);
5409        for bad in [
5410            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
5411            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
5412            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
5413        ] {
5414            let reply = f.run(bad);
5415            assert!(reply.starts_with("-ERR"), "got {reply}");
5416            assert!(!reply.contains('*'), "an array header went out in front");
5417        }
5418    }
5419
5420    #[test]
5421    fn a_hash_writes_reads_and_deletes_its_fields() {
5422        let mut f = Fixture::new();
5423        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
5424        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
5425        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5426        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
5427        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
5428        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
5429        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
5430        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
5431        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
5432        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
5433
5434        // The value the client sent is `9`, so HGET h b must not find the `2`
5435        // that is a value. A search with a step of one would have.
5436        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
5437
5438        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
5439        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
5440        assert_eq!(
5441            f.run(&[b"EXISTS", b"h"]),
5442            ":0\r\n",
5443            "and losing the last field lost the key"
5444        );
5445    }
5446
5447    #[test]
5448    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
5449        let mut f = Fixture::new();
5450        f.run(&[b"HSET", b"h", b"a", b"1"]);
5451        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5452        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
5453        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
5454        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
5455        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
5456
5457        f.run(&[b"HELLO", b"3"]);
5458        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
5459        assert_eq!(
5460            f.run(&[b"HGETALL", b"nokey"]),
5461            "%0\r\n",
5462            "a missing key is the empty hash and never a nil"
5463        );
5464        assert_eq!(
5465            f.run(&[b"HKEYS", b"h"]),
5466            "*1\r\n$1\r\na\r\n",
5467            "and the two that answer one side stay arrays"
5468        );
5469    }
5470
5471    #[test]
5472    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
5473        let mut f = Fixture::new();
5474        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
5475        assert_eq!(
5476            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
5477            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
5478            "the reply is positional, so b is a nil and not a gap"
5479        );
5480        assert_eq!(
5481            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
5482            "*2\r\n$-1\r\n$-1\r\n",
5483            "and a missing key is all nils rather than an empty array"
5484        );
5485
5486        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
5487        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
5488        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5489    }
5490
5491    #[test]
5492    fn a_hash_counts_up_and_says_so_when_it_cannot() {
5493        let mut f = Fixture::new();
5494        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
5495        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
5496        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
5497        assert_eq!(
5498            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
5499            "$4\r\n10.5\r\n",
5500            "a bulk string and not a double, on both protocols"
5501        );
5502
5503        f.run(&[b"HSET", b"h", b"s", b"words"]);
5504        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
5505        assert!(
5506            bad.starts_with("-ERR hash value is not an integer"),
5507            "{bad}"
5508        );
5509        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
5510        assert!(
5511            bad.starts_with("-ERR value is not an integer"),
5512            "a bad argument is not yet a hash value, {bad}"
5513        );
5514        assert_eq!(
5515            f.run(&[b"HGET", b"h", b"s"]),
5516            "$5\r\nwords\r\n",
5517            "and neither of them wrote anything"
5518        );
5519    }
5520
5521    #[test]
5522    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
5523        let mut f = Fixture::new();
5524        for i in 0..500 {
5525            let field = format!("field-{i}");
5526            let value = format!("value-{i}");
5527            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
5528        }
5529
5530        let mut seen: Vec<String> = Vec::new();
5531        let mut cursor = "0".to_owned();
5532        loop {
5533            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
5534            let (next, items) = scan_reply(&reply);
5535            assert_eq!(items.len() % 2, 0, "a pair went out half written");
5536            for pair in items.chunks(2) {
5537                assert_eq!(
5538                    pair[0].strip_prefix("field-"),
5539                    pair[1].strip_prefix("value-"),
5540                    "a field came back with someone else's value"
5541                );
5542                seen.push(pair[0].clone());
5543            }
5544            cursor = next;
5545            if cursor == "0" {
5546                break;
5547            }
5548        }
5549        seen.sort();
5550        seen.dedup();
5551        assert_eq!(seen.len(), 500, "every field once and only once");
5552
5553        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
5554        assert!(
5555            items.iter().all(|s| s.starts_with("field-")),
5556            "NOVALUES still sent the values"
5557        );
5558
5559        let (_, one) = scan_reply(&f.run(&[
5560            b"HSCAN",
5561            b"h",
5562            b"0",
5563            b"MATCH",
5564            b"field-499",
5565            b"COUNT",
5566            b"1000",
5567        ]));
5568        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
5569    }
5570
5571    #[test]
5572    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
5573        let mut f = Fixture::new();
5574        f.run(&[b"HSET", b"h", b"a", b"1"]);
5575        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
5576        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
5577        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
5578        assert_eq!(
5579            f.run(&[b"HRANDFIELD", b"h", b"3"]),
5580            "*1\r\n$1\r\na\r\n",
5581            "a positive count is capped at the size of the hash"
5582        );
5583        assert_eq!(
5584            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
5585            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
5586            "and a negative one repeats itself"
5587        );
5588        assert_eq!(
5589            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5590            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5591            "flat on RESP2"
5592        );
5593
5594        f.run(&[b"HELLO", b"3"]);
5595        assert_eq!(
5596            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
5597            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
5598            "and nested on RESP3, but still an array and never a map"
5599        );
5600    }
5601
5602    #[test]
5603    fn every_hash_command_says_wrongtype_and_writes_nothing() {
5604        let mut f = Fixture::new();
5605        f.run(&[b"SET", b"str", b"v"]);
5606        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5607
5608        for cmd in [
5609            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5610            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5611            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5612            &[b"HGET".as_slice(), b"str", b"f"][..],
5613            &[b"HMGET".as_slice(), b"str", b"f"][..],
5614            &[b"HDEL".as_slice(), b"str", b"f"][..],
5615            &[b"HLEN".as_slice(), b"str"][..],
5616            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5617            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5618            &[b"HGETALL".as_slice(), b"str"][..],
5619            &[b"HKEYS".as_slice(), b"str"][..],
5620            &[b"HVALS".as_slice(), b"str"][..],
5621            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5622            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5623            &[b"HRANDFIELD".as_slice(), b"str"][..],
5624            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5625            &[b"HSCAN".as_slice(), b"str", b"0"][..],
5626        ] {
5627            let reply = f.run(cmd);
5628            assert_eq!(reply, wrong, "{:?}", cmd[0]);
5629        }
5630        assert_eq!(
5631            f.run(&[b"GET", b"str"]),
5632            "$1\r\nv\r\n",
5633            "and none of them touched the value"
5634        );
5635    }
5636
5637    #[test]
5638    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5639        let mut f = Fixture::new();
5640        f.run(&[b"HSET", b"h", b"f", b"v"]);
5641        for bad in [
5642            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5643            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5644            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5645            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5646        ] {
5647            let reply = f.run(bad);
5648            assert!(reply.starts_with("-ERR"), "got {reply}");
5649            assert!(!reply.contains('*'), "an array header went out in front");
5650        }
5651    }
5652
5653    #[test]
5654    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5655        let mut f = Fixture::new();
5656        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5657        assert_eq!(
5658            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5659            "*1\r\n:1\r\n"
5660        );
5661        assert_eq!(
5662            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5663            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5664            "one answer per field, and the two sentinels are TTL's own"
5665        );
5666
5667        // The same deadline in the other three units, all of them derived from
5668        // the one number the store kept.
5669        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5670        assert!((99_000..=100_000).contains(&ms), "got {ms}");
5671        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5672        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5673        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5674        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5675
5676        assert_eq!(
5677            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5678            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5679            "one for the deadline taken off, and it does not say what it was"
5680        );
5681        assert_eq!(
5682            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5683            "*1\r\n:-1\r\n"
5684        );
5685        assert_eq!(
5686            f.run(&[b"HGET", b"h", b"a"]),
5687            "$1\r\n1\r\n",
5688            "and the field is still there with the value it had"
5689        );
5690    }
5691
5692    #[test]
5693    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5694        let mut f = Fixture::new();
5695        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5696        assert_eq!(
5697            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5698            "*1\r\n:2\r\n",
5699            "two, and not one, because nothing was stored"
5700        );
5701        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5702        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5703
5704        assert_eq!(
5705            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5706            "*1\r\n:2\r\n"
5707        );
5708        assert_eq!(
5709            f.run(&[b"EXISTS", b"h"]),
5710            ":0\r\n",
5711            "and the last field going took the key with it"
5712        );
5713
5714        // Zero is a delete and not an error, where minus one is an error. That
5715        // is Redis's split and it is easy to get backwards.
5716        f.run(&[b"HSET", b"h", b"a", b"1"]);
5717        assert_eq!(
5718            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5719            "*1\r\n:2\r\n"
5720        );
5721    }
5722
5723    #[test]
5724    fn a_field_is_gone_once_its_moment_passes() {
5725        let mut f = Fixture::new();
5726        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5727        assert_eq!(
5728            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5729            "*1\r\n:1\r\n"
5730        );
5731        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5732
5733        // Time moves once per turn of the event loop and nowhere else, so a
5734        // test moves it by hand rather than by sleeping. There is nothing to
5735        // sleep for: the deadline is a number and so is the clock.
5736        f.server.advance_clock_ms(60);
5737        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5738        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5739        assert_eq!(
5740            f.run(&[b"HGETALL", b"h"]),
5741            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5742            "and the walks do not hand back a field that has expired"
5743        );
5744    }
5745
5746    #[test]
5747    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5748        let mut f = Fixture::new();
5749        for cmd in [
5750            &[
5751                b"HEXPIRE".as_slice(),
5752                b"nokey",
5753                b"100",
5754                b"FIELDS",
5755                b"2",
5756                b"a",
5757                b"b",
5758            ][..],
5759            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5760            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5761            &[
5762                b"HEXPIRETIME".as_slice(),
5763                b"nokey",
5764                b"FIELDS",
5765                b"2",
5766                b"a",
5767                b"b",
5768            ][..],
5769            &[
5770                b"HPERSIST".as_slice(),
5771                b"nokey",
5772                b"FIELDS",
5773                b"2",
5774                b"a",
5775                b"b",
5776            ][..],
5777        ] {
5778            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5779        }
5780    }
5781
5782    #[test]
5783    fn writing_a_field_clears_the_deadline_that_was_on_it() {
5784        let mut f = Fixture::new();
5785        f.run(&[b"HSET", b"h", b"a", b"1"]);
5786        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5787        f.run(&[b"HSET", b"h", b"a", b"2"]);
5788        assert_eq!(
5789            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5790            "*1\r\n:-1\r\n",
5791            "Redis has done this since 7.4, and it is why HGETEX exists"
5792        );
5793    }
5794
5795    #[test]
5796    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5797        let mut f = Fixture::new();
5798        f.run(&[b"HSET", b"h", b"a", b"1"]);
5799        assert_eq!(
5800            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5801            "*1\r\n:0\r\n",
5802            "XX on a field with no deadline changes nothing"
5803        );
5804        assert_eq!(
5805            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5806            "*1\r\n:1\r\n"
5807        );
5808        assert_eq!(
5809            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5810            "*1\r\n:0\r\n",
5811            "and NX will not move one that is already there"
5812        );
5813        assert_eq!(
5814            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5815            "*1\r\n:0\r\n"
5816        );
5817        assert_eq!(
5818            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5819            "*1\r\n:1\r\n"
5820        );
5821        assert_eq!(
5822            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5823            "*1\r\n:1\r\n"
5824        );
5825        assert_eq!(
5826            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5827            "*1\r\n:50\r\n"
5828        );
5829    }
5830
5831    #[test]
5832    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5833        let mut f = Fixture::new();
5834        f.run(&[b"HSET", b"h", b"a", b"1"]);
5835        for (bad, want) in [
5836            (
5837                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5838                "-ERR invalid expire time, must be >= 0",
5839            ),
5840            (
5841                &[
5842                    b"HEXPIRE".as_slice(),
5843                    b"h",
5844                    b"9999999999999999",
5845                    b"FIELDS",
5846                    b"1",
5847                    b"a",
5848                ][..],
5849                "-ERR invalid expire time in 'hexpire' command",
5850            ),
5851            (
5852                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5853                "-ERR wrong number of arguments for 'hexpire' command",
5854            ),
5855            (
5856                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5857                "-ERR Parameter `numFields` should be greater than 0",
5858            ),
5859            (
5860                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5861                "-ERR wrong number of arguments",
5862            ),
5863            (
5864                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5865                "-ERR wrong number of arguments",
5866            ),
5867        ] {
5868            let reply = f.run(bad);
5869            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5870            assert!(!reply.contains('*'), "an array header went out in front");
5871        }
5872        assert_eq!(
5873            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5874            "*1\r\n:-1\r\n",
5875            "and not one of them put a deadline on anything"
5876        );
5877    }
5878
5879    #[test]
5880    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5881        let mut f = Fixture::new();
5882        f.run(&[b"SET", b"str", b"v"]);
5883        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5884
5885        for cmd in [
5886            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5887            &[
5888                b"HPEXPIRE".as_slice(),
5889                b"str",
5890                b"100",
5891                b"FIELDS",
5892                b"1",
5893                b"f",
5894            ][..],
5895            &[
5896                b"HEXPIREAT".as_slice(),
5897                b"str",
5898                b"9999999999",
5899                b"FIELDS",
5900                b"1",
5901                b"f",
5902            ][..],
5903            &[
5904                b"HPEXPIREAT".as_slice(),
5905                b"str",
5906                b"9999999999999",
5907                b"FIELDS",
5908                b"1",
5909                b"f",
5910            ][..],
5911            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5912            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5913            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5914            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5915            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5916        ] {
5917            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5918        }
5919        assert_eq!(
5920            f.run(&[b"GET", b"str"]),
5921            "$1\r\nv\r\n",
5922            "and none of them touched the value"
5923        );
5924    }
5925
5926    #[test]
5927    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5928        let mut f = Fixture::new();
5929        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5930        assert_eq!(
5931            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5932            "*2\r\n$1\r\n1\r\n$-1\r\n",
5933            "positional, so the field that was not there is a nil in its place"
5934        );
5935        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5936        assert_eq!(
5937            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5938            "*1\r\n$-1\r\n"
5939        );
5940        assert_eq!(
5941            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5942            "*1\r\n$1\r\n2\r\n"
5943        );
5944        assert_eq!(
5945            f.run(&[b"EXISTS", b"h"]),
5946            ":0\r\n",
5947            "and the last field took the key"
5948        );
5949    }
5950
5951    #[test]
5952    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5953        let mut f = Fixture::new();
5954        f.run(&[b"HSET", b"h", b"a", b"1"]);
5955        assert_eq!(
5956            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5957            "*1\r\n$1\r\n1\r\n"
5958        );
5959        assert_eq!(
5960            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5961            "*1\r\n:-1\r\n",
5962            "no option means leave it alone, which is the one place this is not GETEX"
5963        );
5964
5965        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5966        assert_eq!(
5967            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5968            "*1\r\n:100\r\n"
5969        );
5970        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5971        assert_eq!(
5972            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5973            "*1\r\n:100\r\n",
5974            "and a plain read really does leave it alone"
5975        );
5976        assert_eq!(
5977            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5978            "*1\r\n$1\r\n1\r\n"
5979        );
5980        assert_eq!(
5981            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5982            "*1\r\n:-1\r\n"
5983        );
5984
5985        assert_eq!(
5986            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5987            "*1\r\n$1\r\n1\r\n",
5988            "the value goes out before the deadline that has already gone is applied"
5989        );
5990        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5991        assert_eq!(
5992            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5993            "*1\r\n$-1\r\n"
5994        );
5995    }
5996
5997    #[test]
5998    fn hsetex_writes_all_of_it_or_none_of_it() {
5999        let mut f = Fixture::new();
6000        assert_eq!(
6001            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
6002            ":1\r\n"
6003        );
6004        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
6005        assert_eq!(
6006            f.run(&[
6007                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
6008            ]),
6009            ":0\r\n",
6010            "FNX wants every field named to be missing"
6011        );
6012        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
6013        assert_eq!(
6014            f.run(&[b"HEXISTS", b"h", b"new"]),
6015            ":0\r\n",
6016            "and none of the list was written"
6017        );
6018        assert_eq!(
6019            f.run(&[
6020                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
6021            ]),
6022            ":0\r\n",
6023            "and FXX wants every one of them to be there"
6024        );
6025        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
6026        assert_eq!(
6027            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
6028            ":1\r\n"
6029        );
6030        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
6031
6032        assert_eq!(
6033            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
6034            ":0\r\n"
6035        );
6036        assert_eq!(
6037            f.run(&[b"EXISTS", b"gone"]),
6038            ":0\r\n",
6039            "a key with no fields cannot meet FXX and is not created trying"
6040        );
6041    }
6042
6043    #[test]
6044    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
6045        let mut f = Fixture::new();
6046        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
6047        assert_eq!(
6048            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6049            "*1\r\n:100\r\n"
6050        );
6051
6052        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
6053        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
6054        assert_eq!(
6055            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6056            "*1\r\n:100\r\n",
6057            "KEEPTTL put back what the write cleared"
6058        );
6059
6060        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
6061        assert_eq!(
6062            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6063            "*1\r\n:-1\r\n",
6064            "and without it a write clears the deadline the way HSET does"
6065        );
6066
6067        // Any order, because Redis reads these in a loop and not in a fixed
6068        // sequence.
6069        assert_eq!(
6070            f.run(&[
6071                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
6072            ]),
6073            ":1\r\n"
6074        );
6075        assert_eq!(
6076            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6077            "*1\r\n:100\r\n"
6078        );
6079
6080        assert_eq!(
6081            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
6082            ":1\r\n",
6083            "written, and not the separate code the HEXPIRE family has for this"
6084        );
6085        assert_eq!(
6086            f.run(&[b"EXISTS", b"h"]),
6087            ":0\r\n",
6088            "and storing it and then removing it emptied the hash"
6089        );
6090    }
6091
6092    #[test]
6093    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
6094        let mut f = Fixture::new();
6095        f.run(&[b"HSET", b"h", b"a", b"1"]);
6096        for (bad, want) in [
6097            // HGETDEL has three sentences of its own for these three mistakes.
6098            (
6099                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
6100                "-ERR Number of fields must be a positive integer",
6101            ),
6102            (
6103                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
6104                "-ERR The `numfields` parameter must match the number of arguments",
6105            ),
6106            (
6107                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
6108                "-ERR Mandatory argument FIELDS is missing or not at the right position",
6109            ),
6110            // And HGETEX and HSETEX have three different ones between them.
6111            (
6112                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
6113                "-ERR invalid number of fields",
6114            ),
6115            (
6116                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
6117                "-ERR wrong number of arguments",
6118            ),
6119            (
6120                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
6121                "-ERR unknown argument: FIELD",
6122            ),
6123            (
6124                &[
6125                    b"HGETEX".as_slice(),
6126                    b"h",
6127                    b"KEEPTTL",
6128                    b"FIELDS",
6129                    b"1",
6130                    b"a",
6131                ][..],
6132                "-ERR unknown argument: KEEPTTL",
6133            ),
6134            (
6135                &[
6136                    b"HGETEX".as_slice(),
6137                    b"h",
6138                    b"EX",
6139                    b"100",
6140                    b"PERSIST",
6141                    b"FIELDS",
6142                    b"1",
6143                    b"a",
6144                ][..],
6145                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
6146            ),
6147            (
6148                &[
6149                    b"HSETEX".as_slice(),
6150                    b"h",
6151                    b"EX",
6152                    b"1",
6153                    b"KEEPTTL",
6154                    b"FIELDS",
6155                    b"1",
6156                    b"a",
6157                    b"1",
6158                ][..],
6159                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
6160            ),
6161            (
6162                &[
6163                    b"HSETEX".as_slice(),
6164                    b"h",
6165                    b"FNX",
6166                    b"FXX",
6167                    b"FIELDS",
6168                    b"1",
6169                    b"a",
6170                    b"1",
6171                ][..],
6172                "-ERR Only one of FXX or FNX arguments can be specified",
6173            ),
6174            (
6175                &[
6176                    b"HSETEX".as_slice(),
6177                    b"h",
6178                    b"FIELDS",
6179                    b"2",
6180                    b"a",
6181                    b"1",
6182                    b"b",
6183                ][..],
6184                "-ERR wrong number of arguments",
6185            ),
6186            (
6187                &[
6188                    b"HGETEX".as_slice(),
6189                    b"h",
6190                    b"EX",
6191                    b"-1",
6192                    b"FIELDS",
6193                    b"1",
6194                    b"a",
6195                ][..],
6196                "-ERR invalid expire time, must be >= 0",
6197            ),
6198            (
6199                &[
6200                    b"HGETEX".as_slice(),
6201                    b"h",
6202                    b"PXAT",
6203                    b"99999999999999",
6204                    b"FIELDS",
6205                    b"1",
6206                    b"a",
6207                ][..],
6208                "-ERR invalid expire time in 'hgetex' command",
6209            ),
6210            (
6211                &[
6212                    b"HSETEX".as_slice(),
6213                    b"h",
6214                    b"EX",
6215                    b"abc",
6216                    b"FIELDS",
6217                    b"1",
6218                    b"a",
6219                    b"1",
6220                ][..],
6221                "-ERR value is not an integer or out of range",
6222            ),
6223        ] {
6224            let reply = f.run(bad);
6225            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
6226            assert!(!reply.contains('*'), "an array header went out in front");
6227        }
6228        assert_eq!(
6229            f.run(&[b"HGET", b"h", b"a"]),
6230            "$1\r\n1\r\n",
6231            "and not one of them wrote anything"
6232        );
6233        assert_eq!(
6234            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
6235            "*1\r\n:-1\r\n"
6236        );
6237    }
6238
6239    #[test]
6240    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
6241        let mut f = Fixture::new();
6242        f.run(&[b"SET", b"str", b"v"]);
6243        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6244        for cmd in [
6245            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6246            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
6247            &[
6248                b"HGETEX".as_slice(),
6249                b"str",
6250                b"EX",
6251                b"100",
6252                b"FIELDS",
6253                b"1",
6254                b"f",
6255            ][..],
6256            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
6257        ] {
6258            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
6259        }
6260        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
6261    }
6262
6263    /// The two orders `HIMPORT` juggles, which are not the same order.
6264    ///
6265    /// Values arrive in the order the fields were declared in and the hash is
6266    /// built in sorted order, so the first value is not generally the first
6267    /// field. And the sort is by length before bytes, which nothing else here
6268    /// sorts names with: `b` comes before `aa` where a plain byte comparison
6269    /// would put `aa` first. Both read off 8.10.1.
6270    #[test]
6271    fn himport_writes_declared_values_into_sorted_fields() {
6272        let mut f = Fixture::new();
6273        assert_eq!(
6274            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
6275            "+OK\r\n"
6276        );
6277        assert_eq!(
6278            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
6279            "+OK\r\n"
6280        );
6281        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
6282        assert_eq!(
6283            f.run(&[b"HGETALL", b"k"]),
6284            bulks(&["a", "3", "b", "1", "aa", "2"])
6285        );
6286    }
6287
6288    /// It replaces the key rather than writing over it, so a field the fieldset
6289    /// does not name is gone afterwards and so is the deadline.
6290    #[test]
6291    fn himport_set_replaces_the_whole_key() {
6292        let mut f = Fixture::new();
6293        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
6294        f.run(&[b"EXPIRE", b"k", b"100"]);
6295        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6296        assert_eq!(
6297            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6298            "+OK\r\n"
6299        );
6300        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6301        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
6302    }
6303
6304    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
6305    /// throws them away, and a key built from one outlives it.
6306    #[test]
6307    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
6308        let mut f = Fixture::new();
6309        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
6310        f.run(&[b"SELECT", b"1"]);
6311        assert_eq!(
6312            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6313            "+OK\r\n"
6314        );
6315        f.run(&[b"SELECT", b"0"]);
6316        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
6317        assert_eq!(
6318            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
6319            "-ERR no such fieldset\r\n"
6320        );
6321    }
6322
6323    /// Which complaint wins when a line is wrong in more than one place.
6324    ///
6325    /// The type of the key beats both of the others, so a `HIMPORT SET` against
6326    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
6327    /// the ordering a real server has and not the one the argument order
6328    /// suggests.
6329    #[test]
6330    fn himport_complains_in_the_order_a_real_server_does() {
6331        let mut f = Fixture::new();
6332        f.run(&[b"SET", b"str", b"v"]);
6333        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6334        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6335        assert_eq!(
6336            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
6337            wrong,
6338            "the type beats a missing fieldset"
6339        );
6340        assert_eq!(
6341            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
6342            wrong,
6343            "and it beats a value count that does not fit"
6344        );
6345        assert_eq!(
6346            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
6347            "-ERR no such fieldset\r\n"
6348        );
6349        // One sentence for too few and for too many alike.
6350        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
6351            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
6352            line.extend_from_slice(values);
6353            assert_eq!(
6354                f.run(&line),
6355                "-ERR value count does not match fieldset field count\r\n",
6356                "{} values into two fields",
6357                values.len()
6358            );
6359        }
6360        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6361    }
6362
6363    /// The arity of each subcommand, and the unknown one.
6364    #[test]
6365    fn himport_checks_each_subcommand_count_under_its_own_name() {
6366        let mut f = Fixture::new();
6367        assert_eq!(
6368            f.run(&[b"HIMPORT"]),
6369            "-ERR wrong number of arguments for 'himport' command\r\n"
6370        );
6371        for (rest, name) in [
6372            (&["PREPARE"][..], "prepare"),
6373            (&["PREPARE", "fs"][..], "prepare"),
6374            (&["SET"][..], "set"),
6375            (&["SET", "k"][..], "set"),
6376            (&["SET", "k", "fs"][..], "set"),
6377            (&["DISCARD"][..], "discard"),
6378            (&["DISCARD", "a", "b"][..], "discard"),
6379            (&["DISCARDALL", "x"][..], "discardall"),
6380        ] {
6381            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
6382            line.extend(rest.iter().map(|a| a.as_bytes()));
6383            assert_eq!(
6384                f.run(&line),
6385                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
6386                "HIMPORT {}",
6387                rest.join(" ")
6388            );
6389        }
6390        assert_eq!(
6391            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
6392            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
6393        );
6394    }
6395
6396    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
6397    /// is the answer of the two that could not be guessed from outside.
6398    #[test]
6399    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
6400        let mut f = Fixture::new();
6401        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6402        assert_eq!(
6403            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
6404            "-ERR duplicate field name in fieldset\r\n"
6405        );
6406        assert_eq!(
6407            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
6408            "+OK\r\n"
6409        );
6410        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
6411    }
6412
6413    /// Preparing the same name twice replaces it, and the two discards count
6414    /// what they took rather than answering OK.
6415    #[test]
6416    fn himport_prepare_replaces_and_the_discards_count() {
6417        let mut f = Fixture::new();
6418        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
6419        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
6420        assert_eq!(
6421            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
6422            "+OK\r\n"
6423        );
6424        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
6425
6426        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
6427        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
6428        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
6429        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
6430        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
6431        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
6432    }
6433
6434    /// The one integer of a single element array reply.
6435    /// The number out of a plain integer reply.
6436    ///
6437    /// [`int_reply`] is the same thing wrapped in a one element array, which is
6438    /// the shape every hash field command answers in.
6439    fn int(reply: &str) -> i64 {
6440        let body = reply
6441            .strip_prefix(':')
6442            .and_then(|s| s.strip_suffix("\r\n"))
6443            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
6444        body.parse().expect("an integer")
6445    }
6446
6447    fn int_reply(reply: &str) -> i64 {
6448        let body = reply
6449            .strip_prefix("*1\r\n:")
6450            .and_then(|s| s.strip_suffix("\r\n"))
6451            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
6452        body.parse().expect("an integer")
6453    }
6454
6455    /// The cursor and the flat items of a scan reply.
6456    fn scan_reply(reply: &str) -> (String, Vec<String>) {
6457        let mut lines = reply.split("\r\n");
6458        assert_eq!(lines.next(), Some("*2"), "got {reply}");
6459        lines.next().expect("the cursor header");
6460        let cursor = lines.next().expect("a cursor").to_owned();
6461        let header = lines.next().expect("an item count");
6462        let n: usize = header[1..].parse().expect("a count");
6463        let mut items = Vec::with_capacity(n);
6464        for _ in 0..n {
6465            lines.next().expect("an item header");
6466            items.push(lines.next().expect("an item").to_owned());
6467        }
6468        (cursor, items)
6469    }
6470
6471    /// The members of a set reply, sorted, since none of these promise an
6472    /// order and a test that asserted one would be asserting an accident.
6473    fn sorted(reply: &str) -> Vec<String> {
6474        let mut lines = reply.split("\r\n");
6475        let header = lines.next().expect("a header");
6476        assert!(
6477            header.starts_with('*') || header.starts_with('~'),
6478            "got {reply}"
6479        );
6480        let n: usize = header[1..].parse().expect("a member count");
6481        let mut got = Vec::with_capacity(n);
6482        for _ in 0..n {
6483            lines.next().expect("a member header");
6484            got.push(lines.next().expect("a member").to_owned());
6485        }
6486        got.sort();
6487        got
6488    }
6489
6490    #[test]
6491    fn the_algebra_answers_what_the_sets_share_and_do_not() {
6492        let mut f = Fixture::new();
6493        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6494        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6495        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
6496
6497        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
6498        assert_eq!(
6499            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
6500            ["1", "2", "3", "4", "5"]
6501        );
6502        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
6503        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
6504
6505        // A key that is not there is an empty set, which empties an
6506        // intersection and does nothing at all to a union.
6507        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
6508        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
6509        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
6510        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
6511    }
6512
6513    #[test]
6514    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
6515        let mut f = Fixture::new();
6516        f.run(&[b"SADD", b"a", b"x"]);
6517        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
6518        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
6519        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
6520
6521        f.run(&[b"HELLO", b"3"]);
6522        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
6523        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
6524        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
6525        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
6526    }
6527
6528    #[test]
6529    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
6530        let mut f = Fixture::new();
6531        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
6532        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
6533
6534        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
6535        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
6536        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
6537        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
6538        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
6539        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
6540
6541        // An empty answer deletes the destination rather than leaving an empty
6542        // set behind, and the destination may be one of the sources.
6543        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
6544        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6545        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
6546        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
6547
6548        // And a destination holding something else is overwritten, the same way
6549        // SET overwrites, rather than refused.
6550        f.run(&[b"SET", b"str", b"v"]);
6551        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
6552        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
6553    }
6554
6555    #[test]
6556    fn sintercard_counts_without_building_and_stops_at_a_limit() {
6557        let mut f = Fixture::new();
6558        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6559        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
6560
6561        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
6562        assert_eq!(
6563            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6564            ":2\r\n"
6565        );
6566        assert_eq!(
6567            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6568            ":3\r\n",
6569            "a limit of zero is no limit"
6570        );
6571        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
6572        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
6573
6574        // The counted keys are what make its three error messages its own.
6575        assert_eq!(
6576            f.run(&[b"SINTERCARD", b"0", b"a"]),
6577            "-ERR numkeys should be greater than 0\r\n"
6578        );
6579        assert_eq!(
6580            f.run(&[b"SINTERCARD", b"abc", b"a"]),
6581            "-ERR numkeys should be greater than 0\r\n"
6582        );
6583        assert_eq!(
6584            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
6585            "-ERR Number of keys can't be greater than number of args\r\n"
6586        );
6587        assert_eq!(
6588            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
6589            "-ERR LIMIT can't be negative\r\n"
6590        );
6591        assert_eq!(
6592            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
6593            "-ERR syntax error\r\n"
6594        );
6595        // A key really can be called LIMIT, which is why the count exists.
6596        f.run(&[b"SADD", b"LIMIT", b"2"]);
6597        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
6598    }
6599
6600    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
6601    /// over a difference. Every number here was read off 8.10.1 first.
6602    #[test]
6603    fn sunioncard_and_sdiffcard_count_without_building() {
6604        let mut f = Fixture::new();
6605        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6606        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6607
6608        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6609        assert_eq!(
6610            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6611            ":2\r\n"
6612        );
6613        assert_eq!(
6614            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6615            ":6\r\n",
6616            "a limit of zero is no limit"
6617        );
6618        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6619        assert_eq!(
6620            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6621            ":4\r\n",
6622            "a missing key adds nothing to a union"
6623        );
6624
6625        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6626        assert_eq!(
6627            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6628            ":1\r\n"
6629        );
6630        assert_eq!(
6631            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6632            ":2\r\n",
6633            "a difference is not symmetric"
6634        );
6635        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6636        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6637        assert_eq!(
6638            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6639            ":0\r\n",
6640            "nothing taken away from nothing"
6641        );
6642
6643        // The same three messages SINTERCARD has, because the line is the same
6644        // line and is parsed once for all three.
6645        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6646            assert_eq!(
6647                f.run(&[name, b"0", b"a"]),
6648                "-ERR numkeys should be greater than 0\r\n"
6649            );
6650            assert_eq!(
6651                f.run(&[name, b"abc", b"a"]),
6652                "-ERR numkeys should be greater than 0\r\n"
6653            );
6654            assert_eq!(
6655                f.run(&[name, b"-1", b"a"]),
6656                "-ERR numkeys should be greater than 0\r\n"
6657            );
6658            assert_eq!(
6659                f.run(&[name, b"3", b"a", b"b"]),
6660                "-ERR Number of keys can't be greater than number of args\r\n"
6661            );
6662            assert_eq!(
6663                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6664                "-ERR LIMIT can't be negative\r\n"
6665            );
6666            assert_eq!(
6667                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6668                "-ERR LIMIT can't be negative\r\n",
6669                "a LIMIT that is not a number gets the negative message too"
6670            );
6671            assert_eq!(
6672                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6673                "-ERR syntax error\r\n"
6674            );
6675            assert_eq!(
6676                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6677                "-ERR syntax error\r\n"
6678            );
6679            assert_eq!(
6680                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6681                "-ERR syntax error\r\n"
6682            );
6683        }
6684
6685        // And a key called LIMIT is a key, here as much as on SINTERCARD.
6686        f.run(&[b"SADD", b"LIMIT", b"2"]);
6687        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6688        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6689    }
6690
6691    #[test]
6692    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6693        let mut f = Fixture::new();
6694        f.run(&[b"SADD", b"a", b"1"]);
6695        f.run(&[b"SADD", b"d", b"old"]);
6696        f.run(&[b"SET", b"str", b"v"]);
6697
6698        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6699        for bad in [
6700            &[b"SINTER".as_slice(), b"a", b"str"][..],
6701            &[b"SUNION".as_slice(), b"str"][..],
6702            &[b"SDIFF".as_slice(), b"a", b"str"][..],
6703            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6704            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6705            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6706            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6707        ] {
6708            let reply = f.run(bad);
6709            assert_eq!(reply, wrong, "for {:?}", bad[0]);
6710        }
6711        assert_eq!(
6712            f.run(&[b"SMEMBERS", b"d"]),
6713            "*1\r\n$3\r\nold\r\n",
6714            "and the destination was left alone every time"
6715        );
6716    }
6717
6718    /// The leak a set can spring that nothing on the wire would ever show: the
6719    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
6720    #[test]
6721    fn churning_sets_does_not_grow_the_server() {
6722        let mut f = Fixture::new();
6723        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6724        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6725            .chain(std::iter::once(&b"s"[..]))
6726            .chain(members.iter().map(Vec::as_slice))
6727            .collect();
6728
6729        f.run(&args);
6730        f.run(&[b"DEL", b"s"]);
6731        f.server.compact_step();
6732        let after_first = f.server.memory_bytes();
6733
6734        for _ in 0..200 {
6735            f.run(&args);
6736            f.run(&[b"DEL", b"s"]);
6737            f.server.compact_step();
6738        }
6739        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6740        assert!(
6741            f.server.memory_bytes() <= after_first * 2,
6742            "held {} after two hundred passes against {after_first} after one",
6743            f.server.memory_bytes()
6744        );
6745    }
6746
6747    // --------------------------------------------------------------- bitmaps
6748
6749    /// The two single bit commands, and the encoding rule underneath them.
6750    ///
6751    /// A write always leaves the value `raw` and a read never re-encodes, which
6752    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
6753    /// with its first digit changed after a `SETBIT`.
6754    #[test]
6755    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6756        let mut f = Fixture::new();
6757        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6758        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6759        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6760        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6761        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6762        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6763
6764        // Writing a nought past the end still creates the key and still pads.
6765        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6766        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6767        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6768
6769        f.run(&[b"SET", b"num", b"12345"]);
6770        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6771        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6772        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6773        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6774        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6775    }
6776
6777    /// Counting, in bytes and in bits.
6778    ///
6779    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
6780    /// says 22 for it. The server is the thing being copied here.
6781    #[test]
6782    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6783        let mut f = Fixture::new();
6784        f.run(&[b"SET", b"mykey", b"foobar"]);
6785        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6786        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6787        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6788        assert_eq!(
6789            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6790            ":6\r\n"
6791        );
6792        assert_eq!(
6793            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6794            ":25\r\n"
6795        );
6796        assert_eq!(
6797            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6798            ":17\r\n"
6799        );
6800        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6801
6802        // A start past the end is left where it is and the end is pulled back,
6803        // so the range comes out backwards and counts nothing.
6804        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6805
6806        // A lone start is a syntax error here, where BITPOS allows it.
6807        assert_eq!(
6808            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6809            "-ERR syntax error\r\n"
6810        );
6811        assert_eq!(
6812            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6813            "-ERR syntax error\r\n"
6814        );
6815    }
6816
6817    /// Searching, and the one place a miss is not minus one.
6818    ///
6819    /// A search for a nought that runs to the end of the string answers the
6820    /// length in bits, because the string is treated as if it had noughts after
6821    /// it forever. Give it an explicit end and it answers minus one instead.
6822    #[test]
6823    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6824        let mut f = Fixture::new();
6825        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6826        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6827        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6828        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6829        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6830        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6831
6832        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6833        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6834        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6835        assert_eq!(
6836            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6837            ":8\r\n"
6838        );
6839
6840        // A missing key is all noughts, so a one is never found and a nought is
6841        // at position zero.
6842        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6843        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6844    }
6845
6846    /// The eight operations, with the answers a real server gives for them.
6847    #[test]
6848    fn the_eight_combinations_write_what_a_real_server_writes() {
6849        let mut f = Fixture::new();
6850        f.run(&[b"SET", b"a", b"abc"]);
6851        f.run(&[b"SET", b"b", b"abd"]);
6852        let cases: &[(&[u8], &str)] = &[
6853            (b"AND", "ab`"),
6854            (b"OR", "abg"),
6855            (b"XOR", "\u{0}\u{0}\u{7}"),
6856            (b"DIFF", "\u{0}\u{0}\u{3}"),
6857            (b"DIFF1", "\u{0}\u{0}\u{4}"),
6858            (b"ANDOR", "ab`"),
6859            (b"ONE", "\u{0}\u{0}\u{7}"),
6860        ];
6861        for (op, want) in cases {
6862            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6863            assert_eq!(
6864                f.run(&[b"GET", b"d"]),
6865                format!("$3\r\n{want}\r\n"),
6866                "{op:?}"
6867            );
6868        }
6869        // The one whose answer is not text, so it is compared as bytes.
6870        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6871        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6872
6873        // A missing source is a string of noughts as long as it needs to be, so
6874        // an AND against one writes three zero bytes rather than nothing.
6875        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6876        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6877
6878        // Every source missing is an empty result, and an empty result takes
6879        // the destination with it.
6880        f.run(&[b"SET", b"dest", b"x"]);
6881        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6882        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6883    }
6884
6885    /// What `BITOP` says when it is asked for something it cannot do.
6886    #[test]
6887    fn bitop_names_the_operation_in_its_own_complaints() {
6888        let mut f = Fixture::new();
6889        f.run(&[b"SET", b"a", b"abc"]);
6890        assert_eq!(
6891            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6892            "-ERR syntax error\r\n"
6893        );
6894        assert_eq!(
6895            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6896            "-ERR BITOP NOT must be called with a single source key.\r\n"
6897        );
6898        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6899            assert_eq!(
6900                f.run(&[b"BITOP", op, b"d", b"a"]),
6901                format!(
6902                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
6903                    String::from_utf8_lossy(op)
6904                )
6905            );
6906        }
6907        f.run(&[b"LPUSH", b"l", b"x"]);
6908        assert_eq!(
6909            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6910            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6911        );
6912    }
6913
6914    /// Packed fields, the three overflow policies and the `#` offset.
6915    #[test]
6916    fn bitfield_reads_and_writes_packed_fields() {
6917        let mut f = Fixture::new();
6918        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6919        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6920
6921        assert_eq!(
6922            f.run(&[
6923                b"BITFIELD",
6924                b"bf",
6925                b"INCRBY",
6926                b"u2",
6927                b"100",
6928                b"1",
6929                b"GET",
6930                b"u4",
6931                b"0"
6932            ]),
6933            "*2\r\n:1\r\n:0\r\n"
6934        );
6935        // The field at bit 100 is two bits wide, so it ends in the thirteenth
6936        // byte and the value grew to thirteen bytes to hold it.
6937        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6938
6939        // A `#` offset counts in fields rather than in bits.
6940        assert_eq!(
6941            f.run(&[
6942                b"BITFIELD",
6943                b"bf",
6944                b"SET",
6945                b"u8",
6946                b"#0",
6947                b"255",
6948                b"GET",
6949                b"u8",
6950                b"#0"
6951            ]),
6952            "*2\r\n:0\r\n:255\r\n"
6953        );
6954
6955        assert_eq!(
6956            f.run(&[
6957                b"BITFIELD",
6958                b"bf",
6959                b"OVERFLOW",
6960                b"SAT",
6961                b"INCRBY",
6962                b"i8",
6963                b"0",
6964                b"120",
6965                b"INCRBY",
6966                b"i8",
6967                b"0",
6968                b"120"
6969            ]),
6970            "*2\r\n:119\r\n:127\r\n"
6971        );
6972        assert_eq!(
6973            f.run(&[
6974                b"BITFIELD",
6975                b"bf2",
6976                b"OVERFLOW",
6977                b"FAIL",
6978                b"INCRBY",
6979                b"u2",
6980                b"0",
6981                b"5"
6982            ]),
6983            "*1\r\n$-1\r\n"
6984        );
6985        assert_eq!(
6986            f.run(&[
6987                b"BITFIELD",
6988                b"bf3",
6989                b"OVERFLOW",
6990                b"WRAP",
6991                b"INCRBY",
6992                b"u2",
6993                b"0",
6994                b"5"
6995            ]),
6996            "*1\r\n:1\r\n"
6997        );
6998        assert_eq!(
6999            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
7000            "*1\r\n:4611686018427387904\r\n"
7001        );
7002    }
7003
7004    /// A bad subcommand anywhere in the line stops all of it.
7005    ///
7006    /// Redis checks the whole argument list before it runs any of it, so the
7007    /// `SET` in front of the bad type here never happens and the key it would
7008    /// have created is not there afterwards.
7009    #[test]
7010    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
7011        let mut f = Fixture::new();
7012        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
7013        assert_eq!(
7014            f.run(&[
7015                b"BITFIELD",
7016                b"bad",
7017                b"SET",
7018                b"u8",
7019                b"0",
7020                b"1",
7021                b"GET",
7022                b"u99",
7023                b"0"
7024            ]),
7025            bad_type
7026        );
7027        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
7028        assert_eq!(
7029            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
7030            bad_type
7031        );
7032        assert_eq!(
7033            f.run(&[b"BITFIELD", b"bad", b"GET"]),
7034            "-ERR syntax error\r\n"
7035        );
7036        assert_eq!(
7037            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
7038            "-ERR syntax error\r\n"
7039        );
7040        assert_eq!(
7041            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
7042            "-ERR syntax error\r\n"
7043        );
7044        assert_eq!(
7045            f.run(&[
7046                b"BITFIELD",
7047                b"bad",
7048                b"OVERFLOW",
7049                b"NOPE",
7050                b"GET",
7051                b"u8",
7052                b"0"
7053            ]),
7054            "-ERR Invalid OVERFLOW type specified\r\n"
7055        );
7056        assert_eq!(
7057            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
7058            "-ERR value is not an integer or out of range\r\n"
7059        );
7060        for at in [&b"#-1"[..], b"abc"] {
7061            assert_eq!(
7062                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
7063                "-ERR bit offset is not an integer or out of range\r\n"
7064            );
7065        }
7066    }
7067
7068    /// The read only twin reads, refuses to write, and creates nothing.
7069    #[test]
7070    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
7071        let mut f = Fixture::new();
7072        f.run(&[b"SET", b"n", b"123"]);
7073        assert_eq!(
7074            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
7075            "*1\r\n:49\r\n"
7076        );
7077        // A read does not unpack an int the way a write does.
7078        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
7079
7080        // An OVERFLOW word is allowed even though nothing here can overflow.
7081        assert_eq!(
7082            f.run(&[
7083                b"BITFIELD_RO",
7084                b"n",
7085                b"OVERFLOW",
7086                b"SAT",
7087                b"GET",
7088                b"u8",
7089                b"0"
7090            ]),
7091            "*1\r\n:49\r\n"
7092        );
7093        for sub in [&b"SET"[..], b"INCRBY"] {
7094            assert_eq!(
7095                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
7096                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
7097            );
7098        }
7099
7100        assert_eq!(
7101            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
7102            "*1\r\n:0\r\n"
7103        );
7104        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
7105    }
7106
7107    /// The offsets a bitmap command will not take.
7108    #[test]
7109    fn an_offset_off_the_end_of_the_world_is_refused() {
7110        let mut f = Fixture::new();
7111        let bad = "-ERR bit offset is not an integer or out of range\r\n";
7112        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
7113            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
7114            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
7115        }
7116        for arg in [&b"2"[..], b"-1"] {
7117            assert_eq!(
7118                f.run(&[b"BITPOS", b"k", arg]),
7119                "-ERR The bit argument must be 1 or 0.\r\n"
7120            );
7121        }
7122        assert_eq!(
7123            f.run(&[b"BITPOS", b"k", b"abc"]),
7124            "-ERR value is not an integer or out of range\r\n"
7125        );
7126        assert_eq!(
7127            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
7128            "-ERR value is not an integer or out of range\r\n"
7129        );
7130        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
7131        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
7132        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
7133    }
7134
7135    /// Every one of the seven refuses a key that is not a string.
7136    #[test]
7137    fn every_bitmap_command_says_wrongtype() {
7138        let mut f = Fixture::new();
7139        f.run(&[b"LPUSH", b"l", b"x"]);
7140        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7141        let cases: &[&[&[u8]]] = &[
7142            &[b"SETBIT", b"l", b"0", b"1"],
7143            &[b"GETBIT", b"l", b"0"],
7144            &[b"BITCOUNT", b"l"],
7145            &[b"BITPOS", b"l", b"1"],
7146            &[b"BITOP", b"AND", b"d", b"l"],
7147            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
7148            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
7149        ];
7150        for case in cases {
7151            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
7152        }
7153    }
7154
7155    // --------------------------------------------------------- hyperloglogs
7156
7157    #[test]
7158    fn a_sketch_is_added_to_and_counted() {
7159        let mut f = Fixture::new();
7160        // Creating the key counts as a change, even with nothing to add.
7161        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
7162        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
7163        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
7164        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
7165        // And it is a string, which is not an implementation detail: a client
7166        // can `GET` a sketch out of one server and `SET` it into another.
7167        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
7168        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
7169
7170        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
7171        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
7172        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7173    }
7174
7175    #[test]
7176    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
7177        let mut f = Fixture::new();
7178        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7179        // Not text, so it is compared as bytes.
7180        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";
7181        let mut reply = b"$27\r\n".to_vec();
7182        reply.extend_from_slice(want);
7183        reply.extend_from_slice(b"\r\n");
7184        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
7185    }
7186
7187    #[test]
7188    fn counting_several_keys_counts_their_union() {
7189        let mut f = Fixture::new();
7190        f.run(&[b"PFADD", b"a", b"x", b"y"]);
7191        f.run(&[b"PFADD", b"b", b"y", b"z"]);
7192        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
7193        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
7194        // A key that is not there is an empty sketch, not an error and not
7195        // something that gets created by being counted.
7196        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
7197        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
7198        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
7199    }
7200
7201    #[test]
7202    fn a_merge_keeps_what_the_destination_had() {
7203        let mut f = Fixture::new();
7204        f.run(&[b"PFADD", b"a", b"x", b"y"]);
7205        f.run(&[b"PFADD", b"b", b"z"]);
7206        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
7207        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
7208        // The destination is one of the sources, so a second merge adds to it.
7209        f.run(&[b"PFADD", b"c", b"w"]);
7210        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
7211        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
7212        // And with no sources it is a no-op that still answers OK and still
7213        // creates a destination that was not there.
7214        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
7215        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
7216    }
7217
7218    #[test]
7219    fn the_debug_forms_answer_four_different_shapes() {
7220        let mut f = Fixture::new();
7221        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7222        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
7223        assert_eq!(
7224            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7225            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
7226        );
7227        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
7228        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
7229        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
7230        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
7231        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
7232        // A dense sketch has no opcodes left to print.
7233        assert_eq!(
7234            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
7235            "-ERR HLL encoding is not sparse\r\n"
7236        );
7237
7238        // All 16384 registers, of which three are not nought.
7239        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
7240        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
7241        assert_eq!(reply.matches(":0\r\n").count(), 16381);
7242        assert_eq!(reply.matches(":1\r\n").count(), 2);
7243        assert_eq!(reply.matches(":2\r\n").count(), 1);
7244
7245        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
7246    }
7247
7248    #[test]
7249    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
7250        let mut f = Fixture::new();
7251        f.run(&[b"SET", b"plain", b"not a sketch"]);
7252        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
7253        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
7254        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
7255        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
7256        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
7257
7258        // A key that is not a string at all gets the ordinary sentence, and a
7259        // destination that would have been written is not created.
7260        f.run(&[b"RPUSH", b"l", b"x"]);
7261        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7262        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
7263        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
7264        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
7265        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
7266        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
7267    }
7268
7269    #[test]
7270    fn pfdebug_has_its_own_complaints() {
7271        let mut f = Fixture::new();
7272        f.run(&[b"PFADD", b"h", b"a"]);
7273        // The word is quoted exactly as the client spelled it, and this is not
7274        // the "Try X HELP." sentence every other container command uses.
7275        assert_eq!(
7276            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
7277            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
7278        );
7279        // Where all three of the real commands take a missing key as empty.
7280        let gone = "-ERR The specified key does not exist\r\n";
7281        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
7282        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
7283        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
7284        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
7285        assert_eq!(
7286            f.run(&[b"PFDEBUG"]),
7287            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
7288        );
7289        assert_eq!(
7290            f.run(&[b"PFSELFTEST", b"x"]),
7291            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
7292        );
7293    }
7294
7295    #[test]
7296    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
7297        let mut f = Fixture::new();
7298        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
7299        // The sketch with its last byte cut off, which is still a header and a
7300        // magic and is a run length encoding that stops short of register 16384.
7301        let reply = f.raw(&[b"GET", b"h"]);
7302        let short = reply[5..reply.len() - 3].to_vec();
7303        f.run(&[b"SET", b"h", &short]);
7304        assert_eq!(
7305            f.run(&[b"PFCOUNT", b"h"]),
7306            "-INVALIDOBJ Corrupted HLL object detected\r\n"
7307        );
7308    }
7309
7310    #[test]
7311    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
7312        let mut f = Fixture::new();
7313        // One that stays sparse and one that has gone dense, since the payload
7314        // carries the bytes and the two encodings are different lengths.
7315        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
7316        for i in 0..10_000u32 {
7317            let ele = format!("e{i}");
7318            f.run(&[b"PFADD", b"big", ele.as_bytes()]);
7319        }
7320        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
7321        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
7322
7323        for key in [&b"small"[..], b"big"] {
7324            let mut copy = key.to_vec();
7325            copy.push(b'2');
7326            let bytes = payload(&f.raw(&[b"DUMP", key]));
7327            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
7328            // The bytes, the encoding and the estimate all come back, which is
7329            // the whole of what byte compatibility across a round trip means.
7330            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
7331            assert_eq!(
7332                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
7333                f.run(&[b"PFDEBUG", b"ENCODING", key])
7334            );
7335            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
7336        }
7337        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
7338        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
7339    }
7340
7341    /// One RESP2 bulk string. The JSON replies are almost all one of these and
7342    /// the text inside them has quotes in it, so writing the frame out by hand
7343    /// buries the part of the assertion that matters.
7344    fn bulk(s: &str) -> String {
7345        format!("${}\r\n{s}\r\n", s.len())
7346    }
7347
7348    /// A RESP2 array of bulk strings, which is what most of the list replies
7349    /// are and what writing them out by hand in every assertion looks like.
7350    fn bulks(parts: &[&str]) -> String {
7351        let mut s = format!("*{}\r\n", parts.len());
7352        for p in parts {
7353            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
7354        }
7355        s
7356    }
7357
7358    #[test]
7359    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
7360        let mut f = Fixture::new();
7361        // Each element in turn goes at the head, so the last one sent is at the
7362        // front when it is over. That reads like a bug in the client and it is
7363        // what every Redis has always done.
7364        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
7365        assert_eq!(
7366            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7367            bulks(&["c", "b", "a"])
7368        );
7369        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
7370        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
7371        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
7372        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
7373        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
7374        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
7375    }
7376
7377    #[test]
7378    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
7379        let mut f = Fixture::new();
7380        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
7381        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
7382        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7383        f.run(&[b"RPUSH", b"k", b"a"]);
7384        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
7385        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
7386        assert_eq!(
7387            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7388            bulks(&["z", "a", "y"])
7389        );
7390    }
7391
7392    /// The four ways a pop can come back with nothing, which are three
7393    /// different replies and a RESP2 client can tell all of them apart.
7394    #[test]
7395    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
7396        let mut f = Fixture::new();
7397        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
7398        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
7399        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
7400        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
7401        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7402        // A count of zero against a list that is there is an empty array and
7403        // not a null array, which is the fourth answer.
7404        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
7405        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
7406        // More than there is takes what there is and the key goes with it.
7407        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
7408        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7409    }
7410
7411    #[test]
7412    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
7413        let mut f = Fixture::new();
7414        f.run(&[b"RPUSH", b"k", b"a"]);
7415        let range = "-ERR value is out of range, must be positive\r\n";
7416        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
7417        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
7418        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
7419        // Redis calls this an arity error and not a syntax error, which is a
7420        // distinction it does not always make.
7421        assert_eq!(
7422            f.run(&[b"LPOP", b"k", b"1", b"2"]),
7423            "-ERR wrong number of arguments for 'lpop' command\r\n"
7424        );
7425        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7426    }
7427
7428    #[test]
7429    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
7430        let mut f = Fixture::new();
7431        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7432        assert_eq!(
7433            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7434            bulks(&["a", "b", "c"])
7435        );
7436        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
7437        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
7438        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
7439        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
7440        assert_eq!(
7441            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
7442            bulks(&["a", "b", "c"])
7443        );
7444        // A key that is not there is an empty range and not a nil, which is the
7445        // one place a list disagrees with a set.
7446        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
7447        assert_eq!(
7448            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
7449            "-ERR value is not an integer or out of range\r\n"
7450        );
7451    }
7452
7453    #[test]
7454    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
7455        let mut f = Fixture::new();
7456        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7457        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
7458        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
7459        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
7460        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
7461        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
7462        assert_eq!(
7463            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7464            bulks(&["a", "b", "z"])
7465        );
7466        // Both ways of missing are errors here rather than a nil, because a
7467        // list is never empty and there is nothing else the reply could be.
7468        assert_eq!(
7469            f.run(&[b"LSET", b"k", b"99", b"z"]),
7470            "-ERR index out of range\r\n"
7471        );
7472        assert_eq!(
7473            f.run(&[b"LSET", b"nope", b"0", b"z"]),
7474            "-ERR no such key\r\n"
7475        );
7476    }
7477
7478    #[test]
7479    fn linsert_says_three_things_with_one_signed_number() {
7480        let mut f = Fixture::new();
7481        // Zero for a key that is not there, which is not the same as minus one
7482        // for a pivot that is not in a list that is.
7483        assert_eq!(
7484            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
7485            ":0\r\n"
7486        );
7487        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7488        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
7489        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
7490        assert_eq!(
7491            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7492            bulks(&["X", "a", "b", "Y"])
7493        );
7494        assert_eq!(
7495            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
7496            ":-1\r\n"
7497        );
7498        assert_eq!(
7499            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
7500            "-ERR syntax error\r\n"
7501        );
7502    }
7503
7504    #[test]
7505    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
7506        let mut f = Fixture::new();
7507        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
7508        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
7509        assert_eq!(
7510            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
7511            bulks(&["b", "c", "a"])
7512        );
7513        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
7514        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7515        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
7516        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
7517        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7518        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
7519    }
7520
7521    #[test]
7522    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
7523        let mut f = Fixture::new();
7524        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
7525        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
7526        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
7527        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
7528        // leave `EXISTS` answering zero rather than leaving an empty one.
7529        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
7530        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
7531        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
7532    }
7533
7534    #[test]
7535    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
7536        let mut f = Fixture::new();
7537        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
7538        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
7539        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
7540        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
7541        assert_eq!(
7542            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
7543            "*2\r\n:0\r\n:3\r\n"
7544        );
7545        assert_eq!(
7546            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
7547            "*3\r\n:6\r\n:3\r\n:0\r\n"
7548        );
7549        // MAXLEN counts elements looked at and not matches found, so three
7550        // stops after `a b c` and finds the one match in it.
7551        assert_eq!(
7552            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
7553            "*1\r\n:0\r\n"
7554        );
7555        // Nothing found is three different replies depending on how it was
7556        // asked and whether the key is there at all.
7557        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
7558        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
7559        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
7560        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
7561    }
7562
7563    #[test]
7564    fn lpos_words_its_three_mistakes_the_way_redis_does() {
7565        let mut f = Fixture::new();
7566        f.run(&[b"RPUSH", b"p", b"a"]);
7567        // The whole sentence and not a prefix, because the older wording of it
7568        // is still all over the internet and clients match on the text.
7569        assert_eq!(
7570            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
7571            "-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"
7572        );
7573        assert_eq!(
7574            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
7575            "-ERR COUNT can't be negative\r\n"
7576        );
7577        assert_eq!(
7578            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
7579            "-ERR MAXLEN can't be negative\r\n"
7580        );
7581        assert_eq!(
7582            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
7583            "-ERR syntax error\r\n"
7584        );
7585        assert_eq!(
7586            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
7587            "-ERR syntax error\r\n"
7588        );
7589    }
7590
7591    #[test]
7592    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
7593        let mut f = Fixture::new();
7594        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
7595        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
7596        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7597        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
7598        assert_eq!(
7599            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
7600            "$1\r\na\r\n"
7601        );
7602        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7603        // The same key twice is the documented way to rotate a list and falls
7604        // out of taking the element before deciding where to put it.
7605        f.run(&[b"DEL", b"r"]);
7606        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7607        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7608        assert_eq!(
7609            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7610            bulks(&["3", "1", "2"])
7611        );
7612        assert_eq!(
7613            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7614            "$-1\r\n"
7615        );
7616        assert_eq!(
7617            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7618            "-ERR syntax error\r\n"
7619        );
7620    }
7621
7622    #[test]
7623    fn a_move_checks_the_destination_before_it_takes_anything() {
7624        let mut f = Fixture::new();
7625        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7626        f.run(&[b"SET", b"str", b"v"]);
7627        assert_eq!(
7628            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7629            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7630        );
7631        // The element is still where it was, rather than having gone nowhere.
7632        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7633    }
7634
7635    #[test]
7636    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7637        // OBO is what you get from sending LMOVE that many times, BULK keeps
7638        // the source order. The two only differ when both ends are the same,
7639        // which is the whole reason the word exists.
7640        for (from, to, order, want) in [
7641            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7642            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7643            ("LEFT", "LEFT", "OBO", ["b", "a"]),
7644            ("LEFT", "LEFT", "BULK", ["a", "b"]),
7645            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7646            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7647            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7648            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7649        ] {
7650            let mut f = Fixture::new();
7651            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7652            let how = format!("{from} {to} {order}");
7653            let reply = f.run(&[
7654                b"LMOVEM",
7655                b"s",
7656                b"d",
7657                from.as_bytes(),
7658                to.as_bytes(),
7659                b"COUNT",
7660                b"2",
7661                order.as_bytes(),
7662            ]);
7663            assert_eq!(reply, bulks(&want), "the reply for {how}");
7664            assert_eq!(
7665                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7666                bulks(&want),
7667                "the destination for {how}"
7668            );
7669        }
7670    }
7671
7672    #[test]
7673    fn a_block_move_of_one_needs_no_count_at_all() {
7674        let mut f = Fixture::new();
7675        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7676        assert_eq!(
7677            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7678            bulks(&["a"])
7679        );
7680        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7681        // Six and seven arguments are neither of the two forms, so the
7682        // reference calls both of them a syntax error rather than guessing.
7683        assert_eq!(
7684            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7685            "-ERR syntax error\r\n"
7686        );
7687        assert_eq!(
7688            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7689            "-ERR syntax error\r\n"
7690        );
7691    }
7692
7693    #[test]
7694    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7695        let mut f = Fixture::new();
7696        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7697        // A null array and not a null bulk string, which `redis-cli` prints as
7698        // `(nil)` either way and only the raw wire tells apart. What it would
7699        // have sent is an array, so its nothing is an array's nothing.
7700        assert_eq!(
7701            f.run(&[
7702                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7703            ]),
7704            "*-1\r\n"
7705        );
7706        assert_eq!(
7707            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7708            bulks(&["a", "b", "c"])
7709        );
7710        // COUNT takes what there is, and an emptied source goes away.
7711        assert_eq!(
7712            f.run(&[
7713                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7714            ]),
7715            bulks(&["a", "b", "c"])
7716        );
7717        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7718        assert_eq!(
7719            f.run(&[
7720                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7721            ]),
7722            "*-1\r\n"
7723        );
7724    }
7725
7726    #[test]
7727    fn a_block_move_onto_itself_rotates_by_the_count() {
7728        let mut f = Fixture::new();
7729        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7730        assert_eq!(
7731            f.run(&[
7732                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7733            ]),
7734            bulks(&["a", "b"])
7735        );
7736        assert_eq!(
7737            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7738            bulks(&["c", "a", "b"])
7739        );
7740    }
7741
7742    #[test]
7743    fn a_block_move_reads_the_count_before_the_ordering_word() {
7744        let mut f = Fixture::new();
7745        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7746        f.run(&[b"SET", b"str", b"v"]);
7747        let count = "-ERR count should be greater than 0\r\n";
7748        assert_eq!(
7749            f.run(&[
7750                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7751            ]),
7752            count
7753        );
7754        assert_eq!(
7755            f.run(&[
7756                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7757            ]),
7758            count
7759        );
7760        assert_eq!(
7761            f.run(&[
7762                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7763            ]),
7764            "-ERR syntax error\r\n"
7765        );
7766        assert_eq!(
7767            f.run(&[
7768                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7769            ]),
7770            "-ERR syntax error\r\n"
7771        );
7772        // Every argument is read before the keys are looked at, so a bad count
7773        // beats a wrong type even when the type is wrong on the source.
7774        assert_eq!(
7775            f.run(&[
7776                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7777            ]),
7778            count
7779        );
7780        assert_eq!(
7781            f.run(&[
7782                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7783            ]),
7784            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7785        );
7786        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7787    }
7788
7789    #[test]
7790    fn lmpop_answers_from_the_first_key_that_has_anything() {
7791        let mut f = Fixture::new();
7792        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7793        // The name of the key that answered comes back with the elements,
7794        // because the client cannot work out which one it was.
7795        assert_eq!(
7796            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7797            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7798        );
7799        assert_eq!(
7800            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7801            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7802        );
7803        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7804        // A null array and not a null, even though what it stands in for is an
7805        // array holding a key name and then another array.
7806        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7807    }
7808
7809    #[test]
7810    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7811        let mut f = Fixture::new();
7812        f.run(&[b"RPUSH", b"k", b"a"]);
7813        assert_eq!(
7814            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7815            "-ERR numkeys should be greater than 0\r\n"
7816        );
7817        assert_eq!(
7818            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7819            "-ERR numkeys should be greater than 0\r\n"
7820        );
7821        assert_eq!(
7822            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7823            "-ERR count should be greater than 0\r\n"
7824        );
7825        // A key count that eats the direction is a syntax error and not a
7826        // sentence about key counts, because the direction is simply not there.
7827        assert_eq!(
7828            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7829            "-ERR syntax error\r\n"
7830        );
7831        assert_eq!(
7832            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7833            "-ERR syntax error\r\n"
7834        );
7835        assert_eq!(
7836            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7837            "-ERR syntax error\r\n"
7838        );
7839        assert_eq!(
7840            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7841            "-ERR syntax error\r\n"
7842        );
7843        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7844    }
7845
7846    #[test]
7847    fn every_list_command_says_wrongtype_and_writes_nothing() {
7848        let mut f = Fixture::new();
7849        f.run(&[b"SET", b"str", b"v"]);
7850        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7851        for cmd in [
7852            &[b"LPUSH".as_slice(), b"str", b"a"][..],
7853            &[b"RPUSH", b"str", b"a"],
7854            &[b"LPUSHX", b"str", b"a"],
7855            &[b"RPUSHX", b"str", b"a"],
7856            &[b"LPOP", b"str"],
7857            &[b"LPOP", b"str", b"2"],
7858            &[b"RPOP", b"str"],
7859            &[b"LLEN", b"str"],
7860            &[b"LRANGE", b"str", b"0", b"-1"],
7861            &[b"LINDEX", b"str", b"0"],
7862            &[b"LSET", b"str", b"0", b"a"],
7863            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7864            &[b"LREM", b"str", b"0", b"a"],
7865            &[b"LTRIM", b"str", b"0", b"-1"],
7866            &[b"LPOS", b"str", b"a"],
7867            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7868            &[b"RPOPLPUSH", b"str", b"d"],
7869            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7870            &[b"LMPOP", b"1", b"str", b"LEFT"],
7871        ] {
7872            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7873        }
7874        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7875        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7876    }
7877
7878    /// A timeout is not an integer and it is not an ordinary float either: the
7879    /// three sentences it can answer with are its own, and which one a given
7880    /// argument gets is not what reading the code would suggest.
7881    #[test]
7882    fn a_timeout_has_three_ways_of_being_wrong() {
7883        let mut f = Fixture::new();
7884        let not_float = "-ERR timeout is not a float or out of range\r\n";
7885        let range = "-ERR timeout is out of range\r\n";
7886        for (bad, want) in [
7887            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7888            (&[b"BLPOP", b"k", b"nan"], not_float),
7889            (&[b"BLPOP", b"k", b""], not_float),
7890            // Whitespace on either side, which `strtold` would take and Redis
7891            // does not.
7892            (&[b"BLPOP", b"k", b" 1"], not_float),
7893            (&[b"BLPOP", b"k", b"1 "], not_float),
7894            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7895            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7896            // These three parse, so they are not the not-a-float error, and all
7897            // three are further off than an i64 of milliseconds reaches.
7898            (&[b"BLPOP", b"k", b"1e400"], range),
7899            (&[b"BLPOP", b"k", b"inf"], range),
7900            (&[b"BLPOP", b"k", b"9999999999999999"], range),
7901            (&[b"BRPOP", b"k", b"abc"], not_float),
7902            (
7903                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7904                not_float,
7905            ),
7906            (
7907                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7908                "-ERR timeout is negative\r\n",
7909            ),
7910            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7911        ] {
7912            assert_eq!(f.run(bad), want, "for {bad:?}");
7913        }
7914    }
7915
7916    /// A timeout of exactly zero means no timeout, and there are two ways of
7917    /// writing exactly zero.
7918    #[test]
7919    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7920        let mut f = Fixture::new();
7921        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7922            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7923            assert_eq!(flow, Flow::Block, "for {timeout:?}");
7924            assert!(out.is_empty(), "for {timeout:?}");
7925        }
7926        // Positive, so it is a real deadline, and the deadline is this
7927        // millisecond. Nothing is written here either: the reply comes from the
7928        // sweep, which is the engine's and not this layer's.
7929        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7930        assert_eq!(flow, Flow::Block);
7931        assert!(out.is_empty());
7932    }
7933
7934    #[test]
7935    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7936        let mut f = Fixture::new();
7937        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7938
7939        // The one difference from LPOP: the reply names the key that answered,
7940        // which is what makes BLPOP over several keys usable.
7941        assert_eq!(
7942            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7943            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7944        );
7945        assert_eq!(
7946            f.run(&[b"BRPOP", b"L", b"0"]),
7947            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7948        );
7949        assert_eq!(
7950            f.run(&[
7951                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7952            ]),
7953            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7954        );
7955        assert_eq!(
7956            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7957            "$1\r\nd\r\n"
7958        );
7959        assert_eq!(
7960            f.run(&[b"EXISTS", b"L"]),
7961            ":0\r\n",
7962            "and the key went with it"
7963        );
7964        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7965        // Onto itself, which is how a list is rotated and is a real thing to ask
7966        // a blocking move for.
7967        f.run(&[b"RPUSH", b"D", b"x"]);
7968        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7969        assert_eq!(
7970            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7971            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7972        );
7973    }
7974
7975    #[test]
7976    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7977        let mut f = Fixture::new();
7978        f.run(&[b"RPUSH", b"k", b"a"]);
7979        for (bad, want) in [
7980            (
7981                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7982                "-ERR numkeys should be greater than 0\r\n",
7983            ),
7984            (
7985                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7986                "-ERR numkeys should be greater than 0\r\n",
7987            ),
7988            // Two keys named and one given, so the word that should have been
7989            // the direction is a key and there is no direction left.
7990            (
7991                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7992                "-ERR syntax error\r\n",
7993            ),
7994            (
7995                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7996                "-ERR syntax error\r\n",
7997            ),
7998            (
7999                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
8000                "-ERR syntax error\r\n",
8001            ),
8002            (
8003                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
8004                "-ERR syntax error\r\n",
8005            ),
8006            // A count that is not a number at all gets the same sentence a zero
8007            // or a negative one gets, rather than the usual one about integers.
8008            (
8009                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
8010                "-ERR count should be greater than 0\r\n",
8011            ),
8012            (
8013                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
8014                "-ERR count should be greater than 0\r\n",
8015            ),
8016        ] {
8017            assert_eq!(f.run(bad), want, "for {bad:?}");
8018        }
8019        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
8020    }
8021
8022    #[test]
8023    fn a_blocking_move_reads_its_directions_before_its_timeout() {
8024        let mut f = Fixture::new();
8025        // Both are wrong. Redis checks the directions first, so this is the
8026        // syntax error and not a complaint about the timeout.
8027        assert_eq!(
8028            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
8029            "-ERR syntax error\r\n"
8030        );
8031        assert_eq!(
8032            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
8033            "-ERR syntax error\r\n"
8034        );
8035    }
8036
8037    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
8038    /// wait, which is the same relationship every other command in this file has
8039    /// with the one it wraps.
8040    #[test]
8041    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
8042        let mut f = Fixture::new();
8043        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
8044        assert_eq!(
8045            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
8046            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
8047        );
8048        assert_eq!(
8049            f.run(&[
8050                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
8051            ]),
8052            bulks(&["e", "d"])
8053        );
8054        assert_eq!(
8055            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
8056            bulks(&["a", "e", "d"])
8057        );
8058        // `EXACTLY` with enough there does not wait either.
8059        assert_eq!(
8060            f.run(&[
8061                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
8062            ]),
8063            bulks(&["b", "c"])
8064        );
8065        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
8066    }
8067
8068    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
8069    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
8070    /// whole block has arrived.
8071    #[test]
8072    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
8073        let mut f = Fixture::new();
8074        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
8075        // Two there and three asked for. `COUNT` takes the two.
8076        assert_eq!(
8077            f.flow(&[
8078                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
8079            ]),
8080            (Flow::Continue, bulks(&["a", "b"]))
8081        );
8082
8083        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
8084        // The same line with `EXACTLY` parks instead, and takes nothing on the
8085        // way past.
8086        assert_eq!(
8087            f.flow(&[
8088                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
8089            ])
8090            .0,
8091            Flow::Block
8092        );
8093        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
8094    }
8095
8096    #[test]
8097    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
8098        let mut f = Fixture::new();
8099        let syntax = "-ERR syntax error\r\n";
8100        // All three are wrong and the directions are read first.
8101        assert_eq!(
8102            f.run(&[
8103                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
8104            ]),
8105            syntax
8106        );
8107        // Directions fine, timeout and count both wrong, so the timeout wins.
8108        assert_eq!(
8109            f.run(&[
8110                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
8111            ]),
8112            "-ERR timeout is not a float or out of range\r\n"
8113        );
8114        assert_eq!(
8115            f.run(&[
8116                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
8117            ]),
8118            "-ERR timeout is negative\r\n"
8119        );
8120        // And with the timeout fine, the count before the ordering word.
8121        assert_eq!(
8122            f.run(&[
8123                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
8124            ]),
8125            "-ERR count should be greater than 0\r\n"
8126        );
8127        assert_eq!(
8128            f.run(&[
8129                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
8130            ]),
8131            syntax
8132        );
8133        // Seven and eight arguments are neither of the two forms, the same way
8134        // six and seven are for `LMOVEM`.
8135        assert_eq!(
8136            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
8137            syntax
8138        );
8139        assert_eq!(
8140            f.run(&[
8141                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
8142            ]),
8143            syntax
8144        );
8145    }
8146
8147    /// The four ways a blocking command sees a key of another type, and the one
8148    /// way it does not.
8149    #[test]
8150    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
8151        let mut f = Fixture::new();
8152        f.run(&[b"SET", b"S", b"v"]);
8153        f.run(&[b"RPUSH", b"D", b"x"]);
8154        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8155
8156        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
8157        // Every key is checked even when an earlier one would have blocked, so
8158        // an empty key in front of a string does not hide it.
8159        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
8160        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
8161        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
8162        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
8163        // The destination, which is only reached because the source has
8164        // something in it.
8165        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
8166        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
8167        assert_eq!(
8168            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
8169            wrong
8170        );
8171        assert_eq!(
8172            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
8173            wrong
8174        );
8175
8176        // And the one that does not: an empty source means the destination is
8177        // never looked at, so this waits rather than erroring, and on a real
8178        // server it times out.
8179        assert_eq!(
8180            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
8181                .0,
8182            Flow::Block
8183        );
8184        // `BLMOVEM` has a second way of not being ready, and it hides the
8185        // destination just as well: the source is a list with two elements in it
8186        // and `EXACTLY` wants three, so the string never gets looked at.
8187        assert_eq!(
8188            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
8189                .0,
8190            Flow::Block
8191        );
8192        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
8193        assert_eq!(
8194            f.flow(&[
8195                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
8196            ])
8197            .0,
8198            Flow::Block
8199        );
8200    }
8201
8202    /// The same churn the set and the string get, because a list that leaks a
8203    /// chunk per push looks exactly like one that does not until it has run for
8204    /// an afternoon.
8205    #[test]
8206    fn churning_lists_does_not_grow_the_server() {
8207        let mut f = Fixture::new();
8208        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
8209        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
8210            .into_iter()
8211            .chain(vals.iter().map(Vec::as_slice))
8212            .collect();
8213
8214        f.run(&args);
8215        f.run(&[b"DEL", b"k"]);
8216        f.server.compact_step();
8217        let after_first = f.server.memory_bytes();
8218
8219        for _ in 0..200 {
8220            f.run(&args);
8221            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
8222            f.server.compact_step();
8223        }
8224        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8225        assert!(
8226            f.server.memory_bytes() <= after_first * 2,
8227            "held {} after two hundred passes against {after_first} after one",
8228            f.server.memory_bytes()
8229        );
8230    }
8231
8232    // ------------------------------------------------------------ sorted set
8233
8234    #[test]
8235    fn a_sorted_set_takes_scores_and_gives_them_back() {
8236        let mut f = Fixture::new();
8237        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
8238        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
8239        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
8240        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
8241        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
8242        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
8243        assert_eq!(
8244            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
8245            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
8246        );
8247        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
8248        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
8249        // The key goes when the last member does.
8250        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
8251        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8252    }
8253
8254    #[test]
8255    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
8256        let mut f = Fixture::new();
8257        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
8258        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
8259        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
8260        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
8261
8262        f.out = Out::new(Proto::Resp3);
8263        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
8264        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
8265        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
8266        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
8267    }
8268
8269    #[test]
8270    fn the_zadd_options_gate_what_gets_written() {
8271        let mut f = Fixture::new();
8272        f.run(&[b"ZADD", b"z", b"5", b"a"]);
8273        // NX leaves a member that is there alone, XX will not create one.
8274        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
8275        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
8276        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
8277        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
8278        // GT and LT only move a score one way.
8279        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
8280        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
8281        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
8282        // CH counts a moved score and plain ZADD does not.
8283        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
8284        assert_eq!(
8285            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
8286            ":2\r\n"
8287        );
8288    }
8289
8290    #[test]
8291    fn zadd_incr_answers_a_score_or_nothing_at_all() {
8292        let mut f = Fixture::new();
8293        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
8294        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
8295        // A gate that refuses is the string nil, because the reply it stands in
8296        // for is a score.
8297        assert_eq!(
8298            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
8299            "$-1\r\n"
8300        );
8301        assert_eq!(
8302            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
8303            "$-1\r\n"
8304        );
8305        assert_eq!(
8306            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
8307            "$-1\r\n"
8308        );
8309        assert_eq!(
8310            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
8311            "$1\r\n8\r\n"
8312        );
8313        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
8314        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
8315    }
8316
8317    #[test]
8318    fn the_two_infinities_will_not_be_added_together() {
8319        let mut f = Fixture::new();
8320        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
8321        let nan = "-ERR resulting score is not a number (NaN)\r\n";
8322        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
8323        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
8324        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
8325        // And a key made for an increment that then fails does not stay behind.
8326        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
8327    }
8328
8329    #[test]
8330    fn zadd_says_its_mistakes_the_way_redis_says_them() {
8331        let mut f = Fixture::new();
8332        // The pairs are counted before the options are looked at, so this is a
8333        // syntax error about having none and not a complaint about NX and XX.
8334        assert_eq!(
8335            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
8336            "-ERR syntax error\r\n"
8337        );
8338        assert_eq!(
8339            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
8340            "-ERR XX and NX options at the same time are not compatible\r\n"
8341        );
8342        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
8343        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
8344        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
8345        assert_eq!(
8346            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
8347            "-ERR INCR option supports a single increment-element pair\r\n"
8348        );
8349        // An odd number of arguments after the options.
8350        assert_eq!(
8351            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
8352            "-ERR syntax error\r\n"
8353        );
8354        // Every score is read before the first is stored.
8355        assert_eq!(
8356            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
8357            "-ERR value is not a valid float\r\n"
8358        );
8359        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8360    }
8361
8362    #[test]
8363    fn a_rank_says_where_a_member_sits_from_either_end() {
8364        let mut f = Fixture::new();
8365        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8366        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
8367        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
8368        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
8369        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
8370        // WITHSCORE changes both shapes: the answer and the nothing.
8371        assert_eq!(
8372            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
8373            "*2\r\n:1\r\n$1\r\n2\r\n"
8374        );
8375        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
8376        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
8377        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
8378        // A bad option is a syntax error and one argument too many is an arity
8379        // error, which is Redis's split.
8380        assert_eq!(
8381            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
8382            "-ERR syntax error\r\n"
8383        );
8384        assert_eq!(
8385            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
8386            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
8387        );
8388    }
8389
8390    #[test]
8391    fn the_two_counts_read_their_two_kinds_of_bound() {
8392        let mut f = Fixture::new();
8393        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8394        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
8395        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
8396        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
8397        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
8398        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
8399        assert_eq!(
8400            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
8401            "-ERR min or max is not a float\r\n"
8402        );
8403
8404        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
8405        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
8406        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
8407        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
8408        // A bare member is not a bound, because a member can start with any
8409        // byte and there would be no way to say the bracket if it were optional.
8410        assert_eq!(
8411            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
8412            "-ERR min or max not valid string range item\r\n"
8413        );
8414    }
8415
8416    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
8417    ///
8418    /// Every byte in here was read off a real 8.10.1 rather than worked out,
8419    /// because the interesting part of this command is not what it selects, it
8420    /// is which of the two ends the client is expected to name first.
8421    #[test]
8422    fn one_range_command_selects_by_rank_or_score_or_name() {
8423        let mut f = Fixture::new();
8424        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8425        assert_eq!(
8426            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8427            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8428        );
8429        assert_eq!(
8430            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
8431            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8432        );
8433        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
8434        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
8435        // REV over ranks reverses the walk and leaves the two arguments alone,
8436        // because a rank counts from the end the walk starts at.
8437        assert_eq!(
8438            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
8439            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8440        );
8441        assert_eq!(
8442            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
8443            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8444        );
8445        // And REV over scores does swap them, since a bound does not count from
8446        // anywhere. This is the one line of the parse that tells the two apart.
8447        assert_eq!(
8448            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
8449            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8450        );
8451        assert_eq!(
8452            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
8453            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8454        );
8455        assert_eq!(
8456            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
8457            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8458        );
8459    }
8460
8461    /// The older spellings, which are the same six windows with the mode in the
8462    /// name and the high end named first on the three that go backwards.
8463    #[test]
8464    fn the_older_range_spellings_name_their_high_end_first() {
8465        let mut f = Fixture::new();
8466        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8467        assert_eq!(
8468            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
8469            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
8470        );
8471        assert_eq!(
8472            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
8473            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8474        );
8475        assert_eq!(
8476            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
8477            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8478        );
8479        assert_eq!(
8480            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
8481            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
8482        );
8483        // The two arguments the wrong way round is an empty answer and not an
8484        // error, which is what the swap being in the parse rather than in the
8485        // window buys.
8486        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
8487        assert_eq!(
8488            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
8489            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
8490        );
8491        assert_eq!(
8492            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
8493            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
8494        );
8495        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
8496        // way of spelling the mode, they are a syntax error.
8497        for cmd in [
8498            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
8499            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
8500            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
8501        ] {
8502            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
8503        }
8504    }
8505
8506    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
8507    /// only some of them accept.
8508    #[test]
8509    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
8510        let mut f = Fixture::new();
8511        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8512        assert_eq!(
8513            f.run(&[
8514                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
8515            ]),
8516            "*1\r\n$1\r\nb\r\n"
8517        );
8518        // A negative offset skips past everything, a negative count is no bound.
8519        assert_eq!(
8520            f.run(&[
8521                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
8522            ]),
8523            "*0\r\n"
8524        );
8525        assert_eq!(
8526            f.run(&[
8527                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
8528            ]),
8529            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8530        );
8531        // The two options in either order, which falls out of the parse loop.
8532        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";
8533        assert_eq!(
8534            f.run(&[
8535                b"ZRANGEBYSCORE",
8536                b"z",
8537                b"1",
8538                b"3",
8539                b"WITHSCORES",
8540                b"LIMIT",
8541                b"0",
8542                b"2"
8543            ]),
8544            both
8545        );
8546        assert_eq!(
8547            f.run(&[
8548                b"ZRANGEBYSCORE",
8549                b"z",
8550                b"1",
8551                b"3",
8552                b"LIMIT",
8553                b"0",
8554                b"2",
8555                b"WITHSCORES"
8556            ]),
8557            both
8558        );
8559        // LIMIT on a range by rank is refused after the whole option list has
8560        // been read, so this complains about LIMIT and not about WITHSCORES.
8561        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
8562        assert_eq!(
8563            f.run(&[
8564                b"ZREVRANGE",
8565                b"z",
8566                b"0",
8567                b"-1",
8568                b"WITHSCORES",
8569                b"LIMIT",
8570                b"0",
8571                b"1"
8572            ]),
8573            needs_by
8574        );
8575        assert_eq!(
8576            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
8577            needs_by
8578        );
8579        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
8580        assert_eq!(
8581            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
8582            not_bylex
8583        );
8584        assert_eq!(
8585            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
8586            not_bylex
8587        );
8588        // Two modes at once, an option nobody knows, a LIMIT missing its count,
8589        // and the three number errors, which are three different sentences.
8590        for cmd in [
8591            &[
8592                b"ZRANGE".as_slice(),
8593                b"z",
8594                b"0",
8595                b"-1",
8596                b"BYSCORE",
8597                b"BYLEX",
8598            ][..],
8599            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
8600            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
8601        ] {
8602            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8603        }
8604        assert_eq!(
8605            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8606            "-ERR min or max is not a float\r\n"
8607        );
8608        assert_eq!(
8609            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8610            "-ERR min or max not valid string range item\r\n"
8611        );
8612        assert_eq!(
8613            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8614            "-ERR value is not an integer or out of range\r\n"
8615        );
8616    }
8617
8618    /// `WITHSCORES` is the one place in this group where the two protocols
8619    /// disagree about the shape of the reply and not just the type of a value.
8620    #[test]
8621    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8622        let mut f = Fixture::new();
8623        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8624        assert_eq!(
8625            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8626            "*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"
8627        );
8628        f.out = Out::new(Proto::Resp3);
8629        assert_eq!(
8630            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8631            "*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"
8632        );
8633        assert_eq!(
8634            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8635            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8636        );
8637    }
8638
8639    /// The store form, which is the same parse with the destination in front.
8640    #[test]
8641    fn a_range_store_writes_the_window_into_another_key() {
8642        let mut f = Fixture::new();
8643        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8644        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8645        // A window that selects nothing deletes the destination rather than
8646        // leaving an empty sorted set, because an empty one does not exist.
8647        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8648        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8649        assert_eq!(
8650            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8651            ":2\r\n"
8652        );
8653        assert_eq!(
8654            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8655            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8656        );
8657        // The destination is allowed to be the source, because the result is
8658        // built whole before anything is written over.
8659        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8660        assert_eq!(
8661            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8662            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8663        );
8664        // It takes every option ZRANGE takes except WITHSCORES, which is a
8665        // plain syntax error here and not the sentence about BYLEX.
8666        assert_eq!(
8667            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8668            "-ERR syntax error\r\n"
8669        );
8670    }
8671
8672    /// The three removals, which are the read side's window with the walk
8673    /// turned into a removal and no options at all.
8674    #[test]
8675    fn the_three_removals_share_their_window_with_the_reads() {
8676        let mut f = Fixture::new();
8677        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8678        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8679        assert_eq!(
8680            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8681            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8682        );
8683        assert_eq!(
8684            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8685            ":1\r\n"
8686        );
8687        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8688        // The last member going takes the key with it.
8689        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8690        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8691        assert_eq!(
8692            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8693            ":0\r\n"
8694        );
8695        assert_eq!(
8696            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8697            "-ERR value is not an integer or out of range\r\n"
8698        );
8699    }
8700
8701    /// The algebra, which is one gather and three names for it.
8702    #[test]
8703    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8704        let mut f = Fixture::new();
8705        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8706        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8707        assert_eq!(
8708            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8709            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8710        );
8711        // The scores are added where a member is in both, and the answer comes
8712        // out in the order those combined scores put it in.
8713        assert_eq!(
8714            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8715            "*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"
8716        );
8717        assert_eq!(
8718            f.run(&[
8719                b"ZUNION",
8720                b"2",
8721                b"z",
8722                b"y",
8723                b"WEIGHTS",
8724                b"2",
8725                b"3",
8726                b"WITHSCORES"
8727            ]),
8728            "*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"
8729        );
8730        assert_eq!(
8731            f.run(&[
8732                b"ZUNION",
8733                b"2",
8734                b"z",
8735                b"y",
8736                b"AGGREGATE",
8737                b"MIN",
8738                b"WITHSCORES"
8739            ]),
8740            "*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"
8741        );
8742        assert_eq!(
8743            f.run(&[
8744                b"ZUNION",
8745                b"2",
8746                b"z",
8747                b"y",
8748                b"AGGREGATE",
8749                b"MAX",
8750                b"WITHSCORES"
8751            ]),
8752            "*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"
8753        );
8754        assert_eq!(
8755            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8756            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8757        );
8758        assert_eq!(
8759            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8760            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8761        );
8762        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8763        // A plain set is an input, and it behaves as a sorted set in which
8764        // every member scores one.
8765        f.run(&[b"SADD", b"p", b"a", b"d"]);
8766        assert_eq!(
8767            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8768            "*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"
8769        );
8770        // A difference never combines two scores, so it has nothing for either
8771        // of the two options to do and refuses both.
8772        for cmd in [
8773            &[
8774                b"ZDIFF".as_slice(),
8775                b"2",
8776                b"z",
8777                b"y",
8778                b"WEIGHTS",
8779                b"1",
8780                b"1",
8781            ][..],
8782            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8783        ] {
8784            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8785        }
8786    }
8787
8788    /// The count of keys, which is what lets a key be named `WEIGHTS`.
8789    #[test]
8790    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8791        let mut f = Fixture::new();
8792        f.run(&[b"ZADD", b"z", b"1", b"a"]);
8793        f.run(&[b"ZADD", b"y", b"2", b"b"]);
8794        // Redis names the command in this one, so each spelling says its own.
8795        assert_eq!(
8796            f.run(&[b"ZUNION", b"0", b"z"]),
8797            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8798        );
8799        assert_eq!(
8800            f.run(&[b"ZUNION", b"-1", b"z"]),
8801            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8802        );
8803        assert_eq!(
8804            f.run(&[b"ZINTERCARD", b"0", b"z"]),
8805            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8806        );
8807        // A count bigger than the line is a plain syntax error, which reads
8808        // oddly and is what Redis says.
8809        assert_eq!(
8810            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8811            "-ERR syntax error\r\n"
8812        );
8813        assert_eq!(
8814            f.run(&[b"ZUNION", b"x", b"z"]),
8815            "-ERR value is not an integer or out of range\r\n"
8816        );
8817        // A WEIGHTS list that is not one per key is a syntax error, and a
8818        // weight that is not a number gets a sentence of its own.
8819        assert_eq!(
8820            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8821            "-ERR syntax error\r\n"
8822        );
8823        assert_eq!(
8824            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8825            "-ERR weight value is not a float\r\n"
8826        );
8827        assert_eq!(
8828            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8829            "-ERR syntax error\r\n"
8830        );
8831    }
8832
8833    /// The three store forms, which answer a count and take no WITHSCORES.
8834    #[test]
8835    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8836        let mut f = Fixture::new();
8837        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8838        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8839        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8840        assert_eq!(
8841            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8842            "*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"
8843        );
8844        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8845        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8846        // An empty result deletes the destination rather than leaving an empty
8847        // sorted set, because an empty one does not exist.
8848        assert_eq!(
8849            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8850            ":0\r\n"
8851        );
8852        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8853        // The destination is allowed to name its own source.
8854        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8855        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8856        for cmd in [
8857            &[
8858                b"ZUNIONSTORE".as_slice(),
8859                b"d",
8860                b"2",
8861                b"z",
8862                b"y",
8863                b"WITHSCORES",
8864            ][..],
8865            &[
8866                b"ZDIFFSTORE",
8867                b"d",
8868                b"2",
8869                b"z",
8870                b"y",
8871                b"WEIGHTS",
8872                b"1",
8873                b"1",
8874            ],
8875        ] {
8876            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8877        }
8878    }
8879
8880    /// `ZINTERCARD`, which counts without building anything.
8881    #[test]
8882    fn intercard_counts_and_stops_at_its_limit() {
8883        let mut f = Fixture::new();
8884        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8885        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8886        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8887        // A limit of zero is no limit, which is Redis's reading of it.
8888        assert_eq!(
8889            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8890            ":2\r\n"
8891        );
8892        assert_eq!(
8893            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8894            ":1\r\n"
8895        );
8896        // A negative limit and a limit that is not a number at all get the same
8897        // sentence, which looks like a mistake in Redis and is copied as one.
8898        let bad = "-ERR LIMIT can't be negative\r\n";
8899        assert_eq!(
8900            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8901            bad
8902        );
8903        assert_eq!(
8904            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8905            bad
8906        );
8907        for cmd in [
8908            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8909            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8910            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8911        ] {
8912            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8913        }
8914    }
8915
8916    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
8917    #[test]
8918    fn a_draw_answers_one_member_or_an_array_of_them() {
8919        let mut f = Fixture::new();
8920        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8921        // No count is one member or a nil, a count is an array that may be
8922        // empty, and those are two reply types the client has to tell apart.
8923        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8924        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8925        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8926        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8927        // A positive count draws without replacement, so a count over the size
8928        // answers the whole set and never a member twice.
8929        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8930        assert!(all.starts_with("*3\r\n"), "{all}");
8931        for m in ["a", "b", "c"] {
8932            assert!(all.contains(m), "{all}");
8933        }
8934        // A negative one draws with replacement and answers exactly as many as
8935        // it was asked for, whatever the size of the set.
8936        assert!(
8937            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8938            "five draws with replacement"
8939        );
8940        assert!(
8941            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8942                .starts_with("*4\r\n"),
8943            "two pairs, flat on RESP2"
8944        );
8945        f.out = Out::new(Proto::Resp3);
8946        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8947        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8948        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8949        f.out = Out::new(Proto::Resp2);
8950        assert_eq!(
8951            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8952            "-ERR syntax error\r\n"
8953        );
8954        assert_eq!(
8955            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8956            "-ERR value is not an integer or out of range\r\n"
8957        );
8958    }
8959
8960    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
8961    #[test]
8962    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8963        let mut f = Fixture::new();
8964        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8965        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";
8966        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8967        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8968        assert_eq!(
8969            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8970            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8971        );
8972        assert_eq!(
8973            f.run(&[b"ZSCAN", b"nokey", b"0"]),
8974            "*2\r\n$1\r\n0\r\n*0\r\n"
8975        );
8976        // A score stays a bulk string on RESP3, which is the one place the two
8977        // protocols agree about a score and everywhere else they do not.
8978        f.out = Out::new(Proto::Resp3);
8979        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8980        f.out = Out::new(Proto::Resp2);
8981        assert_eq!(
8982            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8983            "-ERR NOVALUES option can only be used in HSCAN\r\n"
8984        );
8985        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8986        assert_eq!(
8987            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8988            "-ERR syntax error\r\n"
8989        );
8990    }
8991
8992    /// The count is what decides the shape, and its value is not.
8993    #[test]
8994    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8995        let mut f = Fixture::new();
8996        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8997        // No count, so one flat pair, and the score is a bulk string on RESP2.
8998        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8999        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
9000        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
9001        // A count, so pairs, and on RESP2 they are flattened into one run.
9002        assert_eq!(
9003            f.run(&[b"ZPOPMIN", b"z", b"2"]),
9004            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
9005        );
9006        // An empty array rather than a null, which is where a sorted set pop and
9007        // a list pop part company, and the same answer a count of zero gives.
9008        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
9009        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
9010        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
9011        // The last member takes the key with it.
9012        assert_eq!(
9013            f.run(&[b"ZPOPMIN", b"z", b"9"]),
9014            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
9015        );
9016        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
9017
9018        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
9019        f.out = Out::new(Proto::Resp3);
9020        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
9021        assert_eq!(
9022            f.run(&[b"ZPOPMIN", b"z", b"1"]),
9023            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
9024        );
9025        f.out = Out::new(Proto::Resp2);
9026        // Both of these are the range error rather than the usual sentence about
9027        // integers, which is the odd answer and so the one worth copying.
9028        let bad = "-ERR value is out of range, must be positive\r\n";
9029        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
9030        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
9031        assert_eq!(
9032            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
9033            "-ERR syntax error\r\n"
9034        );
9035    }
9036
9037    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
9038    #[test]
9039    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
9040        let mut f = Fixture::new();
9041        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
9042        assert_eq!(
9043            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
9044            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
9045        );
9046        // Nested on RESP2 as well, because the key name is already in front of
9047        // the pairs and there is nothing left to flatten into.
9048        assert_eq!(
9049            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
9050            "*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"
9051        );
9052        // A null array and not a null, the same as LMPOP.
9053        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
9054        f.out = Out::new(Proto::Resp3);
9055        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
9056        f.out = Out::new(Proto::Resp2);
9057        let numkeys = "-ERR numkeys should be greater than 0\r\n";
9058        for bad in [
9059            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
9060            &[b"ZMPOP", b"-1", b"z", b"MIN"],
9061            &[b"ZMPOP", b"x", b"z", b"MIN"],
9062        ] {
9063            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
9064        }
9065        let count = "-ERR count should be greater than 0\r\n";
9066        for bad in [
9067            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
9068            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
9069            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
9070        ] {
9071            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
9072        }
9073        let syntax = "-ERR syntax error\r\n";
9074        for bad in [
9075            // Two keys named and one given, so the word that should have been
9076            // the direction is a key and there is no direction left.
9077            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
9078            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
9079            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
9080            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
9081        ] {
9082            assert_eq!(f.run(bad), syntax, "{bad:?}");
9083        }
9084    }
9085
9086    /// The three that wait, when there is something there and they do not have
9087    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
9088    #[test]
9089    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
9090        let mut f = Fixture::new();
9091        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
9092        assert_eq!(
9093            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
9094            (
9095                Flow::Continue,
9096                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
9097            )
9098        );
9099        assert_eq!(
9100            f.run(&[b"BZPOPMAX", b"z", b"0"]),
9101            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
9102        );
9103        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
9104        assert_eq!(
9105            f.run(&[
9106                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
9107            ]),
9108            "*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"
9109        );
9110        f.out = Out::new(Proto::Resp3);
9111        assert_eq!(
9112            f.run(&[b"BZPOPMIN", b"z", b"0"]),
9113            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
9114        );
9115        f.out = Out::new(Proto::Resp2);
9116        // Nothing to take, so the client is parked and nothing was written.
9117        assert_eq!(
9118            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
9119            (Flow::Block, String::new())
9120        );
9121        assert_eq!(
9122            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
9123            (Flow::Block, String::new())
9124        );
9125        // The timeout is read before the key count, so this complains about the
9126        // timeout and not about the count.
9127        assert_eq!(
9128            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
9129            "-ERR timeout is not a float or out of range\r\n"
9130        );
9131        assert_eq!(
9132            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
9133            "-ERR numkeys should be greater than 0\r\n"
9134        );
9135        assert_eq!(
9136            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
9137            "-ERR timeout is negative\r\n"
9138        );
9139    }
9140
9141    /// A parked sorted set client is served by whatever puts a member under one
9142    /// of its keys, and is not served by something of another type landing
9143    /// there.
9144    #[test]
9145    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
9146        let mut f = Fixture::new();
9147        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
9148        assert_eq!(f.server.parked(), 1);
9149        // A string under the key is not what it asked for, so it stays parked
9150        // rather than being handed a WRONGTYPE on a command that was accepted.
9151        f.run(&[b"SET", b"z", b"v"]);
9152        let mut out = Out::new(Proto::Resp2);
9153        assert!(!f.server.serve_waiter(7, 0, &mut out));
9154        assert!(out.as_slice().is_empty());
9155        f.run(&[b"DEL", b"z"]);
9156        f.run(&[b"ZADD", b"z", b"5", b"m"]);
9157        assert!(f.server.serve_waiter(7, 0, &mut out));
9158        assert_eq!(
9159            core::str::from_utf8(out.as_slice()).expect("ascii"),
9160            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
9161        );
9162        // And the member is gone, which is what makes a queue of workers on a
9163        // sorted set work at all.
9164        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
9165    }
9166
9167    #[test]
9168    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
9169        let mut f = Fixture::new();
9170        f.run(&[b"SET", b"s", b"v"]);
9171        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9172        for cmd in [
9173            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
9174            &[b"ZINCRBY", b"s", b"1", b"a"],
9175            &[b"ZCARD", b"s"],
9176            &[b"ZSCORE", b"s", b"a"],
9177            &[b"ZMSCORE", b"s", b"a"],
9178            &[b"ZREM", b"s", b"a"],
9179            &[b"ZRANK", b"s", b"a"],
9180            &[b"ZREVRANK", b"s", b"a"],
9181            &[b"ZCOUNT", b"s", b"1", b"2"],
9182            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
9183            &[b"ZRANGE", b"s", b"0", b"-1"],
9184            &[b"ZREVRANGE", b"s", b"0", b"-1"],
9185            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
9186            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
9187            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
9188            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
9189            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
9190            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
9191            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
9192            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
9193            &[b"ZUNION", b"1", b"s"],
9194            &[b"ZINTER", b"1", b"s"],
9195            &[b"ZDIFF", b"1", b"s"],
9196            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
9197            &[b"ZINTERSTORE", b"d", b"1", b"s"],
9198            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
9199            &[b"ZINTERCARD", b"1", b"s"],
9200            &[b"ZRANDMEMBER", b"s"],
9201            &[b"ZSCAN", b"s", b"0"],
9202            &[b"ZPOPMIN", b"s"],
9203            &[b"ZPOPMAX", b"s", b"2"],
9204            &[b"ZMPOP", b"1", b"s", b"MIN"],
9205            &[b"BZPOPMIN", b"s", b"0"],
9206            &[b"BZPOPMAX", b"s", b"0"],
9207            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
9208        ] {
9209            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
9210        }
9211        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
9212    }
9213
9214    /// The same churn the set, the string and the list get, because a sorted
9215    /// set that leaks a tree node per add looks exactly like one that does not
9216    /// until it has run for an afternoon.
9217    #[test]
9218    fn churning_sorted_sets_does_not_grow_the_server() {
9219        let mut f = Fixture::new();
9220        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
9221        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
9222        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
9223        for i in 0..200 {
9224            args.push(&scores[i]);
9225            args.push(&members[i]);
9226        }
9227
9228        f.run(&args);
9229        f.run(&[b"DEL", b"z"]);
9230        f.server.compact_step();
9231        let after_first = f.server.memory_bytes();
9232
9233        for _ in 0..200 {
9234            f.run(&args);
9235            f.run(&[b"DEL", b"z"]);
9236            f.server.compact_step();
9237        }
9238        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9239        assert!(
9240            f.server.memory_bytes() <= after_first * 2,
9241            "held {} after two hundred passes against {after_first} after one",
9242            f.server.memory_bytes()
9243        );
9244    }
9245
9246    // ------------------------------------------------------------------- geo
9247
9248    /// The three places every Redis geo example uses, and one more.
9249    ///
9250    /// Every reply this section asserts on came off a running 8.10.1 with these
9251    /// three loaded, byte for byte, including the number of digits in a
9252    /// coordinate and the four places on a distance.
9253    fn sicily(f: &mut Fixture) {
9254        f.run(&[
9255            b"GEOADD",
9256            b"Sicily",
9257            b"13.361389",
9258            b"38.115556",
9259            b"Palermo",
9260            b"15.087269",
9261            b"37.502669",
9262            b"Catania",
9263        ]);
9264        f.run(&[
9265            b"GEOADD",
9266            b"Sicily",
9267            b"13.583333",
9268            b"37.316667",
9269            b"Agrigento",
9270        ]);
9271    }
9272
9273    #[test]
9274    fn places_go_in_as_scores_and_come_back_as_positions() {
9275        let mut f = Fixture::new();
9276        assert_eq!(
9277            f.run(&[
9278                b"GEOADD",
9279                b"Sicily",
9280                b"13.361389",
9281                b"38.115556",
9282                b"Palermo",
9283                b"15.087269",
9284                b"37.502669",
9285                b"Catania"
9286            ]),
9287            ":2\r\n"
9288        );
9289        // A geo key is a sorted set and says so, which is not an implementation
9290        // detail either: a client removes a place with ZREM and counts them
9291        // with ZCARD, and the score is the number a real server stores.
9292        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
9293        assert_eq!(
9294            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
9295            "$16\r\n3479099956230698\r\n"
9296        );
9297        assert_eq!(
9298            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
9299            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
9300        );
9301        assert_eq!(
9302            f.run(&[
9303                b"GEOHASH",
9304                b"Sicily",
9305                b"Palermo",
9306                b"Catania",
9307                b"NonExisting"
9308            ]),
9309            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
9310        );
9311        // A key that is not there is an empty one, and the two nulls are not
9312        // the same null: GEOPOS answers the array one and GEOHASH the string
9313        // one, which a RESP2 client can tell apart.
9314        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
9315        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
9316    }
9317
9318    #[test]
9319    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
9320        let mut f = Fixture::new();
9321        sicily(&mut f);
9322        assert_eq!(
9323            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
9324            "$11\r\n166274.1516\r\n"
9325        );
9326        assert_eq!(
9327            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
9328            "$8\r\n166.2742\r\n"
9329        );
9330        assert_eq!(
9331            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
9332            "$8\r\n103.3182\r\n"
9333        );
9334        // A member that is not there and a key that is not there are the same
9335        // nil, and the unit is read before the key is looked up, so a bad unit
9336        // on a missing key is still an error.
9337        assert_eq!(
9338            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
9339            "$-1\r\n"
9340        );
9341        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
9342        assert_eq!(
9343            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
9344            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
9345        );
9346        assert_eq!(
9347            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
9348            "-ERR syntax error\r\n"
9349        );
9350    }
9351
9352    #[test]
9353    fn a_search_finds_what_is_inside_it_nearest_first() {
9354        let mut f = Fixture::new();
9355        sicily(&mut f);
9356        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
9357        assert_eq!(
9358            f.run(&[
9359                b"GEOSEARCH",
9360                b"Sicily",
9361                b"FROMLONLAT",
9362                b"15",
9363                b"37",
9364                b"BYRADIUS",
9365                b"200",
9366                b"km",
9367                b"ASC"
9368            ]),
9369            all
9370        );
9371        // The older spelling of the same search, which is the same nine boxes
9372        // and the same order.
9373        assert_eq!(
9374            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
9375            all
9376        );
9377        assert_eq!(
9378            f.run(&[
9379                b"GEORADIUS_RO",
9380                b"Sicily",
9381                b"15",
9382                b"37",
9383                b"200",
9384                b"km",
9385                b"ASC"
9386            ]),
9387            all
9388        );
9389        // A count with no ordering means the nearest ones, so DESC has to be
9390        // asked for to get the far end.
9391        assert_eq!(
9392            f.run(&[
9393                b"GEORADIUS",
9394                b"Sicily",
9395                b"15",
9396                b"37",
9397                b"200",
9398                b"km",
9399                b"DESC",
9400                b"COUNT",
9401                b"1"
9402            ]),
9403            "*1\r\n$7\r\nPalermo\r\n"
9404        );
9405        assert_eq!(
9406            f.run(&[
9407                b"GEORADIUS",
9408                b"Sicily",
9409                b"15",
9410                b"37",
9411                b"200",
9412                b"km",
9413                b"COUNT",
9414                b"1"
9415            ]),
9416            "*1\r\n$7\r\nCatania\r\n"
9417        );
9418        // Nothing inside a kilometre of that point, and nothing in a key that
9419        // is not there, and both are the empty array rather than an error.
9420        let empty = "*0\r\n";
9421        assert_eq!(
9422            f.run(&[
9423                b"GEOSEARCH",
9424                b"Sicily",
9425                b"FROMLONLAT",
9426                b"15",
9427                b"37",
9428                b"BYRADIUS",
9429                b"1",
9430                b"km"
9431            ]),
9432            empty
9433        );
9434        assert_eq!(
9435            f.run(&[
9436                b"GEOSEARCH",
9437                b"nokey",
9438                b"FROMLONLAT",
9439                b"15",
9440                b"37",
9441                b"BYRADIUS",
9442                b"1",
9443                b"km"
9444            ]),
9445            empty
9446        );
9447        assert_eq!(
9448            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
9449            empty
9450        );
9451    }
9452
9453    #[test]
9454    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
9455        let mut f = Fixture::new();
9456        sicily(&mut f);
9457        assert_eq!(
9458            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
9459            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9460        );
9461        // The member itself is nothing away from itself, which is where the
9462        // fixed point writer's zero shows up on the wire.
9463        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";
9464        assert_eq!(
9465            f.run(&[
9466                b"GEORADIUSBYMEMBER_RO",
9467                b"Sicily",
9468                b"Agrigento",
9469                b"100",
9470                b"km",
9471                b"WITHDIST"
9472            ]),
9473            with_dist
9474        );
9475        assert_eq!(
9476            f.run(&[
9477                b"GEOSEARCH",
9478                b"Sicily",
9479                b"FROMMEMBER",
9480                b"Agrigento",
9481                b"BYRADIUS",
9482                b"100",
9483                b"km",
9484                b"ASC",
9485                b"WITHDIST"
9486            ]),
9487            with_dist
9488        );
9489        assert_eq!(
9490            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
9491            "-ERR could not decode requested zset member\r\n"
9492        );
9493    }
9494
9495    #[test]
9496    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
9497        let mut f = Fixture::new();
9498        sicily(&mut f);
9499        // Three options asked for, so each result is a four element array of
9500        // the member, the distance, the hash and a pair. The order of the three
9501        // is Redis's and not the order they were written in the command.
9502        assert_eq!(
9503            f.run(&[
9504                b"GEOSEARCH",
9505                b"Sicily",
9506                b"FROMLONLAT",
9507                b"15",
9508                b"37",
9509                b"BYBOX",
9510                b"400",
9511                b"400",
9512                b"km",
9513                b"ASC",
9514                b"WITHCOORD",
9515                b"WITHDIST",
9516                b"WITHHASH"
9517            ]),
9518            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
9519             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
9520             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
9521             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
9522             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
9523             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
9524        );
9525    }
9526
9527    #[test]
9528    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
9529        let mut f = Fixture::new();
9530        sicily(&mut f);
9531        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
9532                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
9533                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
9534        assert_eq!(
9535            f.run(&[
9536                b"GEOSEARCHSTORE",
9537                b"dst",
9538                b"Sicily",
9539                b"FROMLONLAT",
9540                b"15",
9541                b"37",
9542                b"BYRADIUS",
9543                b"200",
9544                b"km",
9545                b"ASC"
9546            ]),
9547            ":3\r\n"
9548        );
9549        assert_eq!(
9550            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
9551            hashes
9552        );
9553        // The same again through the older spelling, which stores the same
9554        // scores, so a key written by either is a geo key.
9555        assert_eq!(
9556            f.run(&[
9557                b"GEORADIUS",
9558                b"Sicily",
9559                b"15",
9560                b"37",
9561                b"200",
9562                b"km",
9563                b"STORE",
9564                b"dst3"
9565            ]),
9566            ":3\r\n"
9567        );
9568        assert_eq!(
9569            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
9570            hashes
9571        );
9572        // STOREDIST stores the distance in the search unit instead, and those
9573        // are full doubles rather than the four places WITHDIST writes. The
9574        // numbers on the right are what 8.10.1 stored for this search, and they
9575        // are compared with a tolerance rather than byte for byte because the
9576        // last bit of a haversine is the platform's sin, cos and asin: this
9577        // machine and that one disagree in the sixteenth digit, and so do two
9578        // Redis builds. Everything a client actually reads back is four places
9579        // and is asserted exactly above.
9580        assert_eq!(
9581            f.run(&[
9582                b"GEOSEARCHSTORE",
9583                b"dst2",
9584                b"Sicily",
9585                b"FROMLONLAT",
9586                b"15",
9587                b"37",
9588                b"BYRADIUS",
9589                b"200",
9590                b"km",
9591                b"ASC",
9592                b"STOREDIST"
9593            ]),
9594            ":3\r\n"
9595        );
9596        for (member, want) in [
9597            ("Catania", 56.441_257_870_158_19),
9598            ("Agrigento", 130.423_487_067_147_14),
9599            ("Palermo", 190.442_429_847_757_92),
9600        ] {
9601            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
9602            let got: f64 = reply
9603                .trim_start_matches(|c: char| c != '\n')
9604                .trim()
9605                .parse()
9606                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9607            assert!(
9608                (got - want).abs() < 1e-9,
9609                "{member} scored {got} not {want}"
9610            );
9611        }
9612        // The order they went in is the order the scores put them in, which is
9613        // the point of storing the distance rather than the hash.
9614        assert_eq!(
9615            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9616            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9617        );
9618        // A search that finds nothing takes the destination with it rather than
9619        // leaving what was there, and a source key that is not there is a
9620        // search that finds nothing.
9621        assert_eq!(
9622            f.run(&[
9623                b"GEOSEARCHSTORE",
9624                b"dst",
9625                b"nokey",
9626                b"FROMLONLAT",
9627                b"15",
9628                b"37",
9629                b"BYRADIUS",
9630                b"200",
9631                b"km"
9632            ]),
9633            ":0\r\n"
9634        );
9635        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9636    }
9637
9638    #[test]
9639    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9640        let mut f = Fixture::new();
9641        sicily(&mut f);
9642        // XX on a member that is already where it is changes nothing, and NX on
9643        // one that is there refuses to move it.
9644        assert_eq!(
9645            f.run(&[
9646                b"GEOADD",
9647                b"Sicily",
9648                b"XX",
9649                b"CH",
9650                b"13.361389",
9651                b"38.115556",
9652                b"Palermo"
9653            ]),
9654            ":0\r\n"
9655        );
9656        assert_eq!(
9657            f.run(&[
9658                b"GEOADD",
9659                b"Sicily",
9660                b"NX",
9661                b"13.361389",
9662                b"38.9",
9663                b"Palermo"
9664            ]),
9665            ":0\r\n"
9666        );
9667        assert_eq!(
9668            f.run(&[
9669                b"GEOADD",
9670                b"Sicily",
9671                b"CH",
9672                b"13.361389",
9673                b"38.9",
9674                b"Palermo"
9675            ]),
9676            ":1\r\n"
9677        );
9678        // Out of range, and nothing is stored: the whole call is refused rather
9679        // than the good pairs going in and the bad one stopping it.
9680        assert_eq!(
9681            f.run(&[
9682                b"GEOADD",
9683                b"new",
9684                b"13.361389",
9685                b"38.115556",
9686                b"here",
9687                b"181",
9688                b"38",
9689                b"there"
9690            ]),
9691            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9692        );
9693        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9694        assert_eq!(
9695            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9696            "-ERR value is not a valid float\r\n"
9697        );
9698        // The count of triples is checked before the two gates are, and a call
9699        // with no triples at all reaches the same sentence.
9700        assert_eq!(
9701            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9702            "-ERR syntax error\r\n"
9703        );
9704        assert_eq!(
9705            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9706            "-ERR syntax error\r\n"
9707        );
9708        assert_eq!(
9709            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9710            "-ERR syntax error\r\n"
9711        );
9712        assert_eq!(
9713            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9714            "-ERR wrong number of arguments for 'geoadd' command\r\n"
9715        );
9716    }
9717
9718    /// The sentences a search answers, which are its contract as much as the
9719    /// results are.
9720    #[test]
9721    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9722        let mut f = Fixture::new();
9723        sicily(&mut f);
9724        let cases: &[(&[&[u8]], &str)] = &[
9725            (
9726                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9727                "-ERR need numeric radius\r\n",
9728            ),
9729            (
9730                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9731                "-ERR radius cannot be negative\r\n",
9732            ),
9733            (
9734                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9735                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9736            ),
9737            (
9738                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9739                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9740            ),
9741            (
9742                &[
9743                    b"GEOSEARCH",
9744                    b"Sicily",
9745                    b"FROMLONLAT",
9746                    b"15",
9747                    b"37",
9748                    b"BYBOX",
9749                    b"x",
9750                    b"1",
9751                    b"km",
9752                ],
9753                "-ERR need numeric width\r\n",
9754            ),
9755            (
9756                &[
9757                    b"GEOSEARCH",
9758                    b"Sicily",
9759                    b"FROMLONLAT",
9760                    b"15",
9761                    b"37",
9762                    b"BYBOX",
9763                    b"1",
9764                    b"y",
9765                    b"km",
9766                ],
9767                "-ERR need numeric height\r\n",
9768            ),
9769            (
9770                &[
9771                    b"GEOSEARCH",
9772                    b"Sicily",
9773                    b"FROMLONLAT",
9774                    b"15",
9775                    b"37",
9776                    b"BYBOX",
9777                    b"-1",
9778                    b"1",
9779                    b"km",
9780                ],
9781                "-ERR height or width cannot be negative\r\n",
9782            ),
9783            (
9784                &[
9785                    b"GEOSEARCH",
9786                    b"Sicily",
9787                    b"FROMLONLAT",
9788                    b"15",
9789                    b"37",
9790                    b"BYRADIUS",
9791                    b"1",
9792                    b"km",
9793                    b"ANY",
9794                ],
9795                "-ERR the ANY argument requires COUNT argument\r\n",
9796            ),
9797            (
9798                &[
9799                    b"GEOSEARCH",
9800                    b"Sicily",
9801                    b"FROMLONLAT",
9802                    b"15",
9803                    b"37",
9804                    b"BYRADIUS",
9805                    b"1",
9806                    b"km",
9807                    b"COUNT",
9808                    b"0",
9809                ],
9810                "-ERR COUNT must be > 0\r\n",
9811            ),
9812            (
9813                &[
9814                    b"GEOSEARCH",
9815                    b"Sicily",
9816                    b"BYRADIUS",
9817                    b"1",
9818                    b"km",
9819                    b"BYBOX",
9820                    b"1",
9821                    b"1",
9822                    b"km",
9823                ],
9824                "-ERR syntax error\r\n",
9825            ),
9826            (
9827                &[
9828                    b"GEOSEARCH",
9829                    b"Sicily",
9830                    b"FROMMEMBER",
9831                    b"Palermo",
9832                    b"FROMLONLAT",
9833                    b"1",
9834                    b"2",
9835                    b"BYRADIUS",
9836                    b"1",
9837                    b"km",
9838                ],
9839                "-ERR syntax error\r\n",
9840            ),
9841            // The two options a GEOSEARCH cannot leave out, each with its own
9842            // sentence, and the command quoted the way the client spelled it.
9843            (
9844                &[
9845                    b"geosearch",
9846                    b"Sicily",
9847                    b"BYRADIUS",
9848                    b"1",
9849                    b"km",
9850                    b"ASC",
9851                    b"WITHDIST",
9852                ],
9853                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9854            ),
9855            (
9856                &[
9857                    b"GEOSEARCH",
9858                    b"Sicily",
9859                    b"FROMLONLAT",
9860                    b"15",
9861                    b"37",
9862                    b"ASC",
9863                    b"WITHDIST",
9864                ],
9865                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9866            ),
9867            // A store cannot also be asked for the distance, and the two
9868            // families name themselves differently in the same sentence.
9869            (
9870                &[
9871                    b"GEOSEARCHSTORE",
9872                    b"d",
9873                    b"Sicily",
9874                    b"FROMLONLAT",
9875                    b"15",
9876                    b"37",
9877                    b"BYRADIUS",
9878                    b"1",
9879                    b"km",
9880                    b"WITHCOORD",
9881                ],
9882                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9883            ),
9884            (
9885                &[
9886                    b"GEORADIUS",
9887                    b"Sicily",
9888                    b"15",
9889                    b"37",
9890                    b"1",
9891                    b"km",
9892                    b"WITHDIST",
9893                    b"STORE",
9894                    b"d",
9895                ],
9896                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9897            ),
9898            // The read only forms have no store at all, so the word is a stray
9899            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
9900            (
9901                &[
9902                    b"GEORADIUS_RO",
9903                    b"Sicily",
9904                    b"15",
9905                    b"37",
9906                    b"1",
9907                    b"km",
9908                    b"STORE",
9909                    b"d",
9910                ],
9911                "-ERR syntax error\r\n",
9912            ),
9913            (
9914                &[
9915                    b"GEOSEARCH",
9916                    b"Sicily",
9917                    b"FROMLONLAT",
9918                    b"15",
9919                    b"37",
9920                    b"BYRADIUS",
9921                    b"1",
9922                    b"km",
9923                    b"STOREDIST",
9924                ],
9925                "-ERR syntax error\r\n",
9926            ),
9927        ];
9928        for (parts, want) in cases {
9929            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9930        }
9931    }
9932
9933    /// A wrong type wins over a bad argument, because the key is looked up
9934    /// first, and every one of the ten says the same thing about it.
9935    #[test]
9936    fn every_geo_command_says_wrongtype() {
9937        let mut f = Fixture::new();
9938        f.run(&[b"SET", b"s", b"v"]);
9939        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9940        let cases: &[&[&[u8]]] = &[
9941            &[b"GEOADD", b"s", b"13", b"38", b"m"],
9942            &[b"GEOPOS", b"s", b"m"],
9943            &[b"GEOHASH", b"s", b"m"],
9944            &[b"GEODIST", b"s", b"a", b"b"],
9945            &[
9946                b"GEOSEARCH",
9947                b"s",
9948                b"FROMLONLAT",
9949                b"15",
9950                b"37",
9951                b"BYRADIUS",
9952                b"1",
9953                b"km",
9954            ],
9955            &[
9956                b"GEOSEARCHSTORE",
9957                b"d",
9958                b"s",
9959                b"FROMLONLAT",
9960                b"15",
9961                b"37",
9962                b"BYRADIUS",
9963                b"1",
9964                b"km",
9965            ],
9966            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9967            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9968            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9969            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9970        ];
9971        for case in cases {
9972            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9973        }
9974        // And it wins over an argument that will not parse, which is the whole
9975        // reason the lookup comes first.
9976        assert_eq!(
9977            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9978            wrong
9979        );
9980    }
9981
9982    // ----------------------------------------------------------------- array
9983
9984    #[test]
9985    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9986        let mut f = Fixture::new();
9987        // Three consecutive positions from a high index, and the reply is how
9988        // many of them were empty before rather than how many were written.
9989        assert_eq!(
9990            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9991            ":3\r\n"
9992        );
9993        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9994        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9995        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9996        // A hole and a key that is not there are the same answer.
9997        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9998        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9999        assert_eq!(
10000            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
10001            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
10002        );
10003        // Scattered pairs in one command, last write wins within it.
10004        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
10005        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
10006    }
10007
10008    /// The two numbers an array reports are not the same number, and one of
10009    /// them does not fit a signed integer.
10010    #[test]
10011    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
10012        let mut f = Fixture::new();
10013        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
10014        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
10015        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
10016        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
10017        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
10018        // Deleting in the middle leaves the high water mark where it was.
10019        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
10020        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
10021        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
10022
10023        // The top of the space is addressable, and its length is a number with
10024        // bit sixty three set, so the reply has to be unsigned or it comes back
10025        // negative.
10026        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
10027        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
10028        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
10029        // And one past it does not exist, so a write that would reach it fails
10030        // before any of it lands.
10031        assert_eq!(
10032            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
10033            "-ERR array index overflow\r\n"
10034        );
10035        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
10036    }
10037
10038    /// One reply per position and not one per element, which is the whole
10039    /// reason the range is capped.
10040    #[test]
10041    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
10042        let mut f = Fixture::new();
10043        f.run(&[b"ARSET", b"a", b"1", b"x"]);
10044        assert_eq!(
10045            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
10046            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
10047        );
10048        // The two ends may come in either order, and the answer is reversed
10049        // rather than empty.
10050        assert_eq!(
10051            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
10052            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
10053        );
10054        // A key that is not there reads like an array of nothing but holes.
10055        assert_eq!(
10056            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
10057            "*2\r\n$-1\r\n$-1\r\n"
10058        );
10059        // A range wider than a million positions is refused and not trimmed,
10060        // because against a missing key it is a request for as many nulls as
10061        // the range is wide.
10062        assert_eq!(
10063            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
10064            "-ERR range exceeds maximum of 1000000 items\r\n"
10065        );
10066    }
10067
10068    /// Every index in the argument list is read before the key is touched, so
10069    /// a bad one at the end leaves nothing half written.
10070    #[test]
10071    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
10072        let mut f = Fixture::new();
10073        assert_eq!(
10074            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
10075            "-ERR invalid array index\r\n"
10076        );
10077        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
10078        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
10079        assert_eq!(
10080            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
10081            "-ERR invalid array index\r\n"
10082        );
10083        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
10084        // An index is unsigned here, so the numbers a list would take are not
10085        // the last element, they are errors.
10086        assert_eq!(
10087            f.run(&[b"ARGET", b"a", b"-1"]),
10088            "-ERR invalid array index\r\n"
10089        );
10090        // And a pair list with an odd tail is an arity error rather than a
10091        // syntax one.
10092        assert_eq!(
10093            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
10094            "-ERR wrong number of arguments for 'armset' command\r\n"
10095        );
10096        assert_eq!(
10097            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
10098            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
10099        );
10100    }
10101
10102    #[test]
10103    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
10104        let mut f = Fixture::new();
10105        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
10106        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
10107        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
10108        // Two ranges in one command, and the second one covers the whole space
10109        // without walking it.
10110        assert_eq!(
10111            f.run(&[
10112                b"ARDELRANGE",
10113                b"a",
10114                b"100",
10115                b"200",
10116                b"0",
10117                b"18446744073709551614"
10118            ]),
10119            ":2\r\n"
10120        );
10121        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
10122        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
10123        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
10124    }
10125
10126    /// A value goes out as the bytes it came in as, whichever of the three ways
10127    /// the array found to store it.
10128    #[test]
10129    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
10130        let mut f = Fixture::new();
10131        let long = vec![b'v'; 200];
10132        f.run(&[
10133            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
10134            b"short", b"5", &long, b"6", b"-0",
10135        ]);
10136        // 42 is an integer, 007 is not one because it does not print back the
10137        // same, 3.5 survives a double and 3.14 does not, and the last two are a
10138        // word packed string and a blob.
10139        assert_eq!(
10140            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
10141            format!(
10142                "*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",
10143                String::from_utf8_lossy(&long)
10144            )
10145        );
10146    }
10147
10148    #[test]
10149    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
10150        let mut f = Fixture::new();
10151        f.run(&[b"ARSET", b"a", b"0", b"x"]);
10152        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
10153        assert_eq!(
10154            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
10155            "$12\r\nsliced-array\r\n"
10156        );
10157        // And it is a body like any other, so the key commands work on it.
10158        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
10159        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
10160        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
10161        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
10162        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
10163        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
10164    }
10165
10166    #[test]
10167    fn every_array_command_refuses_a_key_holding_something_else() {
10168        let mut f = Fixture::new();
10169        f.run(&[b"SET", b"s", b"v"]);
10170        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10171        for cmd in [
10172            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
10173            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
10174            &[b"ARGET".as_ref(), b"s", b"0"][..],
10175            &[b"ARMGET".as_ref(), b"s", b"0"][..],
10176            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
10177            &[b"ARLEN".as_ref(), b"s"][..],
10178            &[b"ARCOUNT".as_ref(), b"s"][..],
10179            &[b"ARDEL".as_ref(), b"s", b"0"][..],
10180            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
10181            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
10182            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
10183            &[b"ARNEXT".as_ref(), b"s"][..],
10184            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
10185            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
10186            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
10187            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
10188            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
10189            &[b"ARINFO".as_ref(), b"s"][..],
10190        ] {
10191            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
10192        }
10193    }
10194
10195    /// Two of the array commands look the key up before they read the index and
10196    /// the rest read the index first, so the same broken argument gets two
10197    /// different errors depending on which command it went to.
10198    #[test]
10199    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
10200        let mut f = Fixture::new();
10201        f.run(&[b"SET", b"s", b"v"]);
10202        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10203        let bad = "-ERR invalid array index\r\n";
10204        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
10205        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
10206        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
10207        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
10208        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
10209        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
10210        // And on a key that is an array the index is just an index.
10211        f.run(&[b"ARSET", b"a", b"0", b"x"]);
10212        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
10213        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
10214    }
10215
10216    #[test]
10217    fn an_append_follows_a_cursor_the_client_can_move() {
10218        let mut f = Fixture::new();
10219        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
10220        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
10221        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
10222        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
10223        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
10224
10225        // A seek says where the next one goes, and a missing key has no cursor
10226        // to move and is not created by the asking.
10227        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
10228        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
10229        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
10230        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
10231        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
10232        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
10233        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
10234
10235        // The top of the space is the one index only ARSEEK will take, and it
10236        // leaves the cursor with nowhere to go.
10237        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
10238        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
10239        assert_eq!(
10240            f.run(&[b"ARINSERT", b"a", b"x"]),
10241            "-ERR insert index overflow\r\n"
10242        );
10243        assert_eq!(
10244            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
10245            "-ERR invalid array index\r\n"
10246        );
10247    }
10248
10249    #[test]
10250    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
10251        let mut f = Fixture::new();
10252        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
10253        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
10254        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
10255        assert_eq!(
10256            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
10257            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
10258        );
10259        // Growing it after it has wrapped puts the survivors back in the order
10260        // they arrived, which is the whole point of paying for the rebuild.
10261        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
10262        assert_eq!(
10263            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
10264            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
10265        );
10266        // The size is read before the key, so a bad one is a bad size wherever
10267        // it is sent.
10268        assert_eq!(
10269            f.run(&[b"ARRING", b"r", b"0", b"x"]),
10270            "-ERR size must be positive\r\n"
10271        );
10272        assert_eq!(
10273            f.run(&[b"ARRING", b"r", b"big", b"x"]),
10274            "-ERR invalid size\r\n"
10275        );
10276    }
10277
10278    #[test]
10279    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
10280        let mut f = Fixture::new();
10281        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
10282        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
10283        assert_eq!(
10284            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
10285            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
10286        );
10287        assert_eq!(
10288            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
10289            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
10290        );
10291        assert_eq!(
10292            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
10293            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
10294            "more than there is gets what there is"
10295        );
10296        // Nothing asked for is an empty reply, and Redis answers that before it
10297        // has read the option or looked at the key.
10298        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
10299        assert_eq!(
10300            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
10301            "-ERR syntax error\r\n"
10302        );
10303        assert_eq!(
10304            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
10305            "-ERR invalid COUNT\r\n"
10306        );
10307
10308        // With no cursor the tail of the array is the anchor, and a hole inside
10309        // the window is reported as one.
10310        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
10311        assert_eq!(
10312            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
10313            "*2\r\n$-1\r\n$1\r\nz\r\n"
10314        );
10315    }
10316
10317    #[test]
10318    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
10319        let mut f = Fixture::new();
10320        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
10321        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
10322        // The whole index space, which ARGETRANGE refuses and this one answers
10323        // in three visits because holes cost nothing.
10324        assert_eq!(
10325            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
10326            "*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"
10327        );
10328        assert_eq!(
10329            f.run(&[
10330                b"ARSCAN",
10331                b"a",
10332                b"18446744073709551614",
10333                b"0",
10334                b"LIMIT",
10335                b"1"
10336            ]),
10337            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
10338        );
10339        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
10340        assert_eq!(
10341            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
10342            "-ERR LIMIT must be positive\r\n"
10343        );
10344        assert_eq!(
10345            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
10346            "-ERR syntax error\r\n"
10347        );
10348        assert_eq!(
10349            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
10350            "-ERR wrong number of arguments for 'arscan' command\r\n"
10351        );
10352    }
10353
10354    #[test]
10355    fn a_grep_answers_the_indexes_whose_elements_match() {
10356        let mut f = Fixture::new();
10357        assert_eq!(
10358            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
10359            "*0\r\n"
10360        );
10361        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
10362
10363        // The two bounds take the ends of the array as well as an index, and a
10364        // reversed range is walked backwards the way ARSCAN walks one.
10365        assert_eq!(
10366            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
10367            "*3\r\n:0\r\n:1\r\n:2\r\n"
10368        );
10369        assert_eq!(
10370            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
10371            "*3\r\n:2\r\n:1\r\n:0\r\n"
10372        );
10373        assert_eq!(
10374            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
10375            "*2\r\n:1\r\n:2\r\n"
10376        );
10377
10378        // One test each. NOCASE reaches all four of them and it may be written
10379        // after the pattern it applies to.
10380        assert_eq!(
10381            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
10382            "*1\r\n:0\r\n"
10383        );
10384        assert_eq!(
10385            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
10386            "*2\r\n:0\r\n:3\r\n"
10387        );
10388        assert_eq!(
10389            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
10390            "*1\r\n:2\r\n"
10391        );
10392        assert_eq!(
10393            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
10394            "*2\r\n:1\r\n:2\r\n"
10395        );
10396
10397        // OR is the default and AND has to be asked for, and either way the
10398        // last of a repeated option wins.
10399        let both: &[&[u8]] = &[
10400            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
10401        ];
10402        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
10403        assert_eq!(
10404            f.run(&[
10405                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
10406            ]),
10407            "*0\r\n"
10408        );
10409        assert_eq!(
10410            f.run(&[
10411                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
10412            ]),
10413            "*2\r\n:0\r\n:1\r\n"
10414        );
10415
10416        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
10417        // not the positions it had to look at.
10418        assert_eq!(
10419            f.run(&[
10420                b"ARGREP",
10421                b"a",
10422                b"-",
10423                b"+",
10424                b"MATCH",
10425                b"a",
10426                b"WITHVALUES",
10427                b"LIMIT",
10428                b"2"
10429            ]),
10430            "*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"
10431        );
10432        assert_eq!(
10433            f.run(&[
10434                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
10435            ]),
10436            "*1\r\n:3\r\n"
10437        );
10438    }
10439
10440    /// Everything ARGREP refuses, in the order it refuses it.
10441    #[test]
10442    fn a_grep_reports_a_broken_command_the_way_redis_does() {
10443        let mut f = Fixture::new();
10444        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
10445        let syntax = "-ERR syntax error\r\n";
10446
10447        // The bounds are read before the plan, so a bad index beats a bad
10448        // predicate whichever way round the two are written.
10449        assert_eq!(
10450            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
10451            "-ERR invalid array index\r\n"
10452        );
10453        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
10454        // A keyword with nothing after it, and a command that asks for nothing.
10455        assert_eq!(
10456            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
10457            syntax
10458        );
10459        assert_eq!(
10460            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
10461            syntax
10462        );
10463        assert_eq!(
10464            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
10465            syntax,
10466            "a command with no predicate in it at all"
10467        );
10468        assert_eq!(
10469            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
10470            "-ERR LIMIT must be positive\r\n"
10471        );
10472        assert_eq!(
10473            f.run(&[
10474                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
10475            ]),
10476            "-ERR value is not an integer or out of range\r\n"
10477        );
10478        assert_eq!(
10479            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
10480            "-ERR regular expression is empty\r\n"
10481        );
10482        assert_eq!(
10483            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
10484            "-ERR invalid regular expression: Missing ')'\r\n"
10485        );
10486        assert_eq!(
10487            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
10488            "-ERR regular expression backreferences are not supported\r\n"
10489        );
10490        // The arity is minus six, so a predicate keyword with no pattern after
10491        // it is short by one and never reaches the parser.
10492        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
10493        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
10494        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
10495    }
10496
10497    #[test]
10498    fn an_op_reduces_a_range_to_one_number() {
10499        let mut f = Fixture::new();
10500        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
10501        assert_eq!(
10502            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
10503            "$4\r\n-0.5\r\n"
10504        );
10505        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
10506        assert_eq!(
10507            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
10508            "$3\r\n2.5\r\n"
10509        );
10510        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
10511        assert_eq!(
10512            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
10513            ":1\r\n"
10514        );
10515        // An aggregate is written with seventeen significant digits, which is
10516        // Redis's own choice and not what a score comes back as.
10517        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
10518        assert_eq!(
10519            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
10520            "$19\r\n0.30000000000000004\r\n"
10521        );
10522        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
10523        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
10524
10525        // Nothing to work with is a null, and a missing key is a null for the
10526        // aggregates and a zero for the two that count.
10527        f.run(&[b"ARSET", b"w", b"0", b"word"]);
10528        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
10529        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
10530        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
10531
10532        assert_eq!(
10533            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
10534            "-ERR unknown operation\r\n"
10535        );
10536        assert_eq!(
10537            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
10538            "-ERR MATCH requires a value argument\r\n"
10539        );
10540        assert_eq!(
10541            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
10542            "-ERR wrong number of arguments for 'arop' command\r\n"
10543        );
10544    }
10545
10546    #[test]
10547    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
10548        let mut f = Fixture::new();
10549        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
10550        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
10551        let short = f.run(&[b"ARINFO", b"a"]);
10552        assert!(
10553            short.starts_with("*14\r\n"),
10554            "seven pairs on RESP2: {short}"
10555        );
10556        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
10557        assert!(
10558            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
10559            "{short}"
10560        );
10561        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
10562        let full = f.run(&[b"ARINFO", b"a", b"full"]);
10563        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
10564        // Two values one apart are held sparsely, so the dense count is zero and
10565        // the two dense averages have nothing to average.
10566        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
10567        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
10568        assert!(
10569            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
10570            "{full}"
10571        );
10572        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
10573
10574        // On RESP3 the same reply is a map and the averages are doubles.
10575        let mut g = Fixture::new();
10576        g.run(&[b"HELLO", b"3"]);
10577        g.run(&[b"ARINSERT", b"a", b"x"]);
10578        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
10579        assert!(map.starts_with("%12\r\n"), "{map}");
10580        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
10581        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
10582    }
10583
10584    #[test]
10585    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
10586        let mut f = Fixture::new();
10587        // Whole numbers up to two to the sixty second come back as integers,
10588        // and past that the digit generator takes over and uses an exponent.
10589        for (score, want) in [
10590            ("3", "3"),
10591            ("3.5", "3.5"),
10592            ("0.3", "0.3"),
10593            ("1e30", "1e+30"),
10594            ("1e19", "1e+19"),
10595            ("1e-7", "1e-7"),
10596            ("0.000001", "0.000001"),
10597            ("4611686018427387904", "4611686018427387904"),
10598            ("-0", "-0"),
10599        ] {
10600            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
10601            assert_eq!(
10602                f.run(&[b"ZSCORE", b"z", b"m"]),
10603                format!("${}\r\n{want}\r\n", want.len()),
10604                "score {score}"
10605            );
10606        }
10607
10608        // The same bytes on RESP3, where the reply is a double rather than a
10609        // bulk string.
10610        let mut g = Fixture::new();
10611        g.run(&[b"HELLO", b"3"]);
10612        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10613        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10614        // The two float increments are not this printer. They go through
10615        // ld2string in its human mode, which is a fixed point conversion with
10616        // the trailing zeros taken off, so they never write an exponent, and
10617        // they reply with a bulk string on both protocols.
10618        assert_eq!(
10619            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10620            "$31\r\n1000000000000000000000000000000\r\n"
10621        );
10622        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10623        assert_eq!(
10624            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10625            "$20\r\n10000000000000000000\r\n"
10626        );
10627    }
10628
10629    // ----------------------------------------------------------------- graph
10630
10631    #[test]
10632    fn a_node_comes_back_with_the_fields_it_went_in_with() {
10633        let mut f = Fixture::new();
10634        assert_eq!(
10635            f.run(&[
10636                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10637            ]),
10638            ":1\r\n"
10639        );
10640        // The year comes back as the four bytes that were sent and not as a
10641        // number, because every property is text and there is nothing on the
10642        // wire that says which of `1815` and `"1815"` the client meant. The
10643        // fields are in the document's order, which is sorted by name, because
10644        // that is what makes a field lookup a binary search.
10645        assert_eq!(
10646            f.run(&[b"G.NGET", b"social", b"ada"]),
10647            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10648        );
10649        // A second write to the same id replaces the document and says so with
10650        // a zero, so an ingest can count what it created.
10651        assert_eq!(
10652            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10653            ":0\r\n"
10654        );
10655        assert_eq!(
10656            f.run(&[b"G.NGET", b"social", b"ada"]),
10657            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10658        );
10659        // A node with no properties is an empty map and not a null, which is
10660        // how a client tells an isolated node from one that is not there.
10661        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10662        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10663        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10664        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10665
10666        // A field with no value creates nothing, because the pairs are checked
10667        // before the key is touched.
10668        assert_eq!(
10669            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10670            "-ERR syntax error\r\n"
10671        );
10672        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10673
10674        // On RESP3 the same reply is a map.
10675        let mut g = Fixture::new();
10676        g.run(&[b"HELLO", b"3"]);
10677        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10678        assert_eq!(
10679            g.run(&[b"G.NGET", b"social", b"ada"]),
10680            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10681        );
10682    }
10683
10684    #[test]
10685    fn an_edge_creates_the_ends_it_needs() {
10686        let mut f = Fixture::new();
10687        assert_eq!(
10688            f.run(&[
10689                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10690            ]),
10691            ":1\r\n"
10692        );
10693        // Neither end was written first and both are there, as empty nodes.
10694        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10695        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10696        assert_eq!(
10697            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10698            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10699        );
10700        assert_eq!(
10701            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10702            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10703        );
10704        // The same pair under the same label again updates the edge rather than
10705        // making a second one.
10706        assert_eq!(
10707            f.run(&[
10708                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10709            ]),
10710            ":0\r\n"
10711        );
10712        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10713        // A different label between the same pair is a different edge.
10714        assert_eq!(
10715            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10716            ":1\r\n"
10717        );
10718        assert_eq!(
10719            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10720            ":1\r\n"
10721        );
10722
10723        assert_eq!(
10724            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10725            ":1\r\n"
10726        );
10727        assert_eq!(
10728            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10729            ":0\r\n"
10730        );
10731        // A label nothing has used, an end that is not there, and a key that is
10732        // not there are all a zero rather than an error.
10733        assert_eq!(
10734            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10735            ":0\r\n"
10736        );
10737        assert_eq!(
10738            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10739            ":0\r\n"
10740        );
10741        assert_eq!(
10742            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10743            ":0\r\n"
10744        );
10745    }
10746
10747    /// A run is paged the way `SCAN` is paged, so a client that can walk one
10748    /// can walk the other.
10749    #[test]
10750    fn a_hop_answers_a_cursor_and_a_page() {
10751        let mut f = Fixture::new();
10752        for i in 0..25u32 {
10753            let dst = format!("n{i}");
10754            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10755        }
10756        // Ten without being asked, and the cursor is where to carry on from.
10757        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10758        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10759
10760        let mut seen = 0;
10761        let mut cursor = String::from("0");
10762        loop {
10763            let page = f.run(&[
10764                b"G.OUT",
10765                b"social",
10766                b"hub",
10767                b"FOLLOWS",
10768                b"COUNT",
10769                b"7",
10770                b"CURSOR",
10771                cursor.as_bytes(),
10772            ]);
10773            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10774            cursor = head
10775                .rsplit("\r\n")
10776                .next()
10777                .expect("the cursor line")
10778                .to_string();
10779            seen += rest
10780                .split_once("\r\n")
10781                .expect("the page length")
10782                .0
10783                .parse::<usize>()
10784                .expect("a length");
10785            if cursor == "0" {
10786                break;
10787            }
10788        }
10789        assert_eq!(seen, 25, "every neighbour once across the pages");
10790
10791        // A cursor past the end is an empty page and not an error, and so is a
10792        // key or a label that is not there.
10793        assert_eq!(
10794            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10795            "*2\r\n$1\r\n0\r\n*0\r\n"
10796        );
10797        assert_eq!(
10798            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10799            "*2\r\n$1\r\n0\r\n*0\r\n"
10800        );
10801        assert_eq!(
10802            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10803            "*2\r\n$1\r\n0\r\n*0\r\n"
10804        );
10805        assert_eq!(
10806            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10807            "-ERR COUNT must be a positive integer\r\n"
10808        );
10809        assert_eq!(
10810            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10811            "-ERR syntax error\r\n"
10812        );
10813    }
10814
10815    #[test]
10816    fn a_degree_counts_one_way_or_both() {
10817        let mut f = Fixture::new();
10818        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10819        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10820        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10821        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10822        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10823        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10824        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10825        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10826        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10827        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10828        assert_eq!(
10829            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10830            "-ERR syntax error\r\n"
10831        );
10832    }
10833
10834    /// A walk answers which nodes it can reach and not by how many routes, so a
10835    /// node two ways out is in the frontier once.
10836    #[test]
10837    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10838        let mut f = Fixture::new();
10839        for (src, dst) in [
10840            ("ada", "grace"),
10841            ("ada", "alan"),
10842            ("grace", "edsger"),
10843            ("alan", "edsger"),
10844            ("edsger", "barbara"),
10845        ] {
10846            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10847        }
10848        // Two hops without being asked, the start left out, and edsger once
10849        // even though both of the first hop's nodes point at it.
10850        assert_eq!(
10851            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10852            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10853        );
10854        assert_eq!(
10855            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10856            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10857        );
10858        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10859        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10860        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10861        // COUNT stops the walk rather than trimming what it found.
10862        assert_eq!(
10863            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10864            "*1\r\n$5\r\ngrace\r\n"
10865        );
10866        // A node nothing leaves is an empty array and not an error.
10867        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10868        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10869        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10870        assert_eq!(
10871            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10872            "-ERR DEPTH must be a positive integer\r\n"
10873        );
10874        assert_eq!(
10875            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10876            "-ERR syntax error\r\n"
10877        );
10878    }
10879
10880    /// The two sided search, which is the whole reason `G.PATH` is a command
10881    /// and not something a client builds out of `G.OUT`.
10882    #[test]
10883    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10884        let mut f = Fixture::new();
10885        // A chain of six, and a shortcut that makes a shorter way round under a
10886        // second label so the search has to take either kind of hop.
10887        for i in 0..6u32 {
10888            let src = format!("n{i}");
10889            let dst = format!("n{}", i + 1);
10890            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10891        }
10892        assert_eq!(
10893            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10894            "*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"
10895        );
10896        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10897        assert_eq!(
10898            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10899            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10900        );
10901        // A node to itself is a path of one, and a depth too short to reach is
10902        // no path at all.
10903        assert_eq!(
10904            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10905            "*1\r\n$2\r\nn2\r\n"
10906        );
10907        assert_eq!(
10908            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10909            "*0\r\n"
10910        );
10911        // Direction counts: the chain only goes one way.
10912        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10913        // An unreachable node, a node that is not there, and a key that is not
10914        // there are the same empty answer.
10915        f.run(&[b"G.NADD", b"road", b"island"]);
10916        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10917        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10918        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10919        assert_eq!(
10920            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10921            "-ERR syntax error\r\n"
10922        );
10923    }
10924
10925    /// The point of the escape in the record tag: the keyspace owns a graph key
10926    /// the way it owns every other key, and none of these commands know a graph
10927    /// exists.
10928    #[test]
10929    fn the_keyspace_sees_a_graph_key_like_any_other() {
10930        let mut f = Fixture::new();
10931        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10932        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10933        assert_eq!(
10934            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10935            "$9\r\nadjacency\r\n"
10936        );
10937        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10938        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10939        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10940        // A graph is counted against the server the way every other body is,
10941        // which is what `maxmemory` will read when this key is a million nodes.
10942        // There is no `MEMORY USAGE` command yet, so this asks the server.
10943        let held = f.server.memory_bytes();
10944        for i in 0..200u32 {
10945            let dst = format!("n{i}");
10946            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10947        }
10948        assert!(
10949            f.server.memory_bytes() > held,
10950            "two hundred edges cost something: {held} then {}",
10951            f.server.memory_bytes()
10952        );
10953        f.run(&[b"DEL", b"big"]);
10954
10955        // An expiry, then a rename, then a move to another database, all of
10956        // which are the keyspace moving a record it cannot look inside.
10957        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10958        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10959        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10960        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10961        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10962        f.run(&[b"SELECT", b"1"]);
10963        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10964
10965        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10966        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10967        f.run(&[b"G.NADD", b"g", b"n"]);
10968        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10969        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10970    }
10971
10972    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
10973    /// rather than answering the way they answer for a key that is not there.
10974    #[test]
10975    fn a_graph_cannot_be_copied_or_dumped() {
10976        let mut f = Fixture::new();
10977        f.run(&[b"G.NADD", b"social", b"ada"]);
10978        assert_eq!(
10979            f.run(&[b"COPY", b"social", b"other"]),
10980            "-ERR COPY is not supported for a graph\r\n"
10981        );
10982        assert_eq!(
10983            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10984            "-ERR COPY is not supported for a graph\r\n"
10985        );
10986        assert_eq!(
10987            f.run(&[b"DUMP", b"social"]),
10988            "-ERR DUMP is not supported for a graph\r\n"
10989        );
10990        // A refused copy leaves both keys exactly as they were.
10991        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10992    }
10993
10994    /// A graph key is a key, so the commands for the other types refuse it and
10995    /// the graph commands refuse theirs.
10996    #[test]
10997    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10998        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10999        let mut f = Fixture::new();
11000        f.run(&[b"G.NADD", b"social", b"ada"]);
11001        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
11002        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
11003        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
11004
11005        f.run(&[b"SET", b"str", b"v"]);
11006        for cmd in [
11007            vec![b"G.NADD".as_ref(), b"str", b"n"],
11008            vec![b"G.NGET".as_ref(), b"str", b"n"],
11009            vec![b"G.NDEL".as_ref(), b"str", b"n"],
11010            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
11011            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
11012            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
11013            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
11014            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
11015            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
11016            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
11017        ] {
11018            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
11019        }
11020    }
11021
11022    /// Every other collection here takes its key with it when its last member
11023    /// goes, and a graph is no different.
11024    #[test]
11025    fn a_graph_goes_when_its_last_node_does() {
11026        let mut f = Fixture::new();
11027        f.run(&[
11028            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
11029        ]);
11030        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
11031        // The node and the edges that hung off it are both gone.
11032        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
11033        assert_eq!(
11034            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
11035            ":0\r\n"
11036        );
11037        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
11038        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
11039
11040        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
11041        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
11042        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
11043        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
11044
11045        // The id the removed node had is not handed out again, so a client
11046        // holding an id from an earlier reply cannot have it mean another node.
11047        f.run(&[b"G.NADD", b"social", b"first"]);
11048        f.run(&[b"G.NADD", b"social", b"second"]);
11049        f.run(&[b"G.NDEL", b"social", b"first"]);
11050        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
11051        assert_eq!(
11052            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
11053            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
11054        );
11055    }
11056
11057    // ------------------------------------------------------------------ json
11058
11059    /// The two path syntaxes answer different shapes, which is the thing a
11060    /// client is most likely to be broken by and so the thing to pin first.
11061    #[test]
11062    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
11063        let mut f = Fixture::new();
11064        let doc = br#"{"a":1,"b":{"c":true}}"#;
11065        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
11066        // No path at all is the legacy root and not `$`, so the document comes
11067        // back as itself rather than wrapped.
11068        assert_eq!(
11069            f.run(&[b"JSON.GET", b"doc"]),
11070            bulk(r#"{"a":1,"b":{"c":true}}"#)
11071        );
11072        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
11073        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
11074        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
11075        // A path that matched nothing is an empty set on one syntax and an
11076        // error on the other, and the error does not quote the path.
11077        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
11078        assert_eq!(
11079            f.run(&[b"JSON.GET", b"doc", b".nope"]),
11080            "-ERR Path does not exist\r\n"
11081        );
11082        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
11083        // The key is a document to the rest of the keyspace, under the name
11084        // RedisJSON registers, and every generic command works on it.
11085        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
11086        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
11087        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
11088        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
11089        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
11090    }
11091
11092    /// The two error lines RedisJSON sends without a prefix in front of them.
11093    ///
11094    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
11095    /// two do not, on a real server, and a differential harness compares the
11096    /// whole line.
11097    #[test]
11098    fn the_two_json_errors_that_carry_no_prefix() {
11099        let mut f = Fixture::new();
11100        f.run(&[b"SET", b"plain", b"x"]);
11101        let wrong = "-Existing key has wrong Redis type\r\n";
11102        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
11103        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
11104        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
11105        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
11106        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
11107
11108        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
11109        // A wildcard that matched something writes to all of it. A wildcard
11110        // that matched nothing would have to invent a place, and that is the
11111        // other unprefixed line.
11112        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
11113        assert_eq!(
11114            f.run(&[b"JSON.GET", b"doc"]),
11115            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
11116        );
11117        assert_eq!(
11118            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
11119            "-Err wrong static path\r\n"
11120        );
11121    }
11122
11123    /// What `JSON.SET` does with a path that named nowhere.
11124    #[test]
11125    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
11126        let mut f = Fixture::new();
11127        // A key that is not there can only be written whole.
11128        assert_eq!(
11129            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
11130            "-ERR new objects must be created at the root\r\n"
11131        );
11132        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
11133        // The root check comes before NX and XX, which is the order a real
11134        // server checks them in.
11135        assert_eq!(
11136            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
11137            "-ERR new objects must be created at the root\r\n"
11138        );
11139        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
11140        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
11141
11142        f.run(&[
11143            b"JSON.SET",
11144            b"doc",
11145            b"$",
11146            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
11147        ]);
11148        // One step past a container that is there is a place to write.
11149        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
11150        // One step past something that is not, or past something that is not an
11151        // object, is not an error and is not a write either.
11152        assert_eq!(
11153            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
11154            "$-1\r\n"
11155        );
11156        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
11157        // An index past the end does not append. JSON.ARRAPPEND appends.
11158        assert_eq!(
11159            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
11160            "-ERR array index out of range\r\n"
11161        );
11162        assert_eq!(
11163            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
11164            "-ERR array index out of range\r\n"
11165        );
11166        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
11167        // NX on a path that is there and XX on a path that is not are both a
11168        // nil and neither changes anything.
11169        assert_eq!(
11170            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
11171            "$-1\r\n"
11172        );
11173        assert_eq!(
11174            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
11175            "$-1\r\n"
11176        );
11177        assert_eq!(
11178            f.run(&[b"JSON.GET", b"doc"]),
11179            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
11180        );
11181        // Text that is not JSON is refused before the key is touched. The
11182        // line has no `ERR` in front of it, which is this command's and not
11183        // every command's, and is in D-37.
11184        assert!(
11185            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
11186                .starts_with("-this is not the start of a value")
11187        );
11188        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
11189    }
11190
11191    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
11192    /// answers a count or a word rather than text.
11193    #[test]
11194    fn the_json_commands_that_do_not_answer_text() {
11195        let mut f = Fixture::new();
11196        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
11197        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11198
11199        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
11200        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
11201        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
11202        assert_eq!(
11203            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
11204            format!("*1\r\n{}", bulk("integer"))
11205        );
11206        // The one place a legacy path that matched nothing is a nil rather than
11207        // an error, which lines up with a key that is not there.
11208        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
11209        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
11210
11211        // A boolean flips and answers the value it now has, as an integer on
11212        // one syntax and as the word on the other.
11213        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
11214        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
11215        // Something that is not a boolean is a hole on one syntax and one
11216        // sentence covering both cases on the other.
11217        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
11218        assert_eq!(
11219            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
11220            "-ERR Path does not exist or not a bool\r\n"
11221        );
11222        assert_eq!(
11223            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
11224            "-ERR Path does not exist or not a bool\r\n"
11225        );
11226        assert_eq!(
11227            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
11228            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11229        );
11230
11231        // Clearing empties containers and zeroes numbers and leaves everything
11232        // else alone, and counts only what it changed.
11233        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
11234        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
11235        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
11236        assert_eq!(
11237            f.run(&[b"JSON.GET", b"doc"]),
11238            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
11239        );
11240
11241        // Deleting counts what it removed, and deleting the root is deleting
11242        // the key.
11243        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
11244        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
11245        // Deleting the last member of the root container deletes the key, the
11246        // same way popping the last element off a list does. It is a rule about
11247        // deleting and not about shape: a document written as an empty object
11248        // by JSON.SET stays, because nothing was removed from it.
11249        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
11250        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
11251        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
11252        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
11253        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
11254        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
11255        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
11256        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
11257    }
11258
11259    /// `JSON.GET` with more than one path, and with a layout.
11260    ///
11261    /// The wrapper the reply is built in is laid out too, so what a path
11262    /// matched starts one level in for a single JSONPath and two for one of
11263    /// several, and getting that wrong is the kind of thing only a byte for
11264    /// byte comparison catches.
11265    #[test]
11266    fn json_get_lays_out_the_wrapper_it_builds() {
11267        let mut f = Fixture::new();
11268        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
11269
11270        assert_eq!(
11271            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
11272            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
11273        );
11274        // Legacy paths are not wrapped, even when there are several of them.
11275        assert_eq!(
11276            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
11277            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
11278        );
11279        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
11280        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
11281        one.extend_from_slice(fmt);
11282        one.push(b"$.b");
11283        assert_eq!(
11284            f.run(&one),
11285            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
11286        );
11287        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
11288        two.extend_from_slice(fmt);
11289        two.push(b"$.a");
11290        two.push(b"$.nope");
11291        assert_eq!(
11292            f.run(&two),
11293            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
11294        );
11295        // The options are read before the paths and in any order, and a
11296        // document with nothing to lay out is the same either way.
11297        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
11298        root.push(b".a");
11299        assert_eq!(f.run(&root), bulk("1"));
11300    }
11301
11302    /// `JSON.MGET`, which is the only command here that reads more than one key
11303    /// and so the only one whose answer has holes in it.
11304    #[test]
11305    fn json_mget_answers_once_per_key_whatever_is_under_them() {
11306        let mut f = Fixture::new();
11307        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
11308        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
11309        f.run(&[b"SET", b"plain", b"x"]);
11310        assert_eq!(
11311            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
11312            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
11313        );
11314        // A key that is not there and a key holding something else are both a
11315        // hole rather than an error, the way MGET treats a hash.
11316        assert_eq!(
11317            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
11318            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
11319        );
11320        // A legacy path that matched nothing is a hole too, because one bad
11321        // answer should not lose the others.
11322        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
11323    }
11324
11325    /// The four commands that ask how big something is, and the four different
11326    /// sets of answers they give for the same three failures.
11327    ///
11328    /// There is no pattern in this and there is no reading it off the
11329    /// documentation either. It was read off a running RedisJSON one line at a
11330    /// time, and it is written down here because the error text is what a client
11331    /// library branches on.
11332    #[test]
11333    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
11334        let mut f = Fixture::new();
11335        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
11336        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11337
11338        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
11339        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
11340        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
11341        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
11342        assert_eq!(
11343            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
11344            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
11345        );
11346        // A JSONPath answers one entry per match and a hole for a match of the
11347        // wrong kind, which is the one shape all four agree on.
11348        assert_eq!(
11349            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
11350            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
11351        );
11352
11353        // A legacy path that matched nothing. Two of them are an error and two
11354        // of them are a nil, and the two errors do not use the same sentence.
11355        assert_eq!(
11356            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
11357            "-ERR Path does not exist\r\n"
11358        );
11359        assert_eq!(
11360            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
11361            "-ERR Path does not exist\r\n"
11362        );
11363        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
11364        // A nil bulk and not an empty array, even though the answer would have
11365        // been an array, which is what RedisJSON sends here too.
11366        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
11367        // The JSONPath spelling of the same question is an empty array, since
11368        // no match is not a failure on that syntax.
11369        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
11370
11371        // A legacy path that matched the wrong kind of value. Now two of them
11372        // are an ERR and two of them are a WRONGTYPE, and it is not the same
11373        // two.
11374        assert_eq!(
11375            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
11376            "-ERR Path does not exist or not an array\r\n"
11377        );
11378        assert_eq!(
11379            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
11380            "-ERR Path does not exist or not an object\r\n"
11381        );
11382        assert_eq!(
11383            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
11384            "-WRONGTYPE wrong type of path value - expected object\r\n"
11385        );
11386        assert_eq!(
11387            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
11388            "-WRONGTYPE wrong type of path value - expected string\r\n"
11389        );
11390
11391        // A key that is not there, where the two syntaxes swap over: the legacy
11392        // path is the quiet answer and the JSONPath is the error.
11393        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
11394        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
11395        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
11396        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
11397        assert_eq!(
11398            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
11399            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11400        );
11401        // Except this one, which answers about the path instead.
11402        assert_eq!(
11403            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
11404            "-ERR Path does not exist or not an object\r\n"
11405        );
11406    }
11407
11408    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
11409    ///
11410    /// The four of them share one error line for a path that named something
11411    /// that is not an array, and they disagree about what an index outside the
11412    /// array means: insert refuses it and the other two clamp.
11413    #[test]
11414    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
11415        let mut f = Fixture::new();
11416        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
11417
11418        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
11419        assert_eq!(
11420            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
11421            "*1\r\n:6\r\n"
11422        );
11423        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
11424
11425        // A negative index counts back from the end, and the end itself is a
11426        // place to insert at, so an insert at the length is an append.
11427        assert_eq!(
11428            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
11429            ":7\r\n"
11430        );
11431        assert_eq!(
11432            f.run(&[b"JSON.GET", b"doc", b".a"]),
11433            bulk("[1,2,3,4,5,0,6]")
11434        );
11435        assert_eq!(
11436            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
11437            ":8\r\n"
11438        );
11439        // One past the end is not, and neither is one before the front.
11440        assert_eq!(
11441            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
11442            "-ERR index out of bounds\r\n"
11443        );
11444        assert_eq!(
11445            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
11446            "-ERR index out of bounds\r\n"
11447        );
11448
11449        // Trim takes both ends inclusive and clamps both of them, so a start
11450        // past the end leaves an empty array rather than an error.
11451        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
11452        assert_eq!(
11453            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
11454            ":3\r\n"
11455        );
11456        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
11457        assert_eq!(
11458            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
11459            ":2\r\n"
11460        );
11461        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
11462        assert_eq!(
11463            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
11464            ":0\r\n"
11465        );
11466        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11467
11468        // Pop clamps as well, its default is the last element, and an empty
11469        // array pops a nil rather than failing.
11470        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
11471        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
11472        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
11473        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
11474        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
11475
11476        // One sentence covers a path that matched nothing and a path that
11477        // matched the wrong kind of value, for all four of them.
11478        for call in [
11479            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
11480            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
11481            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
11482            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
11483        ] {
11484            for path in [&b".n"[..], &b".nope"[..]] {
11485                let args: Vec<&[u8]> = call
11486                    .iter()
11487                    .map(|a| if *a == b"PATH" { path } else { *a })
11488                    .collect();
11489                assert_eq!(
11490                    f.run(&args),
11491                    "-ERR Path does not exist or not an array\r\n",
11492                    "{} {}",
11493                    String::from_utf8_lossy(call[0]),
11494                    String::from_utf8_lossy(path)
11495                );
11496            }
11497        }
11498
11499        // A key that is not there is the same sentence for all four, on either
11500        // syntax, and it is about the key and not about the path.
11501        assert_eq!(
11502            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
11503            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11504        );
11505        assert_eq!(
11506            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
11507            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11508        );
11509
11510        // The values are parsed before the key is touched, so text that is not
11511        // JSON leaves the document alone.
11512        // Text that is not JSON is refused before the key is touched, and
11513        // the line has no `ERR` in front of it, which is D-37.
11514        assert!(
11515            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
11516                .starts_with("-this is not the start of a value")
11517        );
11518        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
11519    }
11520
11521    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
11522    /// path matched cannot take the index, which is D-36.
11523    ///
11524    /// RedisJSON walks the matches, inserts into each one it can, and returns
11525    /// the error on the first one it cannot, leaving the earlier inserts in the
11526    /// document. A write here is one list of edits applied together, so either
11527    /// all of them happen or none of them do.
11528    #[test]
11529    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
11530        let mut f = Fixture::new();
11531        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
11532        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11533        assert_eq!(
11534            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
11535            "-ERR index out of bounds\r\n"
11536        );
11537        assert_eq!(
11538            f.run(&[b"JSON.GET", b"doc"]),
11539            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
11540        );
11541        // Every match can take the index, so every match gets it.
11542        assert_eq!(
11543            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
11544            "*3\r\n:4\r\n:3\r\n:2\r\n"
11545        );
11546        assert_eq!(
11547            f.run(&[b"JSON.GET", b"doc"]),
11548            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
11549        );
11550    }
11551
11552    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
11553    /// last element rather than to one past it.
11554    ///
11555    /// Both of those read like mistakes and both are what RedisJSON does. The
11556    /// start is the one that bites: a start of five into an array of four still
11557    /// looks at the fourth, so a search that should have run out of array comes
11558    /// back with an answer.
11559    #[test]
11560    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
11561        let mut f = Fixture::new();
11562        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
11563
11564        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
11565        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
11566        assert_eq!(
11567            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
11568            "*1\r\n:1\r\n"
11569        );
11570
11571        // Zero as the stop means the end rather than the front, so leaving it
11572        // off and passing it are the same thing.
11573        assert_eq!(
11574            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
11575            ":3\r\n"
11576        );
11577        // The stop is exclusive, so a stop of three does not look at index
11578        // three.
11579        assert_eq!(
11580            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
11581            ":-1\r\n"
11582        );
11583
11584        // The start clamps to the last element in both directions, which is why
11585        // a start of four, five or minus one all find the 1 at index three.
11586        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
11587            assert_eq!(
11588                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
11589                ":3\r\n",
11590                "{}",
11591                String::from_utf8_lossy(start)
11592            );
11593        }
11594        assert_eq!(
11595            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
11596            ":0\r\n"
11597        );
11598        // An empty array is the one case that comes back with nothing, since
11599        // the stop is zero and the loop never starts.
11600        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
11601        assert_eq!(
11602            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11603            ":-1\r\n"
11604        );
11605
11606        // The comparison is structural rather than one of the encoded bytes,
11607        // because an object in a stored document holds its keys as intern table
11608        // ids where one parsed off the wire holds them as bytes.
11609        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11610        assert_eq!(
11611            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11612            ":0\r\n"
11613        );
11614        assert_eq!(
11615            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11616            ":1\r\n"
11617        );
11618        assert_eq!(
11619            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11620            ":-1\r\n"
11621        );
11622
11623        // Its errors are a third set again: a missing legacy path is the short
11624        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
11625        // not there is about the path on either syntax.
11626        assert_eq!(
11627            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11628            "-ERR Path does not exist\r\n"
11629        );
11630        assert_eq!(
11631            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11632            "-WRONGTYPE wrong type of path value - expected array\r\n"
11633        );
11634        assert_eq!(
11635            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11636            "-ERR Path does not exist\r\n"
11637        );
11638        assert_eq!(
11639            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11640            "-ERR Path does not exist\r\n"
11641        );
11642    }
11643
11644    /// The number family answers text and keeps an integer an integer until
11645    /// something in the sum is not one.
11646    #[test]
11647    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11648        let mut f = Fixture::new();
11649        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11650        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11651
11652        // A legacy path answers the new value as JSON text in a bulk string,
11653        // not as a number, which is the shape all three of them use.
11654        assert_eq!(
11655            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11656            bulk("9").as_str()
11657        );
11658        // A JSONPath answers a bulk string holding a JSON array.
11659        assert_eq!(
11660            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11661            bulk("[11]").as_str()
11662        );
11663        // Two integers stay an integer and a double anywhere in it makes the
11664        // answer a double, which the document then holds.
11665        assert_eq!(
11666            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11667            bulk("13.0").as_str()
11668        );
11669        assert_eq!(
11670            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11671            bulk("number").as_str()
11672        );
11673        assert_eq!(
11674            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11675            bulk("3.0").as_str()
11676        );
11677        assert_eq!(
11678            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11679            bulk("-8").as_str()
11680        );
11681        // A power of a half is a square root, and the square root of a negative
11682        // number is the error that says the answer is not a number.
11683        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11684        assert_eq!(
11685            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11686            bulk("1.224744871391589").as_str()
11687        );
11688        assert_eq!(
11689            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11690            "-ERR result is not a number\r\n"
11691        );
11692        // An integer answer that does not fit is refused rather than promoted,
11693        // and a negative exponent lands in the same error because there is no
11694        // integer answer to two to the minus one.
11695        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11696        assert_eq!(
11697            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11698            "-ERR numeric overflow\r\n"
11699        );
11700        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11701        assert_eq!(
11702            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11703            "-ERR numeric overflow\r\n"
11704        );
11705        // A double that leaves the finite numbers is the other error.
11706        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11707        assert_eq!(
11708            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11709            "-ERR result is not a number\r\n"
11710        );
11711
11712        // A match that is not a number is a null inside the array on a
11713        // JSONPath, and a legacy path that found no number at all is the error
11714        // with the module's own typo in it.
11715        assert_eq!(
11716            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11717            bulk("[null]").as_str()
11718        );
11719        assert_eq!(
11720            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11721            bulk("[]").as_str()
11722        );
11723        assert_eq!(
11724            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11725            "-ERR Path does not exist or does not contains a number\r\n"
11726        );
11727        assert_eq!(
11728            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11729            "-ERR Path does not exist or does not contains a number\r\n"
11730        );
11731        // The operand is JSON and has to be a number. Valid JSON that is not
11732        // one is a line of its own, and it goes out without a prefix.
11733        assert_eq!(
11734            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11735            "-bad input number\r\n"
11736        );
11737        assert_eq!(
11738            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11739            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11740        );
11741        assert_eq!(
11742            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11743            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11744        );
11745    }
11746
11747    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
11748    /// which nothing else in the group does.
11749    #[test]
11750    fn json_strappend_reads_its_shape_off_the_argument_count() {
11751        let mut f = Fixture::new();
11752        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11753
11754        assert_eq!(
11755            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11756            ":3\r\n"
11757        );
11758        assert_eq!(
11759            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11760            "*1\r\n:4\r\n"
11761        );
11762        // The length is in bytes and not in characters, so one two byte letter
11763        // takes it up by two.
11764        assert_eq!(
11765            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11766            ":6\r\n"
11767        );
11768        // Three arguments means the value is the last one and the path is the
11769        // root, so this appends to a document that is a string on its own.
11770        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11771        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11772        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11773
11774        // The value is JSON and has to be a JSON string. A number is a
11775        // WRONGTYPE about a path value even though it was the value that was
11776        // wrong, which is the module's wording and not a slip here.
11777        assert_eq!(
11778            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11779            "-WRONGTYPE wrong type of path value - expected string\r\n"
11780        );
11781        assert_eq!(
11782            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11783            "*1\r\n$-1\r\n"
11784        );
11785        assert_eq!(
11786            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11787            "-ERR Path does not exist or not a string\r\n"
11788        );
11789        assert_eq!(
11790            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11791            "*0\r\n"
11792        );
11793        assert_eq!(
11794            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11795            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11796        );
11797    }
11798
11799    /// A legacy path can match more than one value, and which of them the one
11800    /// answer comes from is not the same choice twice.
11801    #[test]
11802    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11803        let mut f = Fixture::new();
11804        // Three arrays of one, two and three elements, which tells the first
11805        // match and the last match apart in a single command.
11806        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11807
11808        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11809        assert_eq!(
11810            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11811            ":4\r\n"
11812        );
11813        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11814        assert_eq!(
11815            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11816            ":2\r\n"
11817        );
11818        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11819        assert_eq!(
11820            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11821            ":1\r\n"
11822        );
11823        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11824        assert_eq!(
11825            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11826            bulk("1").as_str()
11827        );
11828        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11829        assert_eq!(
11830            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11831            bulk("13").as_str()
11832        );
11833        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11834        assert_eq!(
11835            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11836            ":4\r\n"
11837        );
11838        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11839        assert_eq!(
11840            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11841            bulk("false").as_str()
11842        );
11843        // Every one of them wrote to all three matches, whichever one it chose
11844        // to answer about.
11845        assert_eq!(
11846            f.run(&[b"JSON.GET", b"doc", b".a"]),
11847            bulk("[false,true,false]").as_str()
11848        );
11849
11850        // A match of the wrong kind is skipped rather than being the answer, so
11851        // a path that found a string and then two arrays still answers.
11852        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11853        assert_eq!(
11854            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11855            ":3\r\n"
11856        );
11857        assert_eq!(
11858            f.run(&[b"JSON.GET", b"doc", b".a"]),
11859            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11860        );
11861        // Nothing of the right kind anywhere is the error, and that is the only
11862        // case that is.
11863        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11864        assert_eq!(
11865            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11866            "-ERR Path does not exist or not an array\r\n"
11867        );
11868        assert_eq!(
11869            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11870            "-ERR Path does not exist or not a bool\r\n"
11871        );
11872        // The one array that was there and had nothing in it is an answer and
11873        // not a skip, so the pop answers about it rather than about the array
11874        // after it.
11875        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11876        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11877        assert_eq!(
11878            f.run(&[b"JSON.GET", b"doc", b".a"]),
11879            bulk("[[],[2]]").as_str()
11880        );
11881    }
11882
11883    /// A path that matched a value and something inside that value writes to
11884    /// both, which is what `$..` and a nested wildcard are for.
11885    #[test]
11886    fn a_write_reaches_a_match_that_sits_inside_another_match() {
11887        let mut f = Fixture::new();
11888        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11889
11890        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11891        assert_eq!(
11892            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11893            "*3\r\n:3\r\n:2\r\n:3\r\n"
11894        );
11895        assert_eq!(
11896            f.run(&[b"JSON.GET", b"doc", b"$"]),
11897            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11898        );
11899
11900        // The same for a trim, where the outer array keeps the two elements the
11901        // inner writes landed in.
11902        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11903        assert_eq!(
11904            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11905            "*3\r\n:1\r\n:1\r\n:1\r\n"
11906        );
11907        assert_eq!(
11908            f.run(&[b"JSON.GET", b"doc", b"$"]),
11909            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11910        );
11911
11912        // And for a number, where the first match is the object the outer array
11913        // holds and only the two inside it are numbers.
11914        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11915        assert_eq!(
11916            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11917            bulk("[null,8,8]").as_str()
11918        );
11919    }
11920
11921    /// The value a write is given is looked at only once the path has found
11922    /// something of the right kind to use it on.
11923    #[test]
11924    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11925        let mut f = Fixture::new();
11926        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11927
11928        // A string is not a number, so the path answers first and the `"x"` is
11929        // never looked at. Same for the value that is not JSON at all.
11930        assert_eq!(
11931            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11932            bulk("[null]").as_str()
11933        );
11934        assert_eq!(
11935            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11936            bulk("[null]").as_str()
11937        );
11938        assert_eq!(
11939            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11940            bulk("[]").as_str()
11941        );
11942        assert_eq!(
11943            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11944            "-ERR Path does not exist or does not contains a number\r\n"
11945        );
11946        // A number match anywhere and the value is looked at after all.
11947        assert_eq!(
11948            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11949            "-bad input number\r\n"
11950        );
11951
11952        // JSON.STRAPPEND follows the same order with its own two answers.
11953        assert_eq!(
11954            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11955            "*1\r\n$-1\r\n"
11956        );
11957        assert_eq!(
11958            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11959            "-ERR Path does not exist or not a string\r\n"
11960        );
11961        assert_eq!(
11962            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11963            "-WRONGTYPE wrong type of path value - expected string\r\n"
11964        );
11965
11966        // A key that is not there still comes before either of them.
11967        assert_eq!(
11968            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11969            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11970        );
11971        assert_eq!(
11972            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11973            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11974        );
11975    }
11976
11977    /// RFC 7386 in one test: a null deletes, everything else merges, and a
11978    /// patch that is not an object replaces what it lands on.
11979    #[test]
11980    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11981        let mut f = Fixture::new();
11982
11983        // A key that is not there is created at the root, nulls and all,
11984        // because a deletion with nothing to delete is still what the client
11985        // sent.
11986        assert_eq!(
11987            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11988            "+OK\r\n"
11989        );
11990        assert_eq!(
11991            f.run(&[b"JSON.GET", b"doc", b"$"]),
11992            bulk(r#"[{"x":null,"y":1}]"#).as_str()
11993        );
11994
11995        // Onto something that is there, a null deletes the member of that name
11996        // and the rest is merged one level at a time.
11997        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11998        assert_eq!(
11999            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
12000            "+OK\r\n"
12001        );
12002        assert_eq!(
12003            f.run(&[b"JSON.GET", b"doc", b"$"]),
12004            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
12005        );
12006
12007        // A patch that is not an object replaces what it is merged onto.
12008        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
12009        assert_eq!(
12010            f.run(&[b"JSON.GET", b"doc", b"$"]),
12011            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
12012        );
12013
12014        // A patch object onto a value that is not an object starts from an
12015        // empty object, so this time the null has nothing to delete and is
12016        // dropped rather than stored.
12017        assert_eq!(
12018            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
12019            "+OK\r\n"
12020        );
12021        assert_eq!(
12022            f.run(&[b"JSON.GET", b"doc", b"$"]),
12023            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
12024        );
12025
12026        // A member one level past the end of the document is created and keeps
12027        // its nulls, two levels past it is a write that did not happen, and a
12028        // path that would have to invent where it goes is the unprefixed line.
12029        assert_eq!(
12030            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
12031            "+OK\r\n"
12032        );
12033        assert_eq!(
12034            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
12035            bulk(r#"[{"z":null}]"#).as_str()
12036        );
12037        assert_eq!(
12038            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
12039            "$-1\r\n"
12040        );
12041        assert_eq!(
12042            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
12043            "-Err wrong static path\r\n"
12044        );
12045
12046        // A wildcard merges every match.
12047        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
12048        assert_eq!(
12049            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
12050            "+OK\r\n"
12051        );
12052        assert_eq!(
12053            f.run(&[b"JSON.GET", b"doc", b"$"]),
12054            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
12055        );
12056
12057        // The three ways to get it wrong.
12058        assert_eq!(
12059            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
12060            "-ERR syntax error\r\n"
12061        );
12062        assert_eq!(
12063            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
12064            "-ERR new objects must be created at the root\r\n"
12065        );
12066        f.run(&[b"SET", b"str", b"x"]);
12067        assert_eq!(
12068            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
12069            "-Existing key has wrong Redis type\r\n"
12070        );
12071    }
12072
12073    /// A descent is the one path that matches a value and something inside that
12074    /// same value, and the inner merge has to survive the outer one.
12075    #[test]
12076    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
12077        let mut f = Fixture::new();
12078        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
12079        assert_eq!(
12080            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
12081            "+OK\r\n"
12082        );
12083        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
12084        // merged onto the result, so the `{"m":1}` written into `a.b` is still
12085        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
12086        assert_eq!(
12087            f.run(&[b"JSON.GET", b"doc", b"$"]),
12088            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
12089        );
12090
12091        // A deletion down the same path, which is the case where the inner
12092        // merge empties the object the outer one then copies.
12093        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
12094        assert_eq!(
12095            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
12096            "+OK\r\n"
12097        );
12098        assert_eq!(
12099            f.run(&[b"JSON.GET", b"doc", b"$"]),
12100            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
12101        );
12102    }
12103
12104    /// A filter is a selector like any other, so every command that takes a path
12105    /// takes one, reads and writes alike.
12106    #[test]
12107    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
12108        let mut f = Fixture::new();
12109        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
12110        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
12111
12112        assert_eq!(
12113            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
12114            bulk(r#"["a","c"]"#).as_str()
12115        );
12116        // `$` inside the expression is the document, so a member can be measured
12117        // against something that is not inside it.
12118        assert_eq!(
12119            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
12120            bulk(r#"["a","c"]"#).as_str()
12121        );
12122        // The legacy syntax takes one too, and answers the first match.
12123        assert_eq!(
12124            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
12125            bulk(r#""a""#).as_str()
12126        );
12127        assert_eq!(
12128            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
12129            "*1\r\n$6\r\nobject\r\n"
12130        );
12131
12132        // A write goes through it as far as a value that is already there. A
12133        // field that is not there yet has nowhere definite to go, which is the
12134        // same refusal a wildcard gets.
12135        assert_eq!(
12136            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
12137            bulk("[9,10]").as_str()
12138        );
12139        assert_eq!(
12140            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
12141            "+OK\r\n"
12142        );
12143        assert_eq!(
12144            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
12145            "-Err wrong static path\r\n"
12146        );
12147        assert_eq!(
12148            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
12149            ":2\r\n"
12150        );
12151        assert_eq!(
12152            f.run(&[b"JSON.GET", b"doc", b"$"]),
12153            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
12154        );
12155
12156        // A path that does not parse is refused before the document is read, so
12157        // a key that is not there answers the same way.
12158        assert!(
12159            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
12160                .starts_with("-ERR")
12161        );
12162        assert!(
12163            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
12164                .starts_with("-ERR")
12165        );
12166    }
12167
12168    /// The operators past the comparisons, over the wire rather than in the
12169    /// parser's own tests, so that a client can reach all of them.
12170    #[test]
12171    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
12172        let mut f = Fixture::new();
12173        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
12174        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
12175
12176        for (path, want) in [
12177            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
12178            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
12179            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
12180            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
12181            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
12182            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
12183            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
12184            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
12185            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
12186            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
12187            (b"$.box[?(@.n~)].t", "[]"),
12188            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
12189            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
12190            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
12191            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
12192        ] {
12193            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
12194        }
12195
12196        // A write goes through one of these the same way it goes through a
12197        // comparison.
12198        assert_eq!(
12199            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
12200            "+OK\r\n"
12201        );
12202        assert_eq!(
12203            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
12204            bulk(r#"["b"]"#).as_str()
12205        );
12206    }
12207
12208    /// D-41. RedisJSON refuses this one, and which document it refuses is
12209    /// decided by how it happens to hold an array of numbers.
12210    #[test]
12211    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
12212        let mut f = Fixture::new();
12213        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
12214        assert_eq!(
12215            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12216            "+OK\r\n"
12217        );
12218        assert_eq!(
12219            f.run(&[b"JSON.GET", b"doc", b"$"]),
12220            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
12221        );
12222        // The same document with one element that is not an integer is the one
12223        // RedisJSON is happy with, and it goes the same way here.
12224        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
12225        assert_eq!(
12226            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
12227            "+OK\r\n"
12228        );
12229        assert_eq!(
12230            f.run(&[b"JSON.GET", b"doc", b"$"]),
12231            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
12232        );
12233    }
12234
12235    /// `JSON.MSET` checks what it can before it writes anything and skips the
12236    /// one thing it cannot, which is a path with nowhere to put its value.
12237    #[test]
12238    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
12239        let mut f = Fixture::new();
12240        assert_eq!(
12241            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
12242            "+OK\r\n"
12243        );
12244        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
12245        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
12246
12247        // A repeated key takes the last write.
12248        assert_eq!(
12249            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
12250            "+OK\r\n"
12251        );
12252        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
12253
12254        // A triple whose path names nowhere is skipped, the others are still
12255        // written and the reply turns into a nil. Both ways round, because a
12256        // loop that gave up at the first skip would agree with this on one
12257        // order and not on the other.
12258        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
12259        assert_eq!(
12260            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
12261            "$-1\r\n"
12262        );
12263        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
12264        assert_eq!(
12265            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
12266            "$-1\r\n"
12267        );
12268        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12269
12270        // A value that is not JSON, a key holding something else and a path
12271        // that would have to create a document below its own root are all
12272        // checked before anything is written, so the good triple next to them
12273        // does not happen either.
12274        f.run(&[b"SET", b"str", b"x"]);
12275        assert_eq!(
12276            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
12277            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
12278        );
12279        assert_eq!(
12280            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
12281            "-Existing key has wrong Redis type\r\n"
12282        );
12283        assert_eq!(
12284            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
12285            "-ERR new objects must be created at the root\r\n"
12286        );
12287        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
12288
12289        // The two errors a path can be are checked up front as well, so the
12290        // triple before them is not written either. A wildcard that matched
12291        // nothing has nowhere to invent, and an index that is not in the array
12292        // is out of range, and both of them stop the whole command.
12293        assert_eq!(
12294            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
12295            "-Err wrong static path\r\n"
12296        );
12297        assert_eq!(
12298            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
12299            "-ERR array index out of range\r\n"
12300        );
12301        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
12302
12303        // Every triple is worked out against the keyspace as the command found
12304        // it, so a second triple on the same key does not see the first one and
12305        // the last write is the one that stays.
12306        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
12307        assert_eq!(
12308            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
12309            "+OK\r\n"
12310        );
12311        assert_eq!(
12312            f.run(&[b"JSON.GET", b"c", b"$"]),
12313            bulk(r#"[{"n":3}]"#).as_str()
12314        );
12315
12316        // An argument count that is not a run of key, path and value is the
12317        // arity error rather than a syntax one.
12318        assert_eq!(
12319            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
12320            "-ERR wrong number of arguments for 'json.mset' command\r\n"
12321        );
12322    }
12323
12324    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
12325    /// an empty array and an empty object apart.
12326    #[test]
12327    fn json_resp_answers_the_document_as_resp_types() {
12328        let mut f = Fixture::new();
12329        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
12330        assert_eq!(
12331            f.run(&[b"JSON.RESP", b"doc"]),
12332            "*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"
12333        );
12334        // A JSONPath wraps the same answer in one more array.
12335        assert_eq!(
12336            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
12337            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
12338        );
12339
12340        f.run(&[
12341            b"JSON.SET",
12342            b"doc",
12343            b"$",
12344            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
12345        ]);
12346        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
12347        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
12348        // A double goes out as its text, so a client reads the same digits
12349        // `JSON.GET` would have given it.
12350        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
12351        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
12352        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
12353
12354        // A missing legacy path is an error, a missing JSONPath is an empty
12355        // array, and a key that is not there is a nil on either.
12356        assert_eq!(
12357            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
12358            "-ERR Path does not exist\r\n"
12359        );
12360        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
12361        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
12362        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
12363    }
12364
12365    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
12366    /// pins the shapes and that the two syntaxes agree rather than a number
12367    /// read off another server. That is D-42.
12368    #[test]
12369    fn json_debug_answers_a_byte_count_and_its_own_help() {
12370        let mut f = Fixture::new();
12371        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
12372        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
12373        assert!(one.starts_with(':'), "{one}");
12374        assert_eq!(
12375            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
12376            format!("*1\r\n{one}")
12377        );
12378        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
12379        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
12380
12381        // A key that is not there is a zero on a legacy path and an empty set
12382        // on a JSONPath, which is the one reader here that does not answer nil
12383        // for it.
12384        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
12385        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
12386        assert_eq!(
12387            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
12388            "-ERR Path does not exist\r\n"
12389        );
12390        assert_eq!(
12391            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
12392            "*0\r\n"
12393        );
12394
12395        assert_eq!(
12396            f.run(&[b"JSON.DEBUG", b"HELP"]),
12397            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
12398             $34\r\nHELP                - this message\r\n"
12399        );
12400        assert_eq!(
12401            f.run(&[b"JSON.DEBUG", b"NOPE"]),
12402            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
12403        );
12404        assert_eq!(
12405            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
12406            "-ERR wrong number of arguments for 'json.debug' command\r\n"
12407        );
12408    }
12409
12410    // ---------------------------------------------------------------- vector
12411
12412    /// The first `VADD` fixes the dimension and every one after it has to
12413    /// agree, because there is no create command to say it earlier.
12414    #[test]
12415    fn the_first_vadd_decides_how_wide_the_set_is() {
12416        let mut f = Fixture::new();
12417        assert_eq!(
12418            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
12419            ":1\r\n"
12420        );
12421        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12422        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12423        // A second vector under the same name replaces it and says so with a
12424        // zero, so an ingest can count what it created.
12425        assert_eq!(
12426            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
12427            ":0\r\n"
12428        );
12429        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
12430        // Three dimensions into a two dimensional set names both numbers, since
12431        // a client that gets this wrong needs to know which end is which.
12432        assert_eq!(
12433            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
12434            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
12435        );
12436        // A vector of zeros has no direction, and it is taken anyway and comes
12437        // back as the origin, because that is what a real server does with it.
12438        assert_eq!(
12439            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
12440            ":1\r\n"
12441        );
12442        assert_eq!(
12443            f.run(&[b"VEMB", b"v", b"nowhere"]),
12444            "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
12445        );
12446        // A set is made with one quantisation and keeps it, and a `VADD` that
12447        // names another is refused. Naming none names `Q8`, which is why this
12448        // set is a `Q8` one.
12449        assert_eq!(
12450            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
12451            "-ERR asked quantization mismatch with existing vector set\r\n"
12452        );
12453        // Nothing above created a key, and a set that never took a vector has
12454        // no dimension to report.
12455        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
12456        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
12457        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
12458    }
12459
12460    /// What a client sent comes back out, and what a client asked for is a
12461    /// similarity and not the distance underneath it.
12462    #[test]
12463    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
12464        let mut f = Fixture::new();
12465        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
12466        // The set stored the direction and the length is multiplied back on the
12467        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
12468        // either, because nobody named a quantisation and that means `Q8`: the
12469        // wider coordinate lands on a code exactly and the other one does not.
12470        // Both numbers are a real server's answers for the same input.
12471        assert_eq!(
12472            f.run(&[b"VEMB", b"v", b"a"]),
12473            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12474        );
12475        // NOQUANT is the way to ask for what went in to come back out.
12476        f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
12477        assert_eq!(
12478            f.run(&[b"VEMB", b"n", b"a"]),
12479            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12480        );
12481        // BIN keeps the signs and nothing else, and does not multiply the
12482        // length back on, since a sign has no length in it to scale.
12483        f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
12484        assert_eq!(
12485            f.run(&[b"VEMB", b"b", b"a"]),
12486            "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
12487        );
12488        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
12489        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
12490
12491        // On the axes, where the unit vector is exact and so is the dot
12492        // product, both ends of the scale come out exact: the same direction is
12493        // 1 and the opposite one is 0, with a right angle at a half.
12494        let mut f = Fixture::new();
12495        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
12496        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
12497        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
12498        assert_eq!(
12499            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
12500            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
12501             $8\r\nopposite\r\n$1\r\n0\r\n"
12502        );
12503        // A search from an element leaves that element out, since it is always
12504        // its own nearest neighbour.
12505        assert_eq!(
12506            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
12507            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12508        );
12509        // An element that is not there is an empty answer and not an error,
12510        // which is what a missing key gives too.
12511        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
12512        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
12513        // COUNT bounds it and TRUTH reads every vector rather than the codes,
12514        // which has to agree with the index on a set this small.
12515        assert_eq!(
12516            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
12517            "*1\r\n$6\r\nacross\r\n"
12518        );
12519        assert_eq!(
12520            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
12521            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
12522        );
12523        // EF widens how much of the index is read and does not change how many
12524        // answers come back, so a wide search still returns what COUNT asked
12525        // for.
12526        assert_eq!(
12527            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
12528            "*1\r\n$6\r\nacross\r\n"
12529        );
12530
12531        // On RESP3 a scored search is a map, which is what the vector set
12532        // module replies and is not what ZRANGE does here.
12533        let mut g = Fixture::new();
12534        g.run(&[b"HELLO", b"3"]);
12535        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12536        assert_eq!(
12537            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
12538            "%1\r\n$4\r\neast\r\n,1\r\n"
12539        );
12540    }
12541
12542    /// The attribute pair, and the one reply that means two things.
12543    #[test]
12544    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
12545        let mut f = Fixture::new();
12546        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12547        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12548        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
12549        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
12550        // Not parsed as JSON, because nothing reads into it yet and refusing a
12551        // write for a rule nothing enforces would be the wrong trade.
12552        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
12553        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
12554        // An empty string clears it, which is Redis's spelling of the removal.
12555        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
12556        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
12557        // An element that is not there answers zero rather than being created,
12558        // since an attribute with no vector under it is not a thing this holds.
12559        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
12560        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
12561        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
12562        // A null for an element with no attribute and a null for one that is
12563        // not there. VISMEMBER is how a client tells the two apart.
12564        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
12565        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
12566        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
12567        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
12568
12569        // WITHATTRIBS carries it alongside the answers.
12570        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12571        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12572        assert_eq!(
12573            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
12574            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
12575        );
12576    }
12577
12578    /// The slot a removed element had is reused, and nothing that was beside it
12579    /// comes back with the next element to get it.
12580    #[test]
12581    fn vrem_takes_the_attribute_with_it() {
12582        let mut f = Fixture::new();
12583        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12584        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12585        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
12586        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
12587        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
12588        // The key went with the last element, the way every other collection
12589        // here works.
12590        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12591
12592        // The next element is given the slot the removed one had, and it comes
12593        // with no attribute on it.
12594        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12595        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
12596        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12597        f.run(&[b"VREM", b"v", b"east"]);
12598        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
12599        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
12600    }
12601
12602    /// `VINFO` says what the index is before it says anything a client could
12603    /// mistake for a graph.
12604    #[test]
12605    fn vinfo_says_partition_first() {
12606        let mut f = Fixture::new();
12607        f.run(&[
12608            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12609        ]);
12610        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12611        let info = f.run(&[b"VINFO", b"v"]);
12612        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12613        // What the client asked for and not what happened to the tuning, which
12614        // is `10` section 7: M is recorded and changes nothing.
12615        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12616        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12617        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12618        // Nobody named a quantisation, so this set is a `Q8` one and every
12619        // element in it is stored that way.
12620        assert!(
12621            info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12622            "{info}"
12623        );
12624        let mut f = Fixture::new();
12625        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12626        assert!(
12627            f.run(&[b"VINFO", b"v"])
12628                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12629        );
12630        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12631    }
12632
12633    /// A set to read ranges of names out of.
12634    fn named() -> Fixture {
12635        let mut f = Fixture::new();
12636        for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12637            .iter()
12638            .enumerate()
12639        {
12640            let x = (i + 1).to_string();
12641            f.run(&[
12642                b"VADD",
12643                b"r",
12644                b"VALUES",
12645                b"2",
12646                x.as_bytes(),
12647                b"1",
12648                name.as_bytes(),
12649            ]);
12650        }
12651        f
12652    }
12653
12654    /// `VRANGE` reads the names in the order bytes come in and pays no
12655    /// attention to where the vectors point.
12656    #[test]
12657    fn vrange_walks_the_names_and_not_the_vectors() {
12658        let mut f = named();
12659        assert_eq!(
12660            f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12661            "*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"
12662        );
12663        assert_eq!(
12664            f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12665            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12666            "the high end is a name and not a prefix, so delta is past it"
12667        );
12668        assert_eq!(
12669            f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12670            "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12671        );
12672        assert_eq!(
12673            f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12674            "*1\r\n$4\r\nbeta\r\n"
12675        );
12676        assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12677        // Bytes and not letters, so an upper case name sorts before every lower
12678        // case one rather than beside its own spelling.
12679        f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12680        assert_eq!(
12681            f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12682            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12683        );
12684        assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12685    }
12686
12687    /// The count cuts the answer after the range is decided, and zero is not
12688    /// the same as leaving it out.
12689    #[test]
12690    fn a_vrange_count_of_zero_asks_for_nothing() {
12691        let mut f = named();
12692        assert_eq!(
12693            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12694            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12695        );
12696        assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12697        assert!(
12698            f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12699                .starts_with("*5\r\n"),
12700            "a negative count is no limit at all"
12701        );
12702    }
12703
12704    /// Both ends are read before either is placed, and the count is read before
12705    /// either end.
12706    #[test]
12707    fn vrange_says_which_end_it_could_not_read() {
12708        let mut f = named();
12709        assert_eq!(
12710            f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12711            "-ERR invalid start range format\r\n"
12712        );
12713        assert_eq!(
12714            f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12715            "-ERR invalid end range format\r\n",
12716            "the high end is spelled wrong, which is worth saying before the \
12717             low end being on the wrong side"
12718        );
12719        assert_eq!(
12720            f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12721            "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12722        );
12723        // A bracket with nothing after it is not the empty name here, though an
12724        // element really can be called that.
12725        assert_eq!(
12726            f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12727            "-ERR invalid start range format\r\n"
12728        );
12729        assert_eq!(
12730            f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12731            "-ERR invalid COUNT value\r\n"
12732        );
12733        assert_eq!(
12734            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12735            "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12736        );
12737        f.run(&[b"SET", b"s", b"x"]);
12738        assert!(
12739            f.run(&[b"VRANGE", b"s", b"-", b"+"])
12740                .starts_with("-WRONGTYPE")
12741        );
12742    }
12743
12744    /// The option that asks for something this index does not have says so
12745    /// rather than doing something else quietly.
12746    #[test]
12747    fn reduce_is_refused_and_not_ignored() {
12748        let mut f = Fixture::new();
12749        let reduce = f.run(&[
12750            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12751        ]);
12752        assert!(
12753            reduce.starts_with("-ERR REDUCE is not supported."),
12754            "{reduce}"
12755        );
12756        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12757    }
12758
12759    /// A filtered search answers with the nearest elements that match, and an
12760    /// expression that is not one is an error before the key is looked at.
12761    #[test]
12762    fn vsim_filter_reads_the_attributes() {
12763        let mut f = Fixture::new();
12764        for (name, x, y, attr) in [
12765            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12766            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12767            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12768            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12769        ] {
12770            f.run(&[
12771                b"VADD",
12772                b"v",
12773                b"VALUES",
12774                b"2",
12775                x.as_bytes(),
12776                y.as_bytes(),
12777                name.as_bytes(),
12778                b"SETATTR",
12779                attr.as_bytes(),
12780            ]);
12781        }
12782        // `b` is the nearest to the query and is the one the filter drops, so
12783        // this is the answer a filter applied afterwards would have got wrong.
12784        assert_eq!(
12785            f.run(&[
12786                b"VSIM",
12787                b"v",
12788                b"VALUES",
12789                b"2",
12790                b"9",
12791                b"1",
12792                b"COUNT",
12793                b"2",
12794                b"FILTER",
12795                b".lang == \"en\"",
12796            ]),
12797            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12798        );
12799        // A number is compared as a number, and the two halves of an `and` both
12800        // have to hold.
12801        assert_eq!(
12802            f.run(&[
12803                b"VSIM",
12804                b"v",
12805                b"VALUES",
12806                b"2",
12807                b"9",
12808                b"1",
12809                b"FILTER",
12810                b".lang == 'en' and .year > 1980",
12811            ]),
12812            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12813        );
12814        // A list, and a field an element does not have.
12815        assert_eq!(
12816            f.run(&[
12817                b"VSIM",
12818                b"v",
12819                b"VALUES",
12820                b"2",
12821                b"9",
12822                b"1",
12823                b"FILTER",
12824                b".lang in ['fr', 'de']",
12825            ]),
12826            "*1\r\n$1\r\nb\r\n"
12827        );
12828        assert_eq!(
12829            f.run(&[
12830                b"VSIM",
12831                b"v",
12832                b"VALUES",
12833                b"2",
12834                b"9",
12835                b"1",
12836                b"FILTER",
12837                b".rating > 3"
12838            ]),
12839            "*0\r\n"
12840        );
12841        // TRUTH measures every vector, and the filter still decides which ones
12842        // are measured.
12843        assert_eq!(
12844            f.run(&[
12845                b"VSIM",
12846                b"v",
12847                b"VALUES",
12848                b"2",
12849                b"9",
12850                b"1",
12851                b"TRUTH",
12852                b"FILTER",
12853                b".year < 1980",
12854            ]),
12855            "*1\r\n$1\r\nc\r\n"
12856        );
12857        // VSETATTR moves an element in and out of a filter, which means the tag
12858        // beside its code was rewritten and not just the string.
12859        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12860        assert_eq!(
12861            f.run(&[
12862                b"VSIM",
12863                b"v",
12864                b"VALUES",
12865                b"2",
12866                b"9",
12867                b"1",
12868                b"COUNT",
12869                b"1",
12870                b"FILTER",
12871                b".lang == \"en\"",
12872            ]),
12873            "*1\r\n$1\r\nb\r\n"
12874        );
12875        // And a VADD that replaces the vector keeps the attribute and the tag,
12876        // which is the same rewrite from the other end.
12877        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12878        assert_eq!(
12879            f.run(&[
12880                b"VSIM",
12881                b"v",
12882                b"VALUES",
12883                b"2",
12884                b"9",
12885                b"1",
12886                b"COUNT",
12887                b"1",
12888                b"FILTER",
12889                b".lang == \"en\"",
12890            ]),
12891            "*1\r\n$1\r\nb\r\n"
12892        );
12893
12894        // The expression is parsed before the key is read, so a bad one is an
12895        // error whether or not the key is there.
12896        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12897        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12898        assert_eq!(
12899            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12900            "-ERR invalid FILTER expression\r\n"
12901        );
12902        // FILTER-EF raises the effort rather than capping it, and zero is
12903        // Redis's word for no limit, so neither is an error.
12904        assert_eq!(
12905            f.run(&[
12906                b"VSIM",
12907                b"v",
12908                b"VALUES",
12909                b"2",
12910                b"9",
12911                b"1",
12912                b"COUNT",
12913                b"1",
12914                b"FILTER-EF",
12915                b"500",
12916                b"FILTER",
12917                b".lang == 'en'",
12918            ]),
12919            "*1\r\n$1\r\nb\r\n"
12920        );
12921        assert_eq!(
12922            f.run(&[
12923                b"VSIM",
12924                b"v",
12925                b"VALUES",
12926                b"2",
12927                b"9",
12928                b"1",
12929                b"COUNT",
12930                b"1",
12931                b"FILTER-EF",
12932                b"0"
12933            ]),
12934            "*1\r\n$1\r\nb\r\n"
12935        );
12936        assert_eq!(
12937            f.run(&[
12938                b"VSIM",
12939                b"v",
12940                b"VALUES",
12941                b"2",
12942                b"9",
12943                b"1",
12944                b"FILTER-EF",
12945                b"lots"
12946            ]),
12947            "-ERR EF must be a positive integer\r\n"
12948        );
12949    }
12950
12951    /// A vector set key is a key, so the keyspace owns it the way it owns every
12952    /// other one and none of those commands know what is inside it.
12953    #[test]
12954    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12955        let mut f = Fixture::new();
12956        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12957        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12958        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12959        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12960        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12961        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12962        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12963        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12964        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12965        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12966        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12967
12968        // And the wrong type is the wrong type in both directions.
12969        f.run(&[b"SET", b"s", b"1"]);
12970        assert_eq!(
12971            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12972            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12973        );
12974        assert_eq!(
12975            f.run(&[b"VCARD", b"s"]),
12976            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12977        );
12978        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12979        assert_eq!(
12980            f.run(&[b"GET", b"v"]),
12981            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12982        );
12983        // A graph and a vector set share the escape in the record tag and are
12984        // still two different types, which is the case the tag alone cannot
12985        // decide.
12986        f.run(&[b"G.NADD", b"social", b"ada"]);
12987        assert_eq!(
12988            f.run(&[b"VCARD", b"social"]),
12989            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12990        );
12991        assert_eq!(
12992            f.run(&[b"G.NGET", b"v", b"ada"]),
12993            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12994        );
12995    }
12996
12997    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
12998    /// shapes, off the database's own generator.
12999    #[test]
13000    fn vrandmember_has_the_two_shapes_srandmember_has() {
13001        let mut f = Fixture::new();
13002        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
13003            let x = (i + 1).to_string();
13004            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
13005        }
13006        // One element is a bulk string and not an array of one.
13007        let one = f.run(&[b"VRANDMEMBER", b"v"]);
13008        assert!(one.starts_with("$1\r\n"), "{one}");
13009        // A positive count is distinct and stops at the size of the set.
13010        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
13011        assert!(all.starts_with("*3\r\n"), "{all}");
13012        for name in ["a", "b", "c"] {
13013            assert!(all.contains(name), "{all} is missing {name}");
13014        }
13015        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
13016        assert!(all.starts_with("*2\r\n"), "{all}");
13017        // A negative one draws that many and allows repeats.
13018        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
13019        assert!(many.starts_with("*5\r\n"), "{many}");
13020        // A key that is not there answers the shape that was asked for.
13021        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
13022        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
13023    }
13024
13025    /// `VLINKS` answers about the index that is here rather than the graph that
13026    /// is not, which is D-2.
13027    #[test]
13028    fn vlinks_reports_one_layer_of_partition_neighbours() {
13029        let mut f = Fixture::new();
13030        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
13031        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
13032        // One layer deep, because the index is one layer deep, so a client
13033        // walking layers gets a short list and not a shape it cannot parse.
13034        assert_eq!(
13035            f.run(&[b"VLINKS", b"v", b"east"]),
13036            "*1\r\n*1\r\n$5\r\nnorth\r\n"
13037        );
13038        assert_eq!(
13039            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
13040            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
13041        );
13042        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
13043        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
13044    }
13045
13046    /// A vector arrives either as digits or as bytes, and the two have to mean
13047    /// the same thing.
13048    #[test]
13049    fn fp32_and_values_are_the_same_vector() {
13050        let mut f = Fixture::new();
13051        let mut blob = Vec::new();
13052        for x in [3.0f32, 4.0] {
13053            blob.extend_from_slice(&x.to_le_bytes());
13054        }
13055        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
13056        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
13057        assert_eq!(
13058            f.run(&[b"VEMB", b"v", b"a"]),
13059            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
13060        );
13061        // RAW is the stored bytes and the numbers that turn them back into the
13062        // client's vector, which for `Q8` is a code a coordinate, the length the
13063        // vector arrived with and the scale the codes are measured against. The
13064        // name of the form is a simple string, which is a real server's shape,
13065        // and all four of these are a real server's answers.
13066        assert_eq!(
13067            f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
13068            "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
13069        );
13070        // A blob that is not a whole number of floats is not a vector.
13071        assert_eq!(
13072            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
13073            "-ERR invalid vector specification\r\n"
13074        );
13075        // Neither is a count that promises more than arrived.
13076        assert_eq!(
13077            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
13078            "-ERR syntax error\r\n"
13079        );
13080        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
13081    }
13082
13083    // ----------------------------------------------------------------- bloom
13084
13085    /// The filter a client gets when it does not describe one, and the two
13086    /// answers an add can give.
13087    #[test]
13088    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
13089        let mut f = Fixture::new();
13090        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
13091        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
13092        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
13093        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
13094        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
13095        // The defaults are the module's configs and not anything the command
13096        // said, which is 100 entries at a hundredth and a growth of 2.
13097        assert_eq!(
13098            f.run(&[b"BF.INFO", b"b"]),
13099            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
13100             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13101             +Expansion rate\r\n:2\r\n"
13102        );
13103        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
13104        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
13105        // A key that is not there has no filter to report on, and answers two
13106        // different ways about it depending on which command asked.
13107        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
13108        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
13109    }
13110
13111    /// `BF.EXISTS` on a key holding something else answers a miss, and
13112    /// everything else in the family answers `WRONGTYPE`.
13113    ///
13114    /// The two halves of a check and set disagree about what that key is, which
13115    /// is the module's behaviour and not a decision taken here.
13116    #[test]
13117    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
13118        let mut f = Fixture::new();
13119        f.run(&[b"SET", b"s", b"text"]);
13120        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
13121        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13122        for cmd in [
13123            vec![&b"BF.ADD"[..], b"s", b"x"],
13124            vec![&b"BF.MADD"[..], b"s", b"x"],
13125            vec![&b"BF.CARD"[..], b"s"],
13126            vec![&b"BF.INFO"[..], b"s"],
13127            vec![&b"BF.DEBUG"[..], b"s"],
13128            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
13129        ] {
13130            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13131            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13132        }
13133        // The arguments are read before the key is, so a reserve with a bad
13134        // error rate complains about the rate and never learns about the string.
13135        assert_eq!(
13136            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
13137            "-ERR bad error rate\r\n"
13138        );
13139        assert!(
13140            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
13141                .starts_with("-WRONGTYPE")
13142        );
13143    }
13144
13145    /// A chain grows by its expansion factor and each link is half as wrong as
13146    /// the one before, which is what makes the whole filter hold its rate.
13147    #[test]
13148    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
13149        let mut f = Fixture::new();
13150        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
13151        for i in 0..10u32 {
13152            assert_eq!(
13153                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
13154                ":1\r\n"
13155            );
13156        }
13157        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
13158        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
13159        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
13160        // Capacity is the sum of every link and not the number that was asked
13161        // for, so it is 10 and then 10 plus 20.
13162        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
13163        assert_eq!(
13164            f.run(&[b"BF.DEBUG", b"g"]),
13165            "*3\r\n$7\r\nsize:11\r\n\
13166             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
13167             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
13168        );
13169
13170        // The same filter told not to grow fills instead.
13171        assert_eq!(
13172            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
13173            "+OK\r\n"
13174        );
13175        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
13176        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
13177        assert_eq!(
13178            f.run(&[b"BF.ADD", b"n", b"c"]),
13179            "-ERR non scaling filter is full\r\n"
13180        );
13181        // And an item that is already in it still answers, because membership
13182        // is checked before fullness.
13183        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
13184        // A filter that will not grow has no expansion rate to report, in
13185        // either of the two spellings that make one.
13186        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
13187        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
13188        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
13189        // Asking for both at once is refused, which is one of the module's
13190        // errors that carries no prefix at all.
13191        assert_eq!(
13192            f.run(&[
13193                b"BF.RESERVE",
13194                b"q",
13195                b"0.01",
13196                b"2",
13197                b"NONSCALING",
13198                b"EXPANSION",
13199                b"2"
13200            ]),
13201            "-Nonscaling filters cannot expand\r\n"
13202        );
13203    }
13204
13205    /// A multi add stops where the filter did, so the reply can be shorter than
13206    /// the argument list.
13207    #[test]
13208    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
13209        let mut f = Fixture::new();
13210        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
13211        assert_eq!(
13212            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
13213            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
13214        );
13215        assert_eq!(
13216            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
13217            "*2\r\n:1\r\n:0\r\n"
13218        );
13219    }
13220
13221    /// `BF.INSERT` describes a filter and fills it in one command, with its own
13222    /// spelling of every complaint.
13223    #[test]
13224    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
13225        let mut f = Fixture::new();
13226        assert_eq!(
13227            f.run(&[
13228                b"BF.INSERT",
13229                b"i",
13230                b"CAPACITY",
13231                b"50",
13232                b"ERROR",
13233                b"0.001",
13234                b"ITEMS",
13235                b"a",
13236                b"b"
13237            ]),
13238            "*2\r\n:1\r\n:1\r\n"
13239        );
13240        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
13241        // NOCREATE is the only way to add without making the key.
13242        assert_eq!(
13243            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13244            "-ERR not found\r\n"
13245        );
13246        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13247        // The same mistakes as BF.RESERVE, in the sentences this command uses
13248        // for them, and one sentence where BF.RESERVE has two.
13249        assert_eq!(
13250            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13251            "-Bad capacity\r\n"
13252        );
13253        assert_eq!(
13254            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
13255            "-Bad error rate\r\n"
13256        );
13257        assert_eq!(
13258            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
13259            "-Bad expansion\r\n"
13260        );
13261        // An option is matched on its first letter and not on the word, so a
13262        // token nobody meant as an option is one anyway if it starts with the
13263        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
13264        // builds says so.
13265        assert_eq!(
13266            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
13267            "*1\r\n:1\r\n"
13268        );
13269        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
13270        // Only E and N need a second look, one for ERROR against EXPANSION and
13271        // the other for NOCREATE against NONSCALING, and both stop as soon as
13272        // they can tell the two apart.
13273        assert_eq!(
13274            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
13275            "*1\r\n:1\r\n"
13276        );
13277        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
13278        assert_eq!(
13279            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
13280            "*1\r\n:1\r\n"
13281        );
13282        assert_eq!(
13283            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
13284            "-ERR not found\r\n"
13285        );
13286        // A letter that starts nothing is the one case that is refused.
13287        assert_eq!(
13288            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13289            "-Unknown argument received\r\n"
13290        );
13291        // Everything after ITEMS is an item, even when it spells an option.
13292        assert_eq!(
13293            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13294            "*1\r\n:1\r\n"
13295        );
13296        // And ITEMS with nothing after it is the same as leaving it out.
13297        assert!(
13298            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
13299                .contains("wrong number of arguments")
13300        );
13301    }
13302
13303    /// A filter dumped a chunk at a time and put back into another key is the
13304    /// same filter.
13305    #[test]
13306    fn a_dump_replays_into_a_filter_that_answers_the_same() {
13307        let mut f = Fixture::new();
13308        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
13309        for i in 0..25u32 {
13310            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
13311        }
13312        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
13313
13314        // Iterator zero asks for the header and every one after it is a running
13315        // byte offset, and a chunk never spans two links.
13316        let mut iter = b"0".to_vec();
13317        let mut chunks = 0;
13318        loop {
13319            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
13320            let text = String::from_utf8_lossy(&raw).into_owned();
13321            let next = text
13322                .split("\r\n")
13323                .nth(1)
13324                .and_then(|n| n.strip_prefix(':'))
13325                .expect("a two element reply of an iterator and a chunk")
13326                .to_owned();
13327            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13328            let data = &body[body
13329                .windows(2)
13330                .position(|w| w == b"\r\n")
13331                .expect("a length line")
13332                + 2..body.len() - 2];
13333            if next == "0" {
13334                assert!(data.is_empty(), "the last chunk is empty");
13335                break;
13336            }
13337            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
13338            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
13339            iter = next.into_bytes();
13340            chunks += 1;
13341        }
13342        assert_eq!(chunks, 3, "a header and one chunk per link");
13343
13344        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
13345        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
13346        for i in 0..25u32 {
13347            assert_eq!(
13348                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
13349                ":1\r\n"
13350            );
13351        }
13352
13353        // A header on top of a filter is refused rather than merged, and so is
13354        // one that no filter wrote.
13355        assert_eq!(
13356            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
13357            "-ERR received bad data\r\n"
13358        );
13359        assert_eq!(
13360            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
13361            "-ERR received bad data\r\n"
13362        );
13363        // An offset past the end of the filter names itself.
13364        assert_eq!(
13365            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
13366            "-ERR invalid offset - no link found\r\n"
13367        );
13368        assert_eq!(
13369            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
13370            "-ERR Second argument must be numeric\r\n"
13371        );
13372        // The same complaint without the prefix on the way out, which is the
13373        // module's inconsistency and not a slip here.
13374        assert_eq!(
13375            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
13376            "-Second argument must be numeric\r\n"
13377        );
13378    }
13379
13380    /// The argument checks, which have a sentence each and read numbers the way
13381    /// Redis reads them everywhere else.
13382    #[test]
13383    fn reserve_reads_its_numbers_the_way_string2ll_does() {
13384        let mut f = Fixture::new();
13385        for (args, want) in [
13386            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
13387            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
13388            (
13389                vec![&b"0"[..], b"10"],
13390                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13391            ),
13392            (
13393                vec![&b"1"[..], b"10"],
13394                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13395            ),
13396            (
13397                vec![&b"inf"[..], b"10"],
13398                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
13399            ),
13400            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
13401            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
13402            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
13403            (
13404                vec![&b"0.01"[..], b"0"],
13405                "-ERR capacity must be in the range [1, 1073741824]\r\n",
13406            ),
13407            (
13408                vec![&b"0.01"[..], b"1073741825"],
13409                "-ERR capacity must be in the range [1, 1073741824]\r\n",
13410            ),
13411        ] {
13412            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
13413            cmd.extend(args.iter().copied());
13414            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
13415        }
13416        assert_eq!(
13417            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
13418            "-ERR no expansion\r\n"
13419        );
13420        assert_eq!(
13421            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
13422            "-ERR bad expansion\r\n"
13423        );
13424        assert_eq!(
13425            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
13426            "-ERR expansion must be in the range [0, 32768]\r\n"
13427        );
13428        // Trailing rubbish after the capacity is ignored rather than refused.
13429        assert_eq!(
13430            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
13431            "+OK\r\n"
13432        );
13433        assert_eq!(
13434            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
13435            "-ERR item exists\r\n"
13436        );
13437        assert_eq!(
13438            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
13439            "-Invalid information value\r\n"
13440        );
13441        assert!(
13442            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
13443                .contains("wrong number of arguments")
13444        );
13445    }
13446
13447    /// The RESP3 shapes, which are where this family differs most from RESP2.
13448    #[test]
13449    fn the_bloom_family_answers_in_resp3_spelling_too() {
13450        let mut f = Fixture::new();
13451        f.out.set_proto(Proto::Resp3);
13452        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
13453        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
13454        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
13455        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
13456        assert_eq!(
13457            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
13458            "*2\r\n#t\r\n#f\r\n"
13459        );
13460        // The count stays an integer, because it counts rather than answers.
13461        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
13462        assert_eq!(
13463            f.run(&[b"BF.INFO", b"b"]),
13464            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
13465             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
13466             +Expansion rate\r\n:2\r\n"
13467        );
13468        // One field is a map of one here and a bare array of one on RESP2, so
13469        // this is the reply where the two protocols carry different facts.
13470        assert_eq!(
13471            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
13472            "%1\r\n+Capacity\r\n:100\r\n"
13473        );
13474    }
13475
13476    // ---------------------------------------------------------------- cuckoo
13477
13478    /// A dump header, which is the four counts and the three widths a filter
13479    /// writes in front of its fingerprints.
13480    ///
13481    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
13482    /// tests below want out of it is the states a filter cannot be put into
13483    /// from the wire.
13484    fn cf_header(
13485        items: u64,
13486        buckets: u64,
13487        deletes: u64,
13488        filters: u64,
13489        geometry: [u16; 3],
13490    ) -> Vec<u8> {
13491        let mut out = Vec::with_capacity(38);
13492        for n in [items, buckets, deletes, filters] {
13493            out.extend_from_slice(&n.to_le_bytes());
13494        }
13495        for n in geometry {
13496            out.extend_from_slice(&n.to_le_bytes());
13497        }
13498        out
13499    }
13500
13501    /// The filter a client gets when it does not describe one, and the thing a
13502    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
13503    /// take them out again.
13504    #[test]
13505    fn cf_add_makes_the_filter_and_counts_the_copies() {
13506        let mut f = Fixture::new();
13507        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13508        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
13509        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
13510        // The NX form is the one that looks first, which is why it is a command
13511        // of its own rather than an option.
13512        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
13513        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
13514        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
13515        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
13516        assert_eq!(
13517            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
13518            "*2\r\n:1\r\n:0\r\n"
13519        );
13520        // The defaults are the module's configs: 1024 entries over buckets of
13521        // two, twenty kicks and a chain that grows by one.
13522        assert_eq!(
13523            f.run(&[b"CF.INFO", b"d"]),
13524            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13525             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
13526             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
13527             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13528        );
13529        assert_eq!(
13530            f.run(&[b"CF.DEBUG", b"d"]),
13531            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
13532             max_iterations:20 expansion:1\r\n"
13533        );
13534        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
13535        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13536
13537        // A delete takes one copy, so the same item goes twice and then stops.
13538        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13539        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
13540        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
13541        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
13542        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
13543
13544        // A key with no filter under it gets three different sentences and one
13545        // plain miss, depending on which command asked.
13546        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
13547        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
13548        assert_eq!(
13549            f.run(&[b"CF.COMPACT", b"gone"]),
13550            "-Cuckoo filter was not found\r\n"
13551        );
13552        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
13553        // And `CF.COMPACT` is declared as taking any number of keys and takes
13554        // exactly one, which is the module's own arity being wrong rather than
13555        // this table's.
13556        assert!(
13557            f.run(&[b"CF.COMPACT", b"a", b"b"])
13558                .contains("wrong number of arguments")
13559        );
13560    }
13561
13562    /// The four that only read fingerprints treat a key holding something else
13563    /// as a key with no filter, and everything else answers `WRONGTYPE`.
13564    #[test]
13565    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
13566        let mut f = Fixture::new();
13567        f.run(&[b"SET", b"s", b"text"]);
13568        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
13569        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
13570        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
13571        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
13572        // and is declared read only, so neither of the two halves of the family
13573        // is the same set as the flags say.
13574        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
13575        assert_eq!(
13576            f.run(&[b"CF.COMPACT", b"s"]),
13577            "-Cuckoo filter was not found\r\n"
13578        );
13579        for cmd in [
13580            vec![&b"CF.ADD"[..], b"s", b"x"],
13581            vec![&b"CF.ADDNX"[..], b"s", b"x"],
13582            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
13583            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
13584            vec![&b"CF.INFO"[..], b"s"],
13585            vec![&b"CF.DEBUG"[..], b"s"],
13586            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
13587            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
13588            vec![&b"CF.RESERVE"[..], b"s", b"64"],
13589        ] {
13590            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13591            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
13592        }
13593    }
13594
13595    /// `CF.RESERVE` reads its options by name in an order of its own, and the
13596    /// first pair with a given name is the only one it looks at.
13597    #[test]
13598    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
13599        let mut f = Fixture::new();
13600        assert_eq!(
13601            f.run(&[
13602                b"CF.RESERVE",
13603                b"r",
13604                b"64",
13605                b"BUCKETSIZE",
13606                b"1",
13607                b"MAXITERATIONS",
13608                b"7",
13609                b"EXPANSION",
13610                b"4"
13611            ]),
13612            "+OK\r\n"
13613        );
13614        assert_eq!(
13615            f.run(&[b"CF.DEBUG", b"r"]),
13616            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13617             max_iterations:7 expansion:4\r\n"
13618        );
13619        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13620
13621        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13622        assert_eq!(
13623            f.run(&[b"CF.RESERVE", b"q", b"1"]),
13624            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13625        );
13626        // The range is the bucket size's and not a constant, so a capacity that
13627        // was fine at two slots a bucket is not at four.
13628        assert_eq!(
13629            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13630            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13631        );
13632        assert_eq!(
13633            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13634            "+OK\r\n"
13635        );
13636
13637        // The capacity is checked last, so a command that is wrong twice
13638        // answers about the option. Which option it answers about is the order
13639        // the module looks for them in and not the order they were written, so
13640        // a bad kick budget wins over a bad bucket size wherever the two sit.
13641        assert_eq!(
13642            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13643            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13644        );
13645        assert_eq!(
13646            f.run(&[
13647                b"CF.RESERVE",
13648                b"q2",
13649                b"64",
13650                b"EXPANSION",
13651                b"xx",
13652                b"BUCKETSIZE",
13653                b"0"
13654            ]),
13655            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13656        );
13657        assert_eq!(
13658            f.run(&[
13659                b"CF.RESERVE",
13660                b"q2",
13661                b"64",
13662                b"MAXITERATIONS",
13663                b"0",
13664                b"BUCKETSIZE",
13665                b"0"
13666            ]),
13667            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13668        );
13669        // A second pair with a name that has already been read is not looked at
13670        // at all, so this one is a filter with buckets of one rather than an
13671        // error about a bucket size of zero.
13672        assert_eq!(
13673            f.run(&[
13674                b"CF.RESERVE",
13675                b"q3",
13676                b"64",
13677                b"BUCKETSIZE",
13678                b"1",
13679                b"BUCKETSIZE",
13680                b"0"
13681            ]),
13682            "+OK\r\n"
13683        );
13684        // A pair nobody knows is dropped, which is the opposite of what
13685        // `CF.INSERT` does with the same mistake.
13686        assert_eq!(
13687            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13688            "+OK\r\n"
13689        );
13690        assert_eq!(
13691            f.run(&[b"CF.DEBUG", b"q4"]),
13692            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13693             max_iterations:20 expansion:1\r\n"
13694        );
13695        // And an option with nothing after it leaves an odd number of them,
13696        // which is an arity error rather than a complaint about the option.
13697        assert!(
13698            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13699                .contains("wrong number of arguments")
13700        );
13701    }
13702
13703    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
13704    /// with `CF.RESERVE` about nothing.
13705    #[test]
13706    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13707        let mut f = Fixture::new();
13708        assert_eq!(
13709            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13710            "*2\r\n:1\r\n:1\r\n"
13711        );
13712        assert_eq!(
13713            f.run(&[b"CF.DEBUG", b"i"]),
13714            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13715             max_iterations:20 expansion:1\r\n"
13716        );
13717        // The NX form has three answers rather than two, which is why it stays
13718        // integers on both protocols.
13719        assert_eq!(
13720            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13721            "*2\r\n:0\r\n:1\r\n"
13722        );
13723        assert_eq!(
13724            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13725            "-ERR not found\r\n"
13726        );
13727        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13728
13729        assert_eq!(
13730            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13731            "-Bad capacity\r\n"
13732        );
13733        // The bucket size cannot be given here, so the range names the config
13734        // that holds it instead of the option `CF.RESERVE` names.
13735        assert_eq!(
13736            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13737            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13738        );
13739        // Every occurrence is checked, which is where this differs from
13740        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
13741        // one is the one that would have been used.
13742        assert_eq!(
13743            f.run(&[
13744                b"CF.INSERT",
13745                b"i",
13746                b"CAPACITY",
13747                b"8",
13748                b"CAPACITY",
13749                b"2",
13750                b"ITEMS",
13751                b"a"
13752            ]),
13753            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13754        );
13755        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
13756        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
13757        // refused.
13758        assert_eq!(
13759            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13760            "*1\r\n:1\r\n"
13761        );
13762        assert_eq!(
13763            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13764            "*1\r\n:1\r\n"
13765        );
13766        assert_eq!(
13767            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13768            "-Unknown argument received\r\n"
13769        );
13770        // Everything after ITEMS is an item, even when it spells an option.
13771        assert_eq!(
13772            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13773            "*1\r\n:1\r\n"
13774        );
13775        // And the two ways of sending no items at all are the same complaint.
13776        assert!(
13777            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13778                .contains("wrong number of arguments")
13779        );
13780        assert!(
13781            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13782                .contains("wrong number of arguments")
13783        );
13784    }
13785
13786    /// The two walls a filter can hit, which say different things and are not
13787    /// the same wall.
13788    #[test]
13789    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13790        let mut f = Fixture::new();
13791        f.run(&[
13792            b"CF.RESERVE",
13793            b"s",
13794            b"4",
13795            b"BUCKETSIZE",
13796            b"1",
13797            b"EXPANSION",
13798            b"0",
13799        ]);
13800        for i in 0..4u32 {
13801            assert_eq!(
13802                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13803                ":1\r\n"
13804            );
13805        }
13806        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13807        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13808        // The add commands say it in a sentence and the insert commands say it
13809        // in the array, one value per item, and the array is never short.
13810        assert_eq!(
13811            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13812            "*2\r\n:-1\r\n:-1\r\n"
13813        );
13814        assert_eq!(
13815            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13816            "*2\r\n:0\r\n:-1\r\n"
13817        );
13818
13819        // A chain that is allowed to grow stops for a different reason, and the
13820        // count it stops at is the filter limit rather than the room: this one
13821        // gives up with three slots free. Loading a chain that already has
13822        // every filter it is allowed shows why, since it refuses an item
13823        // straight into an empty one.
13824        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13825        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13826        assert_eq!(
13827            f.run(&[b"CF.ADD", b"g", b"q"]),
13828            "-Maximum expansions reached\r\n"
13829        );
13830        assert_eq!(
13831            f.run(&[b"CF.INFO", b"g"]),
13832            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13833             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13834             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13835             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13836        );
13837    }
13838
13839    /// A filter dumped a chunk at a time and put back under another key is the
13840    /// same filter, and the headers that describe one nobody could build are
13841    /// refused on the way in.
13842    #[test]
13843    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13844        let mut f = Fixture::new();
13845        f.run(&[
13846            b"CF.RESERVE",
13847            b"src",
13848            b"8",
13849            b"BUCKETSIZE",
13850            b"2",
13851            b"EXPANSION",
13852            b"2",
13853        ]);
13854        for i in 0..40u32 {
13855            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13856        }
13857        // Position zero asks for the header and every one after it is a byte
13858        // offset across every filter laid end to end, and the walk ends on a
13859        // zero and a nil rather than an empty chunk.
13860        let mut pos = b"0".to_vec();
13861        let mut chunks = 0;
13862        loop {
13863            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13864            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13865            let next = head
13866                .split("\r\n")
13867                .nth(1)
13868                .and_then(|n| n.strip_prefix(':'))
13869                .expect("a two element reply of a position and a chunk")
13870                .to_owned();
13871            if next == "0" {
13872                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13873                break;
13874            }
13875            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13876            let at = body
13877                .windows(2)
13878                .position(|w| w == b"\r\n")
13879                .expect("a length line")
13880                + 2;
13881            let data = &body[at..body.len() - 2];
13882            assert_eq!(
13883                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13884                "+OK\r\n",
13885                "loading chunk {chunks}"
13886            );
13887            pos = next.into_bytes();
13888            chunks += 1;
13889        }
13890        assert!(chunks >= 2, "a header and at least one chunk");
13891
13892        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13893        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13894        for i in 0..40u32 {
13895            assert_eq!(
13896                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13897                ":1\r\n"
13898            );
13899        }
13900
13901        // A filter with nothing in it hands out no header at all, so a client
13902        // that dumps one has nothing to load back.
13903        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13904        assert_eq!(
13905            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13906            "*2\r\n:0\r\n$-1\r\n"
13907        );
13908
13909        // The positions this end will not take, which are not the same set at
13910        // both ends: a dump refuses a negative one and a load takes it as an
13911        // offset and fails to find anything there.
13912        assert_eq!(
13913            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13914            "-Invalid position\r\n"
13915        );
13916        assert_eq!(
13917            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13918            "-Invalid position\r\n"
13919        );
13920        assert_eq!(
13921            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13922            "-Invalid position\r\n"
13923        );
13924        assert_eq!(
13925            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13926            "-Couldn't load chunk!\r\n"
13927        );
13928        // A header on top of a filter is refused rather than merged.
13929        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13930        assert_eq!(
13931            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13932            "-ERR item exists\r\n"
13933        );
13934        // A chunk that is not the size of a header where a header should have
13935        // been is one sentence, and one that is the size of a header and
13936        // describes a filter nobody could build is another.
13937        assert_eq!(
13938            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13939            "-Invalid header\r\n"
13940        );
13941        for (why, bad) in [
13942            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13943            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13944            (
13945                "a bucket count that is not a power of two",
13946                cf_header(0, 3, 0, 1, [2, 20, 1]),
13947            ),
13948            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13949            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13950            (
13951                "a growth nobody could reach",
13952                cf_header(0, 8, 0, 1, [2, 20, 32769]),
13953            ),
13954            (
13955                "a chain that cannot grow and did",
13956                cf_header(0, 8, 0, 2, [2, 20, 0]),
13957            ),
13958            // The count is written in eight bytes and read into two, so a
13959            // number that is a multiple of the second arrives as none.
13960            (
13961                "a filter count that wraps",
13962                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13963            ),
13964        ] {
13965            assert_eq!(
13966                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13967                "-Couldn't create filter!\r\n",
13968                "{why}"
13969            );
13970        }
13971    }
13972
13973    /// The RESP3 shapes, which are where this family differs most from RESP2
13974    /// and where one of its answers stops being readable.
13975    #[test]
13976    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13977        let mut f = Fixture::new();
13978        f.out.set_proto(Proto::Resp3);
13979        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13980        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13981        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13982        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13983        assert_eq!(
13984            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13985            "*2\r\n#t\r\n#f\r\n"
13986        );
13987        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13988        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13989        // The count stays an integer, because it counts rather than answers.
13990        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13991        assert_eq!(
13992            f.run(&[b"CF.INFO", b"c"]),
13993            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13994             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13995             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13996             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13997        );
13998
13999        // `CF.INSERT` writes a boolean per item here and an integer per item on
14000        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
14001        // client cannot tell an item that did not fit from one that is already
14002        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
14003        f.run(&[
14004            b"CF.RESERVE",
14005            b"s",
14006            b"4",
14007            b"BUCKETSIZE",
14008            b"1",
14009            b"EXPANSION",
14010            b"0",
14011        ]);
14012        assert_eq!(
14013            f.run(&[
14014                b"CF.INSERT",
14015                b"s",
14016                b"ITEMS",
14017                b"a",
14018                b"b",
14019                b"c",
14020                b"d",
14021                b"e",
14022                b"f"
14023            ]),
14024            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
14025        );
14026        assert_eq!(
14027            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
14028            "*2\r\n:0\r\n:-1\r\n"
14029        );
14030        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
14031        // The end of a dump is a nil and not an empty chunk, which is one
14032        // underscore here and a negative length on RESP2.
14033        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
14034    }
14035
14036    // ------------------------------------------------------------------- cms
14037
14038    /// A sketch is made from either end, and both constructors look at the key
14039    /// before they look at their arguments.
14040    #[test]
14041    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
14042        let mut f = Fixture::new();
14043        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
14044        assert_eq!(
14045            f.run(&[b"CMS.INFO", b"d"]),
14046            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
14047        );
14048        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
14049        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
14050        // Two over the error rounded up, and the log of the probability over the
14051        // log of a half rounded up, which for these two is 200 by 6.
14052        assert_eq!(
14053            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
14054            "+OK\r\n"
14055        );
14056        assert_eq!(
14057            f.run(&[b"CMS.INFO", b"p"]),
14058            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
14059        );
14060        // The key is checked first, so a width of zero at a key that is already
14061        // there is about the key and not about the width.
14062        assert_eq!(
14063            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
14064            "-CMS: key already exists\r\n"
14065        );
14066        assert_eq!(
14067            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
14068            "-CMS: invalid width\r\n"
14069        );
14070        assert_eq!(
14071            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
14072            "-CMS: invalid depth\r\n"
14073        );
14074        assert_eq!(
14075            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
14076            "-CMS: invalid overestimation value\r\n"
14077        );
14078        assert_eq!(
14079            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
14080            "-CMS: invalid prob value\r\n"
14081        );
14082        // A probability whose float conversion is zero has no depth, and a width
14083        // past a signed sixty four bit integer has no width, and both are the
14084        // same sentence.
14085        assert_eq!(
14086            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
14087            "-CMS: invalid init arguments\r\n"
14088        );
14089        // And a sketch bigger than a gibibyte of counters is refused here where
14090        // the reference reserves address space nobody has touched, which is
14091        // D-47.
14092        assert_eq!(
14093            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
14094            "-CMS: Insufficient memory to create the key\r\n"
14095        );
14096        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
14097    }
14098
14099    /// Every pair is parsed before any of them lands, the counters saturate,
14100    /// and the count is a signed total of what was asked for.
14101    #[test]
14102    fn increments_are_parsed_whole_and_the_counters_saturate() {
14103        let mut f = Fixture::new();
14104        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
14105        assert_eq!(
14106            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
14107            "*2\r\n:3\r\n:4\r\n"
14108        );
14109        // An item that is incremented twice in one call sees its own first
14110        // increment in the reply to the second.
14111        assert_eq!(
14112            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
14113            "*2\r\n:4\r\n:5\r\n"
14114        );
14115        // A bad number anywhere means nothing at all is applied.
14116        assert_eq!(
14117            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
14118            "-CMS: Cannot parse number\r\n"
14119        );
14120        assert_eq!(
14121            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
14122            "-CMS: Number cannot be negative\r\n"
14123        );
14124        assert_eq!(
14125            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
14126            "*2\r\n:5\r\n:4\r\n"
14127        );
14128        // The counters stop at four billion and the item that stopped says so in
14129        // its own slot while the one beside it answers a number.
14130        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
14131        assert_eq!(
14132            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
14133            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
14134        );
14135        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
14136        // The count is what was asked for rather than what landed, and it is
14137        // signed, so a big enough total comes back negative.
14138        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
14139        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
14140        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
14141        assert_eq!(
14142            f.run(&[b"CMS.INFO", b"w"]),
14143            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
14144        );
14145        // An odd number of arguments after the key is an arity error and not a
14146        // syntax one.
14147        assert!(
14148            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
14149                .contains("wrong number of arguments")
14150        );
14151        assert_eq!(
14152            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
14153            "-CMS: key does not exist\r\n"
14154        );
14155        assert_eq!(
14156            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
14157            "-CMS: key does not exist\r\n"
14158        );
14159    }
14160
14161    /// A merge overwrites its destination, and it is worked out in full before
14162    /// any of it is written.
14163    #[test]
14164    fn a_merge_lands_whole_or_not_at_all() {
14165        let mut f = Fixture::new();
14166        for name in [&b"m1"[..], b"m2", b"dst"] {
14167            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
14168        }
14169        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
14170        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
14171        assert_eq!(
14172            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
14173            "+OK\r\n"
14174        );
14175        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
14176        // Overwritten and not added to, so the same merge twice is the same
14177        // answer twice.
14178        assert_eq!(
14179            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
14180            "+OK\r\n"
14181        );
14182        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
14183        assert_eq!(
14184            f.run(&[
14185                b"CMS.MERGE",
14186                b"dst",
14187                b"2",
14188                b"m1",
14189                b"m2",
14190                b"WEIGHTS",
14191                b"2",
14192                b"3"
14193            ]),
14194            "+OK\r\n"
14195        );
14196        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14197        // A cell times a weight is checked wide rather than wrapped, so this is
14198        // a refusal and the destination is left exactly as it was.
14199        assert_eq!(
14200            f.run(&[
14201                b"CMS.MERGE",
14202                b"dst",
14203                b"1",
14204                b"m1",
14205                b"WEIGHTS",
14206                b"4611686018427387904"
14207            ]),
14208            "-CMS: MERGE overflow\r\n"
14209        );
14210        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
14211        // The destination comes first, then the count, then the layout, then the
14212        // weights, then the sources one at a time.
14213        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
14214        assert_eq!(
14215            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
14216            "-CMS: key does not exist\r\n"
14217        );
14218        assert_eq!(
14219            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
14220            "-CMS: Number of keys must be positive\r\n"
14221        );
14222        assert_eq!(
14223            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
14224            "-CMS: wrong number of keys\r\n"
14225        );
14226        assert_eq!(
14227            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
14228            "-CMS: wrong number of keys/weights\r\n"
14229        );
14230        assert_eq!(
14231            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
14232            "-CMS: width/depth is not equal\r\n"
14233        );
14234        assert_eq!(
14235            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
14236            "-CMS: key does not exist\r\n"
14237        );
14238    }
14239
14240    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
14241    /// a sketch is refused by the two commands that would have to serialise it.
14242    #[test]
14243    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14244        let mut f = Fixture::new();
14245        f.run(&[b"SET", b"s", b"text"]);
14246        for cmd in [
14247            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
14248            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
14249            vec![&b"CMS.QUERY"[..], b"s", b"a"],
14250            vec![&b"CMS.INFO"[..], b"s"],
14251            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
14252        ] {
14253            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14254            let reply = f.run(&cmd);
14255            // The two constructors see the key before anything else and say so
14256            // in the module's own words, and the rest are `WRONGTYPE`.
14257            assert!(
14258                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
14259                "{name}: {reply}"
14260            );
14261        }
14262        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
14263        // Redis refuses to copy a module key that has no copy callback, and
14264        // these are its words rather than ours. `DUMP` is the other half of
14265        // D-48: the reference has a payload for one of these and we do not.
14266        assert_eq!(
14267            f.run(&[b"COPY", b"c", b"c2"]),
14268            "-ERR not supported for this module key\r\n"
14269        );
14270        assert_eq!(
14271            f.run(&[b"DUMP", b"c"]),
14272            "-ERR DUMP is not supported for this module key\r\n"
14273        );
14274        // A graph is nobody's module and keeps its own sentence.
14275        f.run(&[b"G.NADD", b"g", b"a"]);
14276        assert_eq!(
14277            f.run(&[b"COPY", b"g", b"g2"]),
14278            "-ERR COPY is not supported for a graph\r\n"
14279        );
14280        assert_eq!(
14281            f.run(&[b"DUMP", b"g"]),
14282            "-ERR DUMP is not supported for a graph\r\n"
14283        );
14284        // Everything that does not need a byte shape works on a sketch key the
14285        // way it works on any other.
14286        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
14287        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
14288        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
14289        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
14290        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
14291    }
14292
14293    // ------------------------------------------------------------------ topk
14294
14295    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
14296    /// it looks at any of them.
14297    #[test]
14298    fn a_reserve_takes_three_arguments_or_six() {
14299        let mut f = Fixture::new();
14300        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
14301        assert_eq!(
14302            f.run(&[b"TOPK.INFO", b"t"]),
14303            "*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"
14304        );
14305        // Four arguments and five are an arity error rather than a defaulted
14306        // depth or decay.
14307        for cmd in [
14308            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
14309            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
14310        ] {
14311            assert!(f.run(&cmd).contains("wrong number of arguments"));
14312        }
14313        assert_eq!(
14314            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
14315            "+OK\r\n"
14316        );
14317        // The key is checked first, so a reserve with nothing else right at a
14318        // key that is taken still says the key is taken.
14319        assert_eq!(
14320            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
14321            "-TopK: key already exists\r\n"
14322        );
14323        assert_eq!(
14324            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
14325            "-TopK: invalid k\r\n"
14326        );
14327        assert_eq!(
14328            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
14329            "-TopK: invalid width\r\n"
14330        );
14331        assert_eq!(
14332            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
14333            "-TopK: invalid depth\r\n"
14334        );
14335        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
14336        assert_eq!(
14337            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
14338            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
14339        );
14340        assert_eq!(
14341            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
14342            "+OK\r\n"
14343        );
14344        // Past the cap, with the one sentence in the family that has a prefix.
14345        assert_eq!(
14346            f.run(&[
14347                b"TOPK.RESERVE",
14348                b"w",
14349                b"1",
14350                b"4294967295",
14351                b"4294967295",
14352                b"0.9"
14353            ]),
14354            "-ERR Insufficient memory to create topk data structure\r\n"
14355        );
14356    }
14357
14358    /// What the sketch keeps, and the three ways of asking about it.
14359    #[test]
14360    fn the_kept_set_is_what_query_and_list_answer_from() {
14361        let mut f = Fixture::new();
14362        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
14363        // A null an item while there is room, then the name of whatever was
14364        // pushed out.
14365        assert_eq!(
14366            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
14367            "*2\r\n$-1\r\n$-1\r\n"
14368        );
14369        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
14370        // Two slots are full and `c` arrives with a count of one, which is not
14371        // under the smallest kept count, so it takes that slot straight away.
14372        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
14373        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
14374        assert_eq!(
14375            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
14376            "*3\r\n:1\r\n:0\r\n:1\r\n"
14377        );
14378        // The table still counts what the kept set let go of.
14379        assert_eq!(
14380            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14381            "*3\r\n:11\r\n:1\r\n:6\r\n"
14382        );
14383        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
14384        assert_eq!(
14385            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
14386            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
14387        );
14388        // Any prefix of the keyword turns the counts on, the empty string
14389        // included, and only a longer word or a different one is refused.
14390        assert_eq!(
14391            f.run(&[b"TOPK.LIST", b"t", b"w"]),
14392            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14393        );
14394        assert_eq!(
14395            f.run(&[b"TOPK.LIST", b"t", b""]),
14396            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
14397        );
14398        assert_eq!(
14399            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
14400            "-WITHCOUNT keyword expected\r\n"
14401        );
14402        // And the keyword is looked at before the key, so a missing key with a
14403        // bad keyword complains about the keyword.
14404        assert_eq!(
14405            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
14406            "-WITHCOUNT keyword expected\r\n"
14407        );
14408        assert_eq!(
14409            f.run(&[b"TOPK.LIST", b"missing"]),
14410            "-TopK: key does not exist\r\n"
14411        );
14412        // An item counted zero times is kept and not listed.
14413        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
14414        assert_eq!(
14415            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
14416            "*1\r\n$-1\r\n"
14417        );
14418        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
14419        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
14420    }
14421
14422    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
14423    /// before it counted, and the reply counts what it wrote.
14424    #[test]
14425    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
14426        let mut f = Fixture::new();
14427        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
14428        // Three pairs, the middle one bad: two elements come back, one of them
14429        // the error, and the array header says two rather than three. That last
14430        // part is D-51 and it is why a client here stays in step.
14431        assert_eq!(
14432            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
14433            format!(
14434                "*2\r\n$-1\r\n-{}\r\n",
14435                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
14436            )
14437        );
14438        assert_eq!(
14439            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
14440            "*3\r\n:3\r\n:0\r\n:0\r\n"
14441        );
14442        // A hundred thousand is in and one more is out.
14443        assert_eq!(
14444            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
14445            "*1\r\n$-1\r\n"
14446        );
14447        assert!(
14448            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
14449                .contains("smaller or equal to 100,000")
14450        );
14451        // Pairs have to be pairs.
14452        assert!(
14453            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
14454                .contains("wrong number of arguments")
14455        );
14456        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
14457    }
14458
14459    /// The RESP3 shapes, which are the two the protocols disagree about.
14460    #[test]
14461    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
14462        let mut f = Fixture::new();
14463        f.run(&[b"HELLO", b"3"]);
14464        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
14465        f.run(&[b"TOPK.ADD", b"t", b"a"]);
14466        assert_eq!(
14467            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
14468            "*2\r\n#t\r\n#f\r\n"
14469        );
14470        // The count stays an integer on both protocols.
14471        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
14472        assert_eq!(
14473            f.run(&[b"TOPK.INFO", b"t"]),
14474            "%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"
14475        );
14476        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
14477    }
14478
14479    /// A top k key answers the module sentences the other sketch families
14480    /// answer, and its own word for its type.
14481    #[test]
14482    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
14483        let mut f = Fixture::new();
14484        f.run(&[b"SET", b"s", b"text"]);
14485        for cmd in [
14486            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
14487            vec![&b"TOPK.ADD"[..], b"s", b"a"],
14488            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
14489            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
14490            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
14491            vec![&b"TOPK.LIST"[..], b"s"],
14492            vec![&b"TOPK.INFO"[..], b"s"],
14493        ] {
14494            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14495            let reply = f.run(&cmd);
14496            assert!(
14497                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
14498                "{name}: {reply}"
14499            );
14500        }
14501        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
14502        assert_eq!(
14503            f.run(&[b"COPY", b"t", b"t2"]),
14504            "-ERR not supported for this module key\r\n"
14505        );
14506        assert_eq!(
14507            f.run(&[b"DUMP", b"t"]),
14508            "-ERR DUMP is not supported for this module key\r\n"
14509        );
14510        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14511        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14512        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14513        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
14514        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14515        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14516        // Every one of the six that is not the constructor says the same thing
14517        // about a key that is not there.
14518        assert_eq!(
14519            f.run(&[b"TOPK.INFO", b"t3"]),
14520            "-TopK: key does not exist\r\n"
14521        );
14522    }
14523
14524    // --------------------------------------------------------------- tdigest
14525
14526    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
14527    /// search rather than a lookup.
14528    #[test]
14529    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
14530        let mut f = Fixture::new();
14531        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
14532        // A hundred is the default and the capacity is six times it plus ten.
14533        assert_eq!(
14534            f.run(&[b"TDIGEST.INFO", b"t"]),
14535            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
14536             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
14537             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
14538        );
14539        assert_eq!(
14540            f.run(&[b"TDIGEST.CREATE", b"t"]),
14541            "-ERR T-Digest: key already exists\r\n"
14542        );
14543        // Three arguments is an arity error and not a missing keyword.
14544        assert!(
14545            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
14546                .contains("wrong number of arguments")
14547        );
14548        assert_eq!(
14549            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
14550            "+OK\r\n"
14551        );
14552        assert_eq!(
14553            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
14554            "+OK\r\n"
14555        );
14556        // The word is looked for across both trailing arguments and the number
14557        // is then read out of the last one whatever was found, so this looks for
14558        // a number inside the word `COMPRESSION` and does not find one.
14559        assert_eq!(
14560            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
14561            "-ERR T-Digest: error parsing compression parameter\r\n"
14562        );
14563        assert_eq!(
14564            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
14565            "-ERR T-Digest: wrong keyword\r\n"
14566        );
14567        assert_eq!(
14568            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
14569            "-ERR T-Digest: error parsing compression parameter\r\n"
14570        );
14571        assert_eq!(
14572            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
14573            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
14574        );
14575        // The reference's own ceiling, which is where the capacity stops fitting
14576        // in an int, and one past it.
14577        assert_eq!(
14578            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
14579            "-ERR T-Digest: allocation failed\r\n"
14580        );
14581        // And ours, which is a gibibyte of centroids and is D-52.
14582        assert_eq!(
14583            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
14584            "-ERR T-Digest: allocation failed\r\n"
14585        );
14586        // The key is checked before the arguments, so a bad compression at a key
14587        // that is already a digest still says the key is taken.
14588        assert_eq!(
14589            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
14590            "-ERR T-Digest: key already exists\r\n"
14591        );
14592    }
14593
14594    /// The four samples every note about this family is written against, and the
14595    /// answers a real 8.10.1 gives for them.
14596    #[test]
14597    fn the_quantile_family_answers_what_the_module_answers() {
14598        let mut f = Fixture::new();
14599        f.run(&[b"TDIGEST.CREATE", b"s"]);
14600        assert_eq!(
14601            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
14602            "+OK\r\n"
14603        );
14604        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14605        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14606        // The cdf of a sample is the weight below it plus half its own.
14607        assert_eq!(
14608            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14609            "*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"
14610        );
14611        assert_eq!(
14612            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14613            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14614        );
14615        // Out of order, the walk restarts, and 0.5 answers 3 either way while
14616        // the two after it are read from the front again.
14617        assert_eq!(
14618            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14619            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14620        );
14621        assert_eq!(
14622            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14623            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14624        );
14625        assert_eq!(
14626            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14627            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14628        );
14629        assert_eq!(
14630            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14631            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14632        );
14633        assert_eq!(
14634            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14635            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14636        );
14637        assert_eq!(
14638            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14639            "$3\r\n2.5\r\n"
14640        );
14641        assert_eq!(
14642            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14643            "$3\r\n2.5\r\n"
14644        );
14645        // The ranges, which are separate sentences from the parse failures.
14646        assert_eq!(
14647            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14648            "-ERR T-Digest: quantile should be in [0,1]\r\n"
14649        );
14650        assert_eq!(
14651            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14652            "-ERR T-Digest: error parsing quantile\r\n"
14653        );
14654        assert_eq!(
14655            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14656            "-ERR T-Digest: error parsing cdf\r\n"
14657        );
14658        assert_eq!(
14659            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14660            "-ERR T-Digest: error parsing value\r\n"
14661        );
14662        assert_eq!(
14663            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14664            "-ERR T-Digest: rank needs to be non negative\r\n"
14665        );
14666        assert_eq!(
14667            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14668            "-ERR T-Digest: error parsing rank\r\n"
14669        );
14670        // Both cuts have their own parse sentence and share the range one, and
14671        // equal cuts are refused rather than answering nothing.
14672        assert_eq!(
14673            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14674            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14675        );
14676        assert_eq!(
14677            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14678            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14679        );
14680        assert_eq!(
14681            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14682            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14683        );
14684        assert_eq!(
14685            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14686            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14687        );
14688    }
14689
14690    /// An empty digest answers every question, and answers most of them with
14691    /// something that is not a number.
14692    #[test]
14693    fn an_empty_digest_has_an_answer_for_everything() {
14694        let mut f = Fixture::new();
14695        f.run(&[b"TDIGEST.CREATE", b"e"]);
14696        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14697        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14698        assert_eq!(
14699            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14700            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14701        );
14702        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14703        assert_eq!(
14704            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14705            "$3\r\nnan\r\n"
14706        );
14707        // Minus two, which is a number no rank on a digest with samples in it
14708        // can ever be.
14709        assert_eq!(
14710            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14711            "*2\r\n:-2\r\n:-2\r\n"
14712        );
14713        assert_eq!(
14714            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14715            "*2\r\n:-2\r\n:-2\r\n"
14716        );
14717        assert_eq!(
14718            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14719            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14720        );
14721        // A reset puts a digest with samples back into exactly this state.
14722        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14723        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14724        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14725        // Down to the compression count, so a reset digest and a fresh one of
14726        // the same compression report the same nine numbers.
14727        f.run(&[b"TDIGEST.CREATE", b"e2"]);
14728        assert_eq!(
14729            f.run(&[b"TDIGEST.INFO", b"e"]),
14730            f.run(&[b"TDIGEST.INFO", b"e2"])
14731        );
14732    }
14733
14734    /// The double parser is Redis's and not this engine's, and the two disagree
14735    /// at both ends of the range.
14736    #[test]
14737    fn a_sample_is_read_the_way_redis_reads_a_double() {
14738        let mut f = Fixture::new();
14739        f.run(&[b"TDIGEST.CREATE", b"a"]);
14740        // Overflow and underflow are parse failures rather than an infinity and
14741        // a zero, which is where this parts company with the rest of the engine.
14742        for bad in [
14743            &b"nan"[..],
14744            b"1e400",
14745            b"-1e400",
14746            b"1e309",
14747            b"1e-400",
14748            b"",
14749            b" 1",
14750            b"1 ",
14751            b"1e",
14752            b"--1",
14753        ] {
14754            assert_eq!(
14755                f.run(&[b"TDIGEST.ADD", b"a", bad]),
14756                "-ERR T-Digest: error parsing val parameter\r\n",
14757                "{}",
14758                String::from_utf8_lossy(bad)
14759            );
14760        }
14761        // An infinity spelled out parses and is then refused for being one, with
14762        // a different sentence.
14763        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14764            assert_eq!(
14765                f.run(&[b"TDIGEST.ADD", b"a", word]),
14766                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14767                "{}",
14768                String::from_utf8_lossy(word)
14769            );
14770        }
14771        // These all parse: hex, a bare point either side, and the smallest
14772        // subnormal the reference will take.
14773        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14774            assert_eq!(
14775                f.run(&[b"TDIGEST.ADD", b"a", good]),
14776                "+OK\r\n",
14777                "{}",
14778                String::from_utf8_lossy(good)
14779            );
14780        }
14781        // Nothing landed from the failures, so six samples is what there is.
14782        assert!(
14783            f.run(&[b"TDIGEST.INFO", b"a"])
14784                .contains("Observations\r\n:6\r\n")
14785        );
14786        // Every value is parsed before any is added, so this whole command is a
14787        // no op.
14788        assert_eq!(
14789            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14790            "-ERR T-Digest: error parsing val parameter\r\n"
14791        );
14792        assert!(
14793            f.run(&[b"TDIGEST.INFO", b"a"])
14794                .contains("Observations\r\n:6\r\n")
14795        );
14796    }
14797
14798    /// What a merge does to its destination, to its inputs and to the buffer
14799    /// split `TDIGEST.INFO` reports.
14800    #[test]
14801    fn a_merge_sweeps_the_destination_between_its_inputs() {
14802        let mut f = Fixture::new();
14803        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14804        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14805        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14806        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14807        assert_eq!(
14808            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14809            "+OK\r\n"
14810        );
14811        // The destination did not exist, so the compression is the largest of
14812        // the inputs. The three from the first input were swept in before the
14813        // three from the second arrived, which is the one visible effect of the
14814        // reference folding one input at a time.
14815        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14816        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14817        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14818        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14819        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14820        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14821        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14822        // Reading a source sweeps it too, so a merge writes to keys it only
14823        // reads from.
14824        assert!(
14825            f.run(&[b"TDIGEST.INFO", b"m1"])
14826                .contains("Merged nodes\r\n:3\r\n")
14827        );
14828        // Without OVERRIDE the destination joins its own inputs, so this takes
14829        // it to nine observations and keeps its own compression.
14830        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14831        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14832        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14833        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14834        // With OVERRIDE the old destination is dropped and the compression goes
14835        // back to the largest of the inputs.
14836        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14837        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14838        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14839        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14840        // And COMPRESSION beats both.
14841        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14842        assert!(
14843            f.run(&[b"TDIGEST.INFO", b"d"])
14844                .contains("Compression\r\n:500\r\n")
14845        );
14846        // Naming the destination as a source folds it in twice.
14847        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14848        assert!(
14849            f.run(&[b"TDIGEST.INFO", b"d"])
14850                .contains("Observations\r\n:12\r\n")
14851        );
14852        // The arguments, in the order the reference checks them.
14853        assert_eq!(
14854            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14855            "-ERR T-Digest: error parsing numkeys\r\n"
14856        );
14857        assert_eq!(
14858            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14859            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14860        );
14861        assert!(
14862            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14863                .contains("wrong number of arguments")
14864        );
14865        assert!(
14866            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14867                .contains("wrong number of arguments")
14868        );
14869        assert_eq!(
14870            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14871            "-ERR T-Digest: wrong keyword\r\n"
14872        );
14873        // A source that is not there stops the whole thing, and the destination
14874        // is left as it was.
14875        assert_eq!(
14876            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14877            "-ERR T-Digest: key does not exist\r\n"
14878        );
14879        assert!(
14880            f.run(&[b"TDIGEST.INFO", b"d"])
14881                .contains("Observations\r\n:12\r\n")
14882        );
14883        // A destination that is not there and is also named as a source is the
14884        // same sentence rather than an empty merge.
14885        assert_eq!(
14886            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14887            "-ERR T-Digest: key does not exist\r\n"
14888        );
14889    }
14890
14891    /// The RESP3 shapes, which are the two the protocols disagree about.
14892    #[test]
14893    fn a_digest_answers_doubles_and_a_map_on_resp3() {
14894        let mut f = Fixture::new();
14895        f.run(&[b"HELLO", b"3"]);
14896        f.run(&[b"TDIGEST.CREATE", b"s"]);
14897        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14898        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14899        assert_eq!(
14900            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14901            "*2\r\n,1\r\n,4\r\n"
14902        );
14903        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14904        // The two infinities and the NaN go out as the bare words.
14905        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14906        assert_eq!(
14907            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14908            "*1\r\n,-inf\r\n"
14909        );
14910        f.run(&[b"TDIGEST.CREATE", b"e"]);
14911        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14912        // The ranks stay integers on both protocols.
14913        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14914        // Every question above swept the buffer in, so the four samples are all
14915        // merged by now and the compression count says it happened once.
14916        assert_eq!(
14917            f.run(&[b"TDIGEST.INFO", b"s"]),
14918            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14919             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14920             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14921        );
14922    }
14923
14924    /// A t digest key answers the module sentences the other sketch families
14925    /// answer, and its own word for its type.
14926    #[test]
14927    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14928        let mut f = Fixture::new();
14929        f.run(&[b"SET", b"s", b"text"]);
14930        for cmd in [
14931            vec![&b"TDIGEST.CREATE"[..], b"s"],
14932            vec![&b"TDIGEST.RESET"[..], b"s"],
14933            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14934            vec![&b"TDIGEST.MIN"[..], b"s"],
14935            vec![&b"TDIGEST.MAX"[..], b"s"],
14936            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14937            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14938            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14939            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14940            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14941            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14942            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14943            vec![&b"TDIGEST.INFO"[..], b"s"],
14944        ] {
14945            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14946            let reply = f.run(&cmd);
14947            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14948        }
14949        // The merge checks its destination the same way, and its sources too.
14950        f.run(&[b"TDIGEST.CREATE", b"t"]);
14951        assert!(
14952            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14953                .starts_with("-WRONGTYPE")
14954        );
14955        assert!(
14956            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14957                .starts_with("-WRONGTYPE")
14958        );
14959        assert_eq!(
14960            f.run(&[b"COPY", b"t", b"t2"]),
14961            "-ERR not supported for this module key\r\n"
14962        );
14963        assert_eq!(
14964            f.run(&[b"DUMP", b"t"]),
14965            "-ERR DUMP is not supported for this module key\r\n"
14966        );
14967        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14968        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14969        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14970        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14971        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14972        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14973        // An empty digest is still a key, so the twelve that are not the
14974        // constructor all say the same thing once it is gone.
14975        assert_eq!(
14976            f.run(&[b"TDIGEST.INFO", b"t3"]),
14977            "-ERR T-Digest: key does not exist\r\n"
14978        );
14979        // The key is looked at before the arguments, so a bad argument at a key
14980        // that is not there still says the key is not there.
14981        assert_eq!(
14982            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14983            "-ERR T-Digest: key does not exist\r\n"
14984        );
14985    }
14986
14987    // -------------------------------------------------------------------- ts
14988
14989    /// A `TS.INFO` reply with the memory usage taken out of it.
14990    ///
14991    /// That number is what a series costs here rather than what one costs in the
14992    /// module, which is D-53, and it moves whenever the layout of a chunk does.
14993    /// Everything either side of it is the wire contract and is worth pinning
14994    /// down exactly, so the tests below check the whole reply with the one
14995    /// number lifted out.
14996    fn without_memory(reply: &str) -> String {
14997        let head = "+memoryUsage\r\n:";
14998        let at = reply.find(head).expect("every TS.INFO reports memory");
14999        let rest = &reply[at + head.len()..];
15000        let end = rest.find("\r\n").expect("and it is a whole number");
15001        format!("{}{}", &reply[..at + head.len()], &rest[end..])
15002    }
15003
15004    /// A series is made empty and still says it has a chunk, and the options are
15005    /// read before the key is looked at.
15006    #[test]
15007    fn a_series_is_made_empty_and_reports_on_itself() {
15008        let mut f = Fixture::new();
15009        assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
15010        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
15011        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
15012        // Fourteen fields, so twenty eight elements. An empty series reports one
15013        // chunk and zero at both ends, and neither the chunk type nor the
15014        // duplicate policy is ever a nil.
15015        assert_eq!(
15016            without_memory(&f.run(&[b"TS.INFO", b"t"])),
15017            "*28\r\n\
15018             +totalSamples\r\n:0\r\n\
15019             +memoryUsage\r\n:\r\n\
15020             +firstTimestamp\r\n:0\r\n\
15021             +lastTimestamp\r\n:0\r\n\
15022             +retentionTime\r\n:0\r\n\
15023             +chunkCount\r\n:1\r\n\
15024             +chunkSize\r\n:4096\r\n\
15025             +chunkType\r\n+compressed\r\n\
15026             +duplicatePolicy\r\n+block\r\n\
15027             +labels\r\n*0\r\n\
15028             +sourceKey\r\n$-1\r\n\
15029             +rules\r\n*0\r\n\
15030             +ignoreMaxTimeDiff\r\n:0\r\n\
15031             +ignoreMaxValDiff\r\n$1\r\n0\r\n"
15032        );
15033        // A key that is already there is about the key whatever it holds, and
15034        // the existence is what is checked rather than the type.
15035        assert_eq!(
15036            f.run(&[b"TS.CREATE", b"t"]),
15037            "-ERR TSDB: key already exists\r\n"
15038        );
15039        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15040        assert_eq!(
15041            f.run(&[b"TS.CREATE", b"str"]),
15042            "-ERR TSDB: key already exists\r\n"
15043        );
15044        // But the arguments are read first, so a bad one at a key that is there
15045        // answers about the argument.
15046        assert_eq!(
15047            f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
15048            "-ERR TSDB: Couldn't parse RETENTION\r\n"
15049        );
15050        // The seven that will not make a series say WRONGTYPE about a key
15051        // holding something else, where the two that would say a sentence.
15052        // The word is inside the sentence and not in front of it, because the
15053        // module writes its own error text and Redis puts ERR on the front of
15054        // anything a module writes.
15055        let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
15056        assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
15057        assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
15058        assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
15059        assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
15060        assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
15061        assert_eq!(
15062            f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
15063            "-ERR TSDB: the key is not a TSDB key\r\n"
15064        );
15065        // And the ones that will not make one say so about a key that is gone.
15066        assert_eq!(
15067            f.run(&[b"TS.INFO", b"nope"]),
15068            "-ERR TSDB: the key does not exist\r\n"
15069        );
15070        assert_eq!(
15071            f.run(&[b"TS.GET", b"nope"]),
15072            "-ERR TSDB: the key does not exist\r\n"
15073        );
15074        assert_eq!(
15075            f.run(&[b"TS.ALTER", b"nope"]),
15076            "-ERR TSDB: the key does not exist\r\n"
15077        );
15078        assert_eq!(
15079            f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
15080            "-ERR TSDB: the key does not exist\r\n"
15081        );
15082    }
15083
15084    /// Every option word, including the ones that are wrong, and the scan that
15085    /// finds them.
15086    #[test]
15087    fn the_options_are_a_keyword_scan_and_not_a_grammar() {
15088        let mut f = Fixture::new();
15089        assert_eq!(
15090            f.run(&[
15091                b"TS.CREATE",
15092                b"t",
15093                b"RETENTION",
15094                b"5000",
15095                b"ENCODING",
15096                b"UNCOMPRESSED",
15097                b"CHUNK_SIZE",
15098                b"128",
15099                b"DUPLICATE_POLICY",
15100                b"LAST",
15101                b"IGNORE",
15102                b"10",
15103                b"0.5",
15104                b"LABELS",
15105                b"room",
15106                b"kitchen"
15107            ]),
15108            "+OK\r\n"
15109        );
15110        let info = f.run(&[b"TS.INFO", b"t"]);
15111        assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
15112        assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
15113        assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
15114        assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
15115        assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
15116        // A plain double here, where a sample value out of TS.GET is the
15117        // shortest digits that read back as the same number.
15118        assert!(
15119            info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
15120            "{info}"
15121        );
15122        assert!(
15123            info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
15124            "{info}"
15125        );
15126
15127        // A word that is not an option is read past rather than refused.
15128        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
15129        // LABELS eats everything after it in pairs, and the later scans still
15130        // look inside what it ate, so this sets a retention and stores a label
15131        // called RETENTION at the same time.
15132        assert_eq!(
15133            f.run(&[
15134                b"TS.CREATE",
15135                b"g",
15136                b"LABELS",
15137                b"a",
15138                b"b",
15139                b"RETENTION",
15140                b"5"
15141            ]),
15142            "+OK\r\n"
15143        );
15144        let greedy = f.run(&[b"TS.INFO", b"g"]);
15145        assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
15146        assert!(
15147            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"),
15148            "{greedy}"
15149        );
15150
15151        // Every way an option can be wrong, in the order the module reads them.
15152        assert_eq!(
15153            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
15154            "-ERR TSDB: Couldn't parse LABELS\r\n"
15155        );
15156        assert_eq!(
15157            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
15158            "-ERR TSDB: Couldn't parse LABELS\r\n"
15159        );
15160        assert_eq!(
15161            f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
15162            "-ERR TSDB: Couldn't parse RETENTION\r\n"
15163        );
15164        // A retention below zero is one of the two the module writes with no
15165        // ERR in front of it, where one that is not a number gets one.
15166        assert_eq!(
15167            f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
15168            "-TSDB: Couldn't parse RETENTION\r\n"
15169        );
15170        assert_eq!(
15171            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
15172            "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
15173        );
15174        assert_eq!(
15175            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
15176            "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
15177        );
15178        assert_eq!(
15179            f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
15180            "-ERR TSDB: unknown ENCODING parameter\r\n"
15181        );
15182        // And an ENCODING with nothing behind it is an arity error where every
15183        // other keyword in the same spot is a sentence.
15184        assert!(
15185            f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
15186                .contains("wrong number of arguments for 'ts.create' command")
15187        );
15188        assert_eq!(
15189            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
15190            "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
15191        );
15192        assert_eq!(
15193            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
15194            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15195        );
15196        assert_eq!(
15197            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
15198            "-ERR TSDB: Couldn't parse IGNORE\r\n"
15199        );
15200        assert_eq!(
15201            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
15202            "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
15203        );
15204        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
15205
15206        // An alter changes what was named and leaves the rest alone, and reads
15207        // an encoding only far enough to refuse a bad one.
15208        assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
15209        let after = f.run(&[b"TS.INFO", b"t"]);
15210        assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
15211        assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
15212        assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
15213        assert_eq!(
15214            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
15215            "-ERR TSDB: unknown ENCODING parameter\r\n"
15216        );
15217        // An encoding it does take is still not applied.
15218        assert_eq!(
15219            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
15220            "+OK\r\n"
15221        );
15222        assert!(
15223            f.run(&[b"TS.INFO", b"t"])
15224                .contains("+chunkType\r\n+uncompressed\r\n")
15225        );
15226    }
15227
15228    /// Samples go in, come back out and are refused for the reasons the module
15229    /// refuses them.
15230    #[test]
15231    fn samples_land_where_they_are_put_and_the_newest_comes_back() {
15232        let mut f = Fixture::new();
15233        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
15234        // The series was made on the way in.
15235        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
15236        assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
15237        // A sample value goes out as a simple string of the shortest digits
15238        // that read back as the same number.
15239        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
15240        assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
15241        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
15242        // An empty series has no newest sample and answers an empty array
15243        // rather than a nil.
15244        assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
15245        assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
15246
15247        // The value is read before the key, so a bad one against a key holding
15248        // a string is about the value.
15249        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15250        assert_eq!(
15251            f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
15252            "-ERR TSDB: invalid value\r\n"
15253        );
15254        // The grammar is tighter than the one a number argument usually gets:
15255        // no leading plus, no bare fraction, no infinity and nothing that does
15256        // not fit.
15257        for bad in [
15258            &b".5"[..],
15259            b"1.",
15260            b"+1",
15261            b" 1",
15262            b"0x10",
15263            b"inf",
15264            b"1e400",
15265            b"--1",
15266            b"1e",
15267        ] {
15268            assert_eq!(
15269                f.run(&[b"TS.ADD", b"v", b"1", bad]),
15270                "-ERR TSDB: invalid value\r\n",
15271                "{}",
15272                String::from_utf8_lossy(bad)
15273            );
15274        }
15275        // And a reading that is not a number is one of three words.
15276        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
15277
15278        // A timestamp that is not a number, and one that is and is below zero,
15279        // are two different sentences.
15280        assert_eq!(
15281            f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
15282            "-ERR TSDB: invalid timestamp\r\n"
15283        );
15284        assert_eq!(
15285            f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
15286            "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
15287        );
15288
15289        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
15290        // command beats what the series was told.
15291        assert_eq!(
15292            f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
15293            "-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"
15294        );
15295        assert_eq!(
15296            f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
15297            ":300\r\n"
15298        );
15299        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
15300        // ON_DUPLICATE is only read when the key was already there, which is
15301        // why a policy word that is not a policy passes on a fresh key.
15302        assert_eq!(
15303            f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
15304            ":1\r\n"
15305        );
15306        assert_eq!(
15307            f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
15308            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
15309        );
15310
15311        // Retention is exact and it is checked before anything else happens, so
15312        // a sample landing behind the window is refused rather than trimmed.
15313        assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
15314        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
15315        assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
15316        assert_eq!(
15317            f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
15318            "-ERR TSDB: Timestamp is older than retention\r\n"
15319        );
15320        // And the window trims as it moves.
15321        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
15322        assert!(
15323            f.run(&[b"TS.INFO", b"r"])
15324                .contains("+totalSamples\r\n:1\r\n")
15325        );
15326
15327        // An ignore window drops a sample close enough to the newest one to be
15328        // uninteresting, and answers the newest timestamp so a client can tell.
15329        assert_eq!(
15330            f.run(&[
15331                b"TS.CREATE",
15332                b"i",
15333                b"DUPLICATE_POLICY",
15334                b"LAST",
15335                b"IGNORE",
15336                b"10",
15337                b"0.5"
15338            ]),
15339            "+OK\r\n"
15340        );
15341        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
15342        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
15343        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
15344    }
15345
15346    /// Every triple in a `TS.MADD` is answered on its own, and none of them
15347    /// makes a series.
15348    #[test]
15349    fn a_madd_answers_each_triple_and_creates_nothing() {
15350        let mut f = Fixture::new();
15351        assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
15352        assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
15353        assert_eq!(
15354            f.run(&[
15355                b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
15356            ]),
15357            "*3\r\n:100\r\n:100\r\n:200\r\n"
15358        );
15359        // A key that is not a series is an error in its own slot and the ones
15360        // after it still land.
15361        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15362        assert_eq!(
15363            f.run(&[
15364                b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
15365            ]),
15366            "*3\r\n\
15367             -ERR TSDB: the key is not a TSDB key\r\n\
15368             -ERR TSDB: the key is not a TSDB key\r\n\
15369             :300\r\n"
15370        );
15371        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
15372        // A bad value and a bad timestamp are answered in their slots too.
15373        assert_eq!(
15374            f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
15375            "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
15376        );
15377        // And a list that is not made of triples is an arity error.
15378        assert!(
15379            f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
15380                .contains("wrong number of arguments for 'ts.madd' command")
15381        );
15382    }
15383
15384    /// The two increments, which only ever write forwards.
15385    #[test]
15386    fn an_increment_walks_the_newest_value_up_and_down() {
15387        let mut f = Fixture::new();
15388        assert_eq!(
15389            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15390            ":100\r\n"
15391        );
15392        assert_eq!(
15393            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
15394            ":100\r\n"
15395        );
15396        // Two on one timestamp add up rather than collide, because the sample
15397        // goes in under the last policy whatever the series says.
15398        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
15399        assert_eq!(
15400            f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
15401            ":200\r\n"
15402        );
15403        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
15404        // A timestamp behind the newest sample is the other of the two errors
15405        // the module writes with no ERR in front of it.
15406        assert_eq!(
15407            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
15408            "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
15409        );
15410        // The increment goes through the ordinary number reader, so it takes
15411        // what a sample value will not and refuses a NaN that a sample value
15412        // takes.
15413        assert_eq!(
15414            f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
15415            ":1\r\n"
15416        );
15417        assert_eq!(
15418            f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
15419            ":1\r\n"
15420        );
15421        assert_eq!(
15422            f.run(&[b"TS.INCRBY", b"t", b"nan"]),
15423            "-ERR TSDB: invalid increase/decrease value\r\n"
15424        );
15425        assert_eq!(
15426            f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
15427            "-ERR TSDB: invalid increase/decrease value\r\n"
15428        );
15429        // A key holding something else is WRONGTYPE and is answered before the
15430        // number is looked at.
15431        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
15432        assert_eq!(
15433            f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
15434            "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15435        );
15436        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
15437        // The reference reads one past the end of its own arguments here and
15438        // answers whatever was in that memory, so there is nothing to copy and
15439        // this answers the same thing every time.
15440        assert_eq!(
15441            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
15442            "-ERR TSDB: invalid timestamp\r\n"
15443        );
15444        // And one behind a LABELS is a label name rather than the keyword, so
15445        // this lands at the clock rather than at 5.
15446        assert_eq!(
15447            f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
15448            format!(":{}\r\n", f.server.now_ms())
15449        );
15450        // Adding to a series whose newest value is not a number has no answer.
15451        assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
15452        assert_eq!(
15453            f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
15454            "-ERR TSDB: cannot increment/decrement NaN value\r\n"
15455        );
15456    }
15457
15458    /// Deleting a span, both ends included.
15459    #[test]
15460    fn deleting_takes_out_a_span_and_answers_how_many_went() {
15461        let mut f = Fixture::new();
15462        for at in [b"100".as_slice(), b"200", b"300", b"400"] {
15463            f.run(&[b"TS.ADD", b"t", at, b"1"]);
15464        }
15465        assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
15466        assert!(
15467            f.run(&[b"TS.INFO", b"t"])
15468                .contains("+totalSamples\r\n:2\r\n")
15469        );
15470        // Ends the wrong way round take nothing out rather than being an error.
15471        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
15472        // The two open ends.
15473        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
15474        // A series everything has been deleted from keeps its chunk and reports
15475        // zero at both ends again.
15476        let empty = f.run(&[b"TS.INFO", b"t"]);
15477        assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
15478        assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
15479        assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
15480        assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
15481        assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
15482        // The two ends have their own sentences.
15483        assert_eq!(
15484            f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
15485            "-ERR TSDB: wrong fromTimestamp\r\n"
15486        );
15487        assert_eq!(
15488            f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
15489            "-ERR TSDB: wrong toTimestamp\r\n"
15490        );
15491        assert_eq!(
15492            f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
15493            "-ERR TSDB: wrong fromTimestamp\r\n"
15494        );
15495    }
15496
15497    /// What RESP3 changes, which is the two places a number is written and the
15498    /// shape of `TS.INFO`.
15499    #[test]
15500    fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
15501        let mut f = Fixture::new();
15502        f.out = Out::new(Proto::Resp3);
15503        assert_eq!(
15504            f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
15505            "+OK\r\n"
15506        );
15507        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
15508        // A double rather than the simple string RESP2 gets.
15509        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
15510        assert_eq!(
15511            without_memory(&f.run(&[b"TS.INFO", b"t"])),
15512            "%14\r\n\
15513             +totalSamples\r\n:1\r\n\
15514             +memoryUsage\r\n:\r\n\
15515             +firstTimestamp\r\n:100\r\n\
15516             +lastTimestamp\r\n:100\r\n\
15517             +retentionTime\r\n:0\r\n\
15518             +chunkCount\r\n:1\r\n\
15519             +chunkSize\r\n:4096\r\n\
15520             +chunkType\r\n+compressed\r\n\
15521             +duplicatePolicy\r\n+block\r\n\
15522             +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
15523             +sourceKey\r\n_\r\n\
15524             +rules\r\n%0\r\n\
15525             +ignoreMaxTimeDiff\r\n:0\r\n\
15526             +ignoreMaxValDiff\r\n,0\r\n"
15527        );
15528    }
15529
15530    /// Reading a span back, both ways round, with the two ends and the three
15531    /// things that trim what comes out.
15532    #[test]
15533    fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
15534        let mut f = Fixture::new();
15535        for (at, v) in [
15536            (b"100".as_slice(), b"1".as_slice()),
15537            (b"200", b"2"),
15538            (b"300", b"3"),
15539            (b"400", b"4"),
15540        ] {
15541            f.run(&[b"TS.ADD", b"t", at, v]);
15542        }
15543        assert_eq!(
15544            f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
15545            "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
15546             *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
15547        );
15548        // Both ends are included.
15549        assert_eq!(
15550            f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
15551            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15552        );
15553        // Backwards, and the count takes from the front of what comes out, so
15554        // backwards it takes the newest.
15555        assert_eq!(
15556            f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
15557            "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
15558        );
15559        // Ends the wrong way round are empty rather than an error.
15560        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
15561        // The two filters.
15562        assert_eq!(
15563            f.run(&[
15564                b"TS.RANGE",
15565                b"t",
15566                b"-",
15567                b"+",
15568                b"FILTER_BY_VALUE",
15569                b"2",
15570                b"3"
15571            ]),
15572            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
15573        );
15574        assert_eq!(
15575            f.run(&[
15576                b"TS.RANGE",
15577                b"t",
15578                b"-",
15579                b"+",
15580                b"FILTER_BY_TS",
15581                b"100",
15582                b"400"
15583            ]),
15584            "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
15585        );
15586        // A word that is not an option is ignored wherever it sits.
15587        assert_eq!(
15588            f.run(&[
15589                b"TS.RANGE",
15590                b"t",
15591                b"-",
15592                b"+",
15593                b"ZZZ",
15594                b"FILTER_BY_TS",
15595                b"400"
15596            ]),
15597            "*1\r\n*2\r\n:400\r\n+4\r\n"
15598        );
15599        // `LATEST` means nothing until there is a compaction rule to follow.
15600        assert_eq!(
15601            f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
15602            "*1\r\n*2\r\n:100\r\n+1\r\n"
15603        );
15604    }
15605
15606    /// The bucketing, which is one column a reduction and a flat row.
15607    #[test]
15608    fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15609        let mut f = Fixture::new();
15610        for (at, v) in [
15611            (b"100".as_slice(), b"1".as_slice()),
15612            (b"200", b"2"),
15613            (b"300", b"3"),
15614            (b"400", b"4"),
15615        ] {
15616            f.run(&[b"TS.ADD", b"t", at, v]);
15617        }
15618        assert_eq!(
15619            f.run(&[
15620                b"TS.RANGE",
15621                b"t",
15622                b"-",
15623                b"+",
15624                b"AGGREGATION",
15625                b"avg",
15626                b"200"
15627            ]),
15628            "*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"
15629        );
15630        // Three reductions is a row of four and not a row of two with a nested
15631        // three in it.
15632        assert_eq!(
15633            f.run(&[
15634                b"TS.RANGE",
15635                b"t",
15636                b"-",
15637                b"+",
15638                b"AGGREGATION",
15639                b"min,max,count",
15640                b"200"
15641            ]),
15642            "*3\r\n\
15643             *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15644             *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15645             *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15646        );
15647        // The timestamp a bucket is reported under.
15648        assert_eq!(
15649            f.run(&[
15650                b"TS.RANGE",
15651                b"t",
15652                b"-",
15653                b"+",
15654                b"AGGREGATION",
15655                b"avg",
15656                b"200",
15657                b"BUCKETTIMESTAMP",
15658                b"+"
15659            ]),
15660            "*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"
15661        );
15662        // An alignment moves where the bucket edges land.
15663        assert_eq!(
15664            f.run(&[
15665                b"TS.RANGE",
15666                b"t",
15667                b"100",
15668                b"400",
15669                b"ALIGN",
15670                b"100",
15671                b"AGGREGATION",
15672                b"sum",
15673                b"200"
15674            ]),
15675            "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15676        );
15677        // A `COUNT` sitting where the reduction name belongs is that name, and
15678        // the scan for a real one starts again two words later.
15679        assert_eq!(
15680            f.run(&[
15681                b"TS.RANGE",
15682                b"t",
15683                b"-",
15684                b"+",
15685                b"AGGREGATION",
15686                b"count",
15687                b"200"
15688            ]),
15689            "*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"
15690        );
15691        assert_eq!(
15692            f.run(&[
15693                b"TS.RANGE",
15694                b"t",
15695                b"-",
15696                b"+",
15697                b"AGGREGATION",
15698                b"count",
15699                b"200",
15700                b"COUNT",
15701                b"1"
15702            ]),
15703            "*1\r\n*2\r\n:0\r\n+1\r\n"
15704        );
15705    }
15706
15707    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
15708    /// carries two different things depending on which kind of empty it is.
15709    #[test]
15710    fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15711        let mut f = Fixture::new();
15712        for (at, v) in [
15713            (b"0".as_slice(), b"1".as_slice()),
15714            (b"100", b"2"),
15715            (b"500", b"nan"),
15716            (b"600", b"3"),
15717        ] {
15718            f.run(&[b"TS.ADD", b"g", at, v]);
15719        }
15720        // Without `EMPTY` the buckets with nothing in them are not there at all,
15721        // and neither is the one holding only a reading that is not a number.
15722        assert_eq!(
15723            f.run(&[
15724                b"TS.RANGE",
15725                b"g",
15726                b"-",
15727                b"+",
15728                b"AGGREGATION",
15729                b"avg",
15730                b"100"
15731            ]),
15732            "*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"
15733        );
15734        // The sum of nothing is zero rather than not a number.
15735        assert_eq!(
15736            f.run(&[
15737                b"TS.RANGE",
15738                b"g",
15739                b"-",
15740                b"+",
15741                b"AGGREGATION",
15742                b"sum",
15743                b"100",
15744                b"EMPTY"
15745            ]),
15746            "*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\
15747             *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\
15748             *2\r\n:600\r\n+3\r\n"
15749        );
15750        // Buckets 200 through 400 have no readings at all and carry the reading
15751        // before the gap either way round. Bucket 500 has a reading that is not
15752        // a number, so it carries whatever the bucket before it in the reading
15753        // direction answered, which is 2 forwards and 3 backwards.
15754        assert_eq!(
15755            f.run(&[
15756                b"TS.RANGE",
15757                b"g",
15758                b"-",
15759                b"+",
15760                b"AGGREGATION",
15761                b"last",
15762                b"100",
15763                b"EMPTY"
15764            ]),
15765            "*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\
15766             *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\
15767             *2\r\n:600\r\n+3\r\n"
15768        );
15769        assert_eq!(
15770            f.run(&[
15771                b"TS.REVRANGE",
15772                b"g",
15773                b"-",
15774                b"+",
15775                b"AGGREGATION",
15776                b"last",
15777                b"100",
15778                b"EMPTY"
15779            ]),
15780            "*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\
15781             *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\
15782             *2\r\n:0\r\n+1\r\n"
15783        );
15784        // And a window that opens on that bucket has nothing in range before it
15785        // to carry, so it answers not a number.
15786        assert_eq!(
15787            f.run(&[
15788                b"TS.RANGE",
15789                b"g",
15790                b"500",
15791                b"600",
15792                b"AGGREGATION",
15793                b"last",
15794                b"100",
15795                b"EMPTY"
15796            ]),
15797            "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15798        );
15799    }
15800
15801    /// The sentences a read answers when its options do not add up, which are
15802    /// the module's own word for word.
15803    #[test]
15804    fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15805        let mut f = Fixture::new();
15806        f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15807        f.run(&[b"SET", b"str", b"x"]);
15808        let cases: &[(&[&[u8]], &str)] = &[
15809            (
15810                &[b"TS.RANGE", b"t"],
15811                "-ERR wrong number of arguments for 'ts.range' command\r\n",
15812            ),
15813            // The key is resolved before a single option is read.
15814            (
15815                &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15816                "-ERR TSDB: the key does not exist\r\n",
15817            ),
15818            (
15819                &[b"TS.RANGE", b"str", b"-", b"+"],
15820                "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15821            ),
15822            (
15823                &[b"TS.RANGE", b"t", b"abc", b"+"],
15824                "-ERR TSDB: wrong fromTimestamp\r\n",
15825            ),
15826            (
15827                &[b"TS.RANGE", b"t", b"-", b"abc"],
15828                "-ERR TSDB: wrong toTimestamp\r\n",
15829            ),
15830            (
15831                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15832                "-ERR TSDB: COUNT argument is missing\r\n",
15833            ),
15834            (
15835                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15836                "-ERR TSDB: Couldn't parse COUNT\r\n",
15837            ),
15838            (
15839                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15840                "-ERR TSDB: Invalid COUNT value\r\n",
15841            ),
15842            (
15843                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15844                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15845            ),
15846            (
15847                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15848                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15849            ),
15850            (
15851                &[
15852                    b"TS.RANGE",
15853                    b"t",
15854                    b"-",
15855                    b"+",
15856                    b"AGGREGATION",
15857                    b"nope",
15858                    b"100",
15859                ],
15860                "-ERR TSDB: Unknown aggregation type\r\n",
15861            ),
15862            (
15863                &[
15864                    b"TS.RANGE",
15865                    b"t",
15866                    b"-",
15867                    b"+",
15868                    b"AGGREGATION",
15869                    b"avg,,min",
15870                    b"100",
15871                ],
15872                "-ERR TSDB: Empty aggregation type in list\r\n",
15873            ),
15874            // The list of names is read before the width is looked at.
15875            (
15876                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15877                "-ERR TSDB: Unknown aggregation type\r\n",
15878            ),
15879            (
15880                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15881                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15882            ),
15883            (
15884                &[
15885                    b"TS.RANGE",
15886                    b"t",
15887                    b"-",
15888                    b"+",
15889                    b"AGGREGATION",
15890                    b"avg",
15891                    b"100",
15892                    b"X",
15893                    b"EMPTY",
15894                ],
15895                "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15896            ),
15897            (
15898                &[
15899                    b"TS.RANGE",
15900                    b"t",
15901                    b"-",
15902                    b"+",
15903                    b"AGGREGATION",
15904                    b"avg",
15905                    b"100",
15906                    b"BUCKETTIMESTAMP",
15907                    b"z",
15908                ],
15909                "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15910            ),
15911            (
15912                &[
15913                    b"TS.RANGE",
15914                    b"t",
15915                    b"-",
15916                    b"+",
15917                    b"AGGREGATION",
15918                    b"avg",
15919                    b"100",
15920                    b"X",
15921                    b"Y",
15922                    b"BUCKETTIMESTAMP",
15923                    b"-",
15924                ],
15925                "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15926                 AGGREGATION flag\r\n",
15927            ),
15928            (
15929                &[
15930                    b"TS.RANGE",
15931                    b"t",
15932                    b"-",
15933                    b"+",
15934                    b"ALIGN",
15935                    b"z",
15936                    b"AGGREGATION",
15937                    b"avg",
15938                    b"100",
15939                ],
15940                "-ERR TSDB: unknown ALIGN parameter\r\n",
15941            ),
15942            (
15943                &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15944                "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15945            ),
15946            (
15947                &[
15948                    b"TS.RANGE",
15949                    b"t",
15950                    b"-",
15951                    b"+",
15952                    b"ALIGN",
15953                    b"-",
15954                    b"AGGREGATION",
15955                    b"avg",
15956                    b"100",
15957                ],
15958                "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15959            ),
15960            (
15961                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15962                "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15963            ),
15964            (
15965                &[
15966                    b"TS.RANGE",
15967                    b"t",
15968                    b"-",
15969                    b"+",
15970                    b"FILTER_BY_VALUE",
15971                    b"x",
15972                    b"2",
15973                ],
15974                "-ERR TSDB: Couldn't parse MIN\r\n",
15975            ),
15976            (
15977                &[
15978                    b"TS.RANGE",
15979                    b"t",
15980                    b"-",
15981                    b"+",
15982                    b"FILTER_BY_VALUE",
15983                    b"1",
15984                    b"y",
15985                ],
15986                "-ERR TSDB: Couldn't parse MAX\r\n",
15987            ),
15988            (
15989                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15990                "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15991            ),
15992        ];
15993        for (argv, want) in cases {
15994            let got = f.run(argv);
15995            assert_eq!(&got, want, "{:?}", argv.last());
15996        }
15997        // The one sentence here that is yo's own rather than the module's, which
15998        // is D-54. A read that would build more rows than yo will build is
15999        // refused instead of attempted.
16000        f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
16001        f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
16002        assert_eq!(
16003            f.run(&[
16004                b"TS.RANGE",
16005                b"wide",
16006                b"-",
16007                b"+",
16008                b"AGGREGATION",
16009                b"avg",
16010                b"1",
16011                b"EMPTY"
16012            ]),
16013            "-ERR TSDB: the requested range holds too many empty buckets\r\n"
16014        );
16015    }
16016
16017    /// What RESP3 changes on a read, which is only how a number is written.
16018    #[test]
16019    fn resp3_writes_a_read_value_as_a_double() {
16020        let mut f = Fixture::new();
16021        f.out = Out::new(Proto::Resp3);
16022        for (at, v) in [
16023            (b"0".as_slice(), b"1".as_slice()),
16024            (b"100", b"2"),
16025            (b"500", b"nan"),
16026            (b"600", b"3"),
16027        ] {
16028            f.run(&[b"TS.ADD", b"g", at, v]);
16029        }
16030        assert_eq!(
16031            f.run(&[
16032                b"TS.RANGE",
16033                b"g",
16034                b"0",
16035                b"100",
16036                b"AGGREGATION",
16037                b"avg,min",
16038                b"200"
16039            ]),
16040            "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
16041        );
16042        assert_eq!(
16043            f.run(&[
16044                b"TS.RANGE",
16045                b"g",
16046                b"500",
16047                b"600",
16048                b"AGGREGATION",
16049                b"last",
16050                b"100",
16051                b"EMPTY"
16052            ]),
16053            "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
16054        );
16055    }
16056
16057    /// Two series with an overlap and a gap each, plus a third holding nothing,
16058    /// which is what the joined reads are measured against.
16059    fn joined() -> Fixture {
16060        let mut f = Fixture::new();
16061        f.run(&[b"TS.CREATE", b"z"]);
16062        for (at, v) in [
16063            (b"10".as_slice(), b"1".as_slice()),
16064            (b"20", b"2"),
16065            (b"40", b"4"),
16066            (b"50", b"5"),
16067        ] {
16068            f.run(&[b"TS.ADD", b"x", at, v]);
16069        }
16070        for (at, v) in [
16071            (b"20".as_slice(), b"20".as_slice()),
16072            (b"30", b"30"),
16073            (b"50", b"50"),
16074            (b"60", b"60"),
16075        ] {
16076            f.run(&[b"TS.ADD", b"y", at, v]);
16077        }
16078        f
16079    }
16080
16081    /// The joined read lines its keys up on the timestamp and writes a row as
16082    /// the timestamp and then a nested array of the columns, which is the one
16083    /// shape in the family that is not the flat pair.
16084    #[test]
16085    fn an_nrange_joins_its_keys_on_the_timestamp() {
16086        let mut f = joined();
16087        // One key still nests, so the shape does not depend on the count.
16088        assert_eq!(
16089            f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
16090            "*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\
16091             *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
16092        );
16093        // A key with no reading where another key has one writes NaN there.
16094        assert_eq!(
16095            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
16096            "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
16097             *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
16098             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
16099             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
16100             *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
16101             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
16102        );
16103        // A series holding nothing is a column of NaN and never a row of its
16104        // own, and the same key twice answers twice.
16105        assert_eq!(
16106            f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
16107            "*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"
16108        );
16109        assert_eq!(
16110            f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
16111            "*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"
16112        );
16113        // COUNT is applied to the joined rows and not to each key, so backwards
16114        // it gives the newest joined row rather than the newest of each.
16115        assert_eq!(
16116            f.run(&[
16117                b"TS.NREVRANGE",
16118                b"2",
16119                b"x",
16120                b"y",
16121                b"-",
16122                b"+",
16123                b"COUNT",
16124                b"1"
16125            ]),
16126            "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
16127        );
16128        assert_eq!(
16129            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
16130            "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
16131        );
16132        // The two sample filters are settled a key at a time, before the join.
16133        assert_eq!(
16134            f.run(&[
16135                b"TS.NRANGE",
16136                b"2",
16137                b"x",
16138                b"y",
16139                b"-",
16140                b"+",
16141                b"FILTER_BY_VALUE",
16142                b"2",
16143                b"30"
16144            ]),
16145            "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
16146             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
16147             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
16148             *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
16149        );
16150    }
16151
16152    /// The aggregation on a joined read names one reduction a key and then the
16153    /// one bucket width, and each name may be a comma list, so a row can be
16154    /// wider than the key count.
16155    #[test]
16156    fn an_nrange_aggregation_names_one_reduction_a_key() {
16157        let mut f = joined();
16158        assert_eq!(
16159            f.run(&[
16160                b"TS.NRANGE",
16161                b"2",
16162                b"x",
16163                b"y",
16164                b"-",
16165                b"+",
16166                b"AGGREGATION",
16167                b"sum",
16168                b"sum",
16169                b"20"
16170            ]),
16171            "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
16172             *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
16173             *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
16174             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
16175        );
16176        // A comma list on the first key widens the row to three columns.
16177        assert_eq!(
16178            f.run(&[
16179                b"TS.NRANGE",
16180                b"2",
16181                b"x",
16182                b"y",
16183                b"-",
16184                b"+",
16185                b"AGGREGATION",
16186                b"sum,count",
16187                b"avg",
16188                b"20"
16189            ]),
16190            "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
16191             *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
16192             *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
16193             *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
16194        );
16195        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
16196        // sits one or two past the width whatever the key count is.
16197        assert_eq!(
16198            f.run(&[
16199                b"TS.NRANGE",
16200                b"2",
16201                b"x",
16202                b"y",
16203                b"-",
16204                b"+",
16205                b"AGGREGATION",
16206                b"avg",
16207                b"sum",
16208                b"100",
16209                b"EMPTY",
16210                b"BUCKETTIMESTAMP",
16211                b"end"
16212            ]),
16213            "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
16214        );
16215        // A COUNT landing in one of the name slots is a reduction name and not
16216        // the keyword, and the read then has no count at all.
16217        assert_eq!(
16218            f.run(&[
16219                b"TS.NRANGE",
16220                b"2",
16221                b"x",
16222                b"y",
16223                b"-",
16224                b"+",
16225                b"AGGREGATION",
16226                b"avg",
16227                b"COUNT",
16228                b"100"
16229            ]),
16230            "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
16231        );
16232    }
16233
16234    /// The sentences a joined read answers when it does not add up, which are
16235    /// the module's own and come out in the module's own order.
16236    #[test]
16237    fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
16238        let mut f = joined();
16239        f.run(&[b"SET", b"str", b"hi"]);
16240        let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
16241        let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
16242                       must be equal to numkeys\r\n";
16243        let cases: &[(&[&[u8]], &str)] = &[
16244            (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
16245            (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
16246            (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
16247            // Not enough words behind the count for the keys and both ends of
16248            // the span, which is an arity error however many keys were named.
16249            (
16250                &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
16251                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16252            ),
16253            (
16254                &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
16255                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
16256            ),
16257            // The reduction names are read before the two ends of the span,
16258            // which no other option is.
16259            (
16260                &[
16261                    b"TS.NRANGE",
16262                    b"2",
16263                    b"x",
16264                    b"y",
16265                    b"abc",
16266                    b"+",
16267                    b"AGGREGATION",
16268                    b"nope",
16269                    b"sum",
16270                    b"100",
16271                ],
16272                "-ERR TSDB: Unknown aggregation type\r\n",
16273            ),
16274            (
16275                &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
16276                "-ERR TSDB: wrong fromTimestamp\r\n",
16277            ),
16278            (
16279                &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
16280                "-ERR TSDB: wrong toTimestamp\r\n",
16281            ),
16282            // A name slot that is missing or holds a number is the count
16283            // sentence, and a width slot that is itself a reduction name is
16284            // that sentence as well.
16285            (
16286                &[
16287                    b"TS.NRANGE",
16288                    b"2",
16289                    b"x",
16290                    b"y",
16291                    b"-",
16292                    b"+",
16293                    b"AGGREGATION",
16294                    b"avg",
16295                ],
16296                numkeys,
16297            ),
16298            (
16299                &[
16300                    b"TS.NRANGE",
16301                    b"2",
16302                    b"x",
16303                    b"y",
16304                    b"-",
16305                    b"+",
16306                    b"AGGREGATION",
16307                    b"100",
16308                    b"sum",
16309                    b"100",
16310                ],
16311                numkeys,
16312            ),
16313            (
16314                &[
16315                    b"TS.NRANGE",
16316                    b"2",
16317                    b"x",
16318                    b"y",
16319                    b"-",
16320                    b"+",
16321                    b"AGGREGATION",
16322                    b"avg",
16323                    b"sum",
16324                    b"sum",
16325                    b"100",
16326                ],
16327                numkeys,
16328            ),
16329            (
16330                &[
16331                    b"TS.NRANGE",
16332                    b"2",
16333                    b"x",
16334                    b"y",
16335                    b"-",
16336                    b"+",
16337                    b"AGGREGATION",
16338                    b"avg",
16339                    b"sum",
16340                    b"abc",
16341                ],
16342                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16343            ),
16344            (
16345                &[
16346                    b"TS.NRANGE",
16347                    b"2",
16348                    b"x",
16349                    b"y",
16350                    b"-",
16351                    b"+",
16352                    b"AGGREGATION",
16353                    b"avg",
16354                    b"sum",
16355                    b"0",
16356                ],
16357                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16358            ),
16359            // With one key none of that applies and the plain parser runs, so a
16360            // lone width is a missing width rather than a count mismatch.
16361            (
16362                &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
16363                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16364            ),
16365            (
16366                &[
16367                    b"TS.NRANGE",
16368                    b"1",
16369                    b"x",
16370                    b"-",
16371                    b"+",
16372                    b"AGGREGATION",
16373                    b"100",
16374                    b"200",
16375                ],
16376                "-ERR TSDB: Unknown aggregation type\r\n",
16377            ),
16378            // The keys come last and in the order they were named.
16379            (
16380                &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
16381                "-ERR TSDB: the key does not exist\r\n",
16382            ),
16383            (
16384                &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
16385                "-ERR WRONGTYPE Operation against a key \
16386                 holding the wrong kind of value\r\n",
16387            ),
16388        ];
16389        for (argv, want) in cases {
16390            let got = f.run(argv);
16391            assert_eq!(&got, want, "{argv:?}");
16392        }
16393    }
16394
16395    /// `TS.READ`, which is a key, one timestamp and everything from there on.
16396    #[test]
16397    fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
16398        let mut f = joined();
16399        assert_eq!(
16400            f.run(&[b"TS.READ", b"x", b"-"]),
16401            "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
16402             *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16403        );
16404        // A plus is the last sample on its own, and a timestamp between two
16405        // samples starts at the one behind it.
16406        assert_eq!(
16407            f.run(&[b"TS.READ", b"x", b"+"]),
16408            "*1\r\n*2\r\n:50\r\n+5\r\n"
16409        );
16410        assert_eq!(
16411            f.run(&[b"TS.READ", b"x", b"25"]),
16412            "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
16413        );
16414        // Past the end, a series holding nothing and a key that is not there
16415        // are all the empty array rather than an error.
16416        assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
16417        assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
16418        assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
16419        assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
16420        // The timestamp refusal goes out with nothing in front of it, and a key
16421        // holding something else answers the bare WRONGTYPE rather than the
16422        // module's prefixed one, both unlike the rest of the family.
16423        assert_eq!(
16424            f.run(&[b"TS.READ", b"x", b"abc"]),
16425            "-TSDB: invalid timestamp\r\n"
16426        );
16427        assert_eq!(
16428            f.run(&[b"TS.READ", b"x", b"-1"]),
16429            "-TSDB: invalid timestamp\r\n"
16430        );
16431        f.run(&[b"SET", b"str", b"hi"]);
16432        assert_eq!(
16433            f.run(&[b"TS.READ", b"str", b"-"]),
16434            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
16435        );
16436        // Anything other than exactly three words is an arity error, so there
16437        // is nowhere to put an option even though the table says minus three.
16438        assert_eq!(
16439            f.run(&[b"TS.READ", b"x"]),
16440            "-ERR wrong number of arguments for 'ts.read' command\r\n"
16441        );
16442        assert_eq!(
16443            f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
16444            "-ERR wrong number of arguments for 'ts.read' command\r\n"
16445        );
16446    }
16447
16448    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
16449    /// to read the count to find them.
16450    #[test]
16451    fn getkeys_reads_the_count_of_a_joined_read() {
16452        let mut f = Fixture::new();
16453        assert_eq!(
16454            f.run(&[
16455                b"COMMAND",
16456                b"GETKEYS",
16457                b"TS.NRANGE",
16458                b"2",
16459                b"a",
16460                b"b",
16461                b"-",
16462                b"+"
16463            ]),
16464            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
16465        );
16466        assert_eq!(
16467            f.run(&[
16468                b"COMMAND",
16469                b"GETKEYS",
16470                b"TS.NREVRANGE",
16471                b"1",
16472                b"a",
16473                b"-",
16474                b"+"
16475            ]),
16476            "*1\r\n$1\r\na\r\n"
16477        );
16478        // A count of zero, or one too large for the words that follow it, is
16479        // the server's own refusal and not the module's.
16480        for n in [b"0".as_slice(), b"9", b"abc"] {
16481            assert_eq!(
16482                f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
16483                "-ERR Invalid arguments specified for command\r\n"
16484            );
16485        }
16486    }
16487
16488    /// The five series every test of the label surface works against.
16489    fn labelled() -> Fixture {
16490        let mut f = Fixture::new();
16491        f.run(&[
16492            b"TS.CREATE",
16493            b"a",
16494            b"LABELS",
16495            b"room",
16496            b"kitchen",
16497            b"x",
16498            b"1",
16499        ]);
16500        f.run(&[
16501            b"TS.CREATE",
16502            b"b",
16503            b"LABELS",
16504            b"room",
16505            b"bedroom",
16506            b"x",
16507            b"2",
16508        ]);
16509        f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
16510        f.run(&[b"TS.CREATE", b"d"]);
16511        f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
16512        f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
16513        f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
16514        f
16515    }
16516
16517    /// The filter grammar, which is four steps and a `strtok` rather than a
16518    /// grammar, and which every command that searches on labels shares.
16519    #[test]
16520    fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
16521        let mut f = labelled();
16522        let cases: &[(&[&[u8]], &str)] = &[
16523            // The plain forms, and the order the answer comes back in, which is
16524            // by key name and not by anything the series remembers.
16525            (
16526                &[b"TS.QUERYINDEX", b"room=kitchen"],
16527                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16528            ),
16529            (
16530                &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
16531                "*1\r\n$1\r\na\r\n",
16532            ),
16533            (
16534                &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
16535                "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
16536            ),
16537            // An empty list still counts as something that says which series to
16538            // take, it just never takes any.
16539            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
16540            // Absent and present, neither of which stands on its own.
16541            (
16542                &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
16543                "*1\r\n$1\r\nc\r\n",
16544            ),
16545            (
16546                &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
16547                "*1\r\n$1\r\na\r\n",
16548            ),
16549            (
16550                &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
16551                "-ERR TSDB: please provide at least one matcher\r\n",
16552            ),
16553            // A run of separators is one separator and everything past the
16554            // second field is dropped, so all three of these ask one question.
16555            (
16556                &[b"TS.QUERYINDEX", b"room==kitchen"],
16557                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16558            ),
16559            (
16560                &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
16561                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
16562            ),
16563            (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
16564            // A bracket is only a list when it sits straight behind the
16565            // separator, and then the label in front of it has to be there.
16566            (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
16567            (
16568                &[b"TS.QUERYINDEX", b"=(1)"],
16569                "-ERR TSDB: failed parsing labels\r\n",
16570            ),
16571            (
16572                &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
16573                "-ERR TSDB: failed parsing labels\r\n",
16574            ),
16575            (
16576                &[b"TS.QUERYINDEX", b"room=(kitchen"],
16577                "-ERR TSDB: failed parsing labels\r\n",
16578            ),
16579            (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
16580            (
16581                &[b"TS.QUERYINDEX", b"nonsense"],
16582                "-ERR TSDB: failed parsing labels\r\n",
16583            ),
16584            // Nothing here says which series to take.
16585            (
16586                &[b"TS.QUERYINDEX", b"room!=kitchen"],
16587                "-ERR TSDB: please provide at least one matcher\r\n",
16588            ),
16589            // Names and values are both compared byte for byte.
16590            (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
16591            (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
16592            (
16593                &[b"TS.QUERYINDEX"],
16594                "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
16595            ),
16596        ];
16597        for (argv, want) in cases {
16598            let got = f.run(argv);
16599            assert_eq!(&got, want, "{:?}", argv.last());
16600        }
16601    }
16602
16603    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
16604    #[test]
16605    fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16606        let mut f = labelled();
16607        let cases: &[(&[&[u8]], &str)] = &[
16608            (
16609                &[b"TS.QUERYLABELS", b"LABELS"],
16610                "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16611            ),
16612            (
16613                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16614                "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16615            ),
16616            (
16617                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16618                "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16619            ),
16620            // The series wearing `r` twice contributes the smaller of the two
16621            // here, which is not the one it was written down as first.
16622            (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16623            (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16624            (
16625                &[b"TS.QUERYLABELS", b"VALUES"],
16626                "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16627            ),
16628            (
16629                &[b"TS.QUERYLABELS", b"ZZZ"],
16630                "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16631            ),
16632            (
16633                &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16634                "-ERR TSDB: unknown argument, expected FILTER\r\n",
16635            ),
16636            (
16637                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16638                "-ERR TSDB: FILTER given with no filter expressions\r\n",
16639            ),
16640            // With no filter at all every series is taken, which is why the
16641            // first case here answers about `r` as well. A filter that is there
16642            // still has to say which series to take.
16643            (
16644                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16645                "-ERR TSDB: please provide at least one matcher\r\n",
16646            ),
16647            (
16648                &[
16649                    b"TS.QUERYLABELS",
16650                    b"LABELS",
16651                    b"FILTER",
16652                    b"room=kitchen",
16653                    b"x=",
16654                ],
16655                "*1\r\n$4\r\nroom\r\n",
16656            ),
16657        ];
16658        for (argv, want) in cases {
16659            let got = f.run(argv);
16660            assert_eq!(&got, want, "{:?}", argv.last());
16661        }
16662    }
16663
16664    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
16665    /// ways of asking for the labels back alongside it.
16666    #[test]
16667    fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16668        let mut f = labelled();
16669        let cases: &[(&[&[u8]], &str)] = &[
16670            // A series with no samples writes an empty array where the sample
16671            // goes rather than dropping out of the reply.
16672            (
16673                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16674                "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16675                 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16676            ),
16677            (
16678                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16679                "*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\
16680                 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16681                 *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",
16682            ),
16683            // A selected label the series does not wear is a nil, not a gap.
16684            (
16685                &[
16686                    b"TS.MGET",
16687                    b"SELECTED_LABELS",
16688                    b"x",
16689                    b"FILTER",
16690                    b"room=kitchen",
16691                ],
16692                "*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\
16693                 *2\r\n:100\r\n+1.5\r\n\
16694                 *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",
16695            ),
16696            // The other half of the duplicated name rule. This one takes the
16697            // first written down where `TS.QUERYLABELS` takes the smallest.
16698            (
16699                &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16700                "*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",
16701            ),
16702            (
16703                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16704                "*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\
16705                 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16706            ),
16707            // A word that is not an option is ignored, but a missing `FILTER`
16708            // is an arity error whatever else was written.
16709            (
16710                &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16711                "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16712            ),
16713            (
16714                &[b"TS.MGET", b"a", b"b", b"c"],
16715                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16716            ),
16717            (
16718                &[b"TS.MGET", b"FILTER"],
16719                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16720            ),
16721            // Both keyword checks happen before the filter is read, and the two
16722            // sentences spell the second keyword without its `ED`.
16723            (
16724                &[
16725                    b"TS.MGET",
16726                    b"WITHLABELS",
16727                    b"SELECTED_LABELS",
16728                    b"x",
16729                    b"FILTER",
16730                    b"bad",
16731                ],
16732                "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16733            ),
16734            (
16735                &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16736                "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16737            ),
16738        ];
16739        for (argv, want) in cases {
16740            let got = f.run(argv);
16741            assert_eq!(&got, want, "{:?}", argv.last());
16742        }
16743    }
16744
16745    /// What RESP3 changes across the label surface, which is a set where there
16746    /// was an array and a map where there was a pair of them.
16747    #[test]
16748    fn resp3_writes_the_label_surface_as_sets_and_maps() {
16749        let mut f = labelled();
16750        f.out = Out::new(Proto::Resp3);
16751        let cases: &[(&[&[u8]], &str)] = &[
16752            (
16753                &[b"TS.QUERYINDEX", b"room=kitchen"],
16754                "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16755            ),
16756            (
16757                &[b"TS.QUERYLABELS", b"LABELS"],
16758                "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16759            ),
16760            (
16761                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16762                "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16763            ),
16764            // The key stops being the first of three and becomes the map key,
16765            // and the labels stop being pairs and become a map of their own.
16766            (
16767                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16768                "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16769                 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16770            ),
16771            (
16772                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16773                "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16774                 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16775                 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16776            ),
16777            (
16778                &[
16779                    b"TS.MGET",
16780                    b"SELECTED_LABELS",
16781                    b"x",
16782                    b"FILTER",
16783                    b"room=kitchen",
16784                ],
16785                "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16786                 *2\r\n:100\r\n,1.5\r\n\
16787                 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16788            ),
16789            // A map with a name in it twice, which is what a series wearing one
16790            // label name twice turns into.
16791            (
16792                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16793                "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16794                 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16795            ),
16796        ];
16797        for (argv, want) in cases {
16798            let got = f.run(argv);
16799            assert_eq!(&got, want, "{:?}", argv.last());
16800        }
16801    }
16802
16803    /// The same five series with enough samples in them for a group to have
16804    /// something to fold.
16805    fn spanned() -> Fixture {
16806        let mut f = labelled();
16807        f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16808        f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16809        f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16810        f
16811    }
16812
16813    /// A span read out of every series a filter takes, with and without a group
16814    /// over the top of it.
16815    #[test]
16816    fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16817        let mut f = spanned();
16818        let cases: &[(&[&[u8]], &str)] = &[
16819            (
16820                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16821                "*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\
16822                 *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",
16823            ),
16824            // Newest first is applied to each series before anything else sees
16825            // the rows.
16826            (
16827                &[
16828                    b"TS.MREVRANGE",
16829                    b"-",
16830                    b"+",
16831                    b"WITHLABELS",
16832                    b"FILTER",
16833                    b"room=kitchen",
16834                ],
16835                "*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\
16836                 *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\
16837                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16838                 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16839            ),
16840            // A label a series does not wear comes back against a nil rather
16841            // than being left out.
16842            (
16843                &[
16844                    b"TS.MRANGE",
16845                    b"-",
16846                    b"+",
16847                    b"SELECTED_LABELS",
16848                    b"x",
16849                    b"FILTER",
16850                    b"room=kitchen",
16851                ],
16852                "*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\
16853                 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16854                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16855                 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16856            ),
16857            // The fold: 100 is in both series and adds up, the other two are in
16858            // one each and are still rows.
16859            (
16860                &[
16861                    b"TS.MRANGE",
16862                    b"-",
16863                    b"+",
16864                    b"FILTER",
16865                    b"room=kitchen",
16866                    b"GROUPBY",
16867                    b"room",
16868                    b"REDUCE",
16869                    b"sum",
16870                ],
16871                "*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\
16872                 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16873            ),
16874            // RESP2 has nowhere to put the reducer and the member keys, so a
16875            // group wearing labels writes them as two more labels.
16876            (
16877                &[
16878                    b"TS.MRANGE",
16879                    b"-",
16880                    b"+",
16881                    b"WITHLABELS",
16882                    b"FILTER",
16883                    b"room=kitchen",
16884                    b"GROUPBY",
16885                    b"room",
16886                    b"REDUCE",
16887                    b"max",
16888                ],
16889                "*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\
16890                 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16891                 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16892                 *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",
16893            ),
16894            // A count is applied to each member and then again to the fold.
16895            (
16896                &[
16897                    b"TS.MREVRANGE",
16898                    b"-",
16899                    b"+",
16900                    b"COUNT",
16901                    b"1",
16902                    b"FILTER",
16903                    b"room=kitchen",
16904                    b"GROUPBY",
16905                    b"room",
16906                    b"REDUCE",
16907                    b"count",
16908                ],
16909                "*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",
16910            ),
16911            // Nothing wears the label, so nothing is in any group.
16912            (
16913                &[
16914                    b"TS.MRANGE",
16915                    b"-",
16916                    b"+",
16917                    b"FILTER",
16918                    b"room=kitchen",
16919                    b"GROUPBY",
16920                    b"nope",
16921                    b"REDUCE",
16922                    b"sum",
16923                ],
16924                "*0\r\n",
16925            ),
16926            (
16927                &[
16928                    b"TS.MRANGE",
16929                    b"-",
16930                    b"+",
16931                    b"AGGREGATION",
16932                    b"sum,avg",
16933                    b"100",
16934                    b"FILTER",
16935                    b"room=bedroom",
16936                ],
16937                "*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",
16938            ),
16939            // The errors, in the order they are looked for.
16940            (
16941                &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16942                "-ERR TSDB: missing FILTER argument\r\n",
16943            ),
16944            (
16945                &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16946                "-ERR TSDB: missing labels for filter argument\r\n",
16947            ),
16948            (
16949                &[
16950                    b"TS.MRANGE",
16951                    b"-",
16952                    b"+",
16953                    b"GROUPBY",
16954                    b"room",
16955                    b"REDUCE",
16956                    b"sum",
16957                    b"FILTER",
16958                    b"room=kitchen",
16959                ],
16960                "-ERR TSDB: GROUPBY should always come after filter\r\n",
16961            ),
16962            // The group is four words from the end here, so the length is what
16963            // is wrong with it.
16964            (
16965                &[
16966                    b"TS.MRANGE",
16967                    b"-",
16968                    b"+",
16969                    b"FILTER",
16970                    b"room=kitchen",
16971                    b"GROUPBY",
16972                    b"room",
16973                    b"REDUCE",
16974                    b"sum",
16975                    b"x",
16976                ],
16977                "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16978            ),
16979            // And here it is not, so its words are filters and answer first.
16980            (
16981                &[
16982                    b"TS.MRANGE",
16983                    b"-",
16984                    b"+",
16985                    b"FILTER",
16986                    b"nope",
16987                    b"GROUPBY",
16988                    b"room",
16989                    b"REDUCE",
16990                    b"sum",
16991                    b"x",
16992                ],
16993                "-ERR TSDB: failed parsing labels\r\n",
16994            ),
16995            (
16996                &[
16997                    b"TS.MRANGE",
16998                    b"-",
16999                    b"+",
17000                    b"FILTER",
17001                    b"room=kitchen",
17002                    b"GROUPBY",
17003                    b"room",
17004                    b"REDUCE",
17005                    b"twa",
17006                ],
17007                "-ERR TSDB: Invalid reducer type\r\n",
17008            ),
17009            (
17010                &[
17011                    b"TS.MRANGE",
17012                    b"-",
17013                    b"+",
17014                    b"AGGREGATION",
17015                    b"sum,avg",
17016                    b"100",
17017                    b"FILTER",
17018                    b"room=kitchen",
17019                    b"GROUPBY",
17020                    b"room",
17021                    b"REDUCE",
17022                    b"sum",
17023                ],
17024                "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
17025            ),
17026            // The label list ends at a keyword, so this is a `COUNT` with a
17027            // `FILTER` where its number should be.
17028            (
17029                &[
17030                    b"TS.MRANGE",
17031                    b"-",
17032                    b"+",
17033                    b"SELECTED_LABELS",
17034                    b"COUNT",
17035                    b"FILTER",
17036                    b"room=kitchen",
17037                ],
17038                "-ERR TSDB: Couldn't parse COUNT\r\n",
17039            ),
17040        ];
17041        for (argv, want) in cases {
17042            let got = f.run(argv);
17043            assert_eq!(&got, want, "{argv:?}");
17044        }
17045    }
17046
17047    /// The multi key reads on RESP3, where the key becomes a map key and the
17048    /// reducer and the member keys become fields of their own.
17049    #[test]
17050    fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
17051        let mut f = spanned();
17052        f.out = Out::new(Proto::Resp3);
17053        let cases: &[(&[&[u8]], &str)] = &[
17054            (
17055                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
17056                "%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\
17057                 *1\r\n*2\r\n:200\r\n,2\r\n",
17058            ),
17059            // The reductions a read asked for, which RESP2 has no room for at
17060            // all and which is empty on a read that asked for none.
17061            (
17062                &[
17063                    b"TS.MRANGE",
17064                    b"-",
17065                    b"+",
17066                    b"AGGREGATION",
17067                    b"sum,avg",
17068                    b"100",
17069                    b"FILTER",
17070                    b"room=bedroom",
17071                ],
17072                "%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\
17073                 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
17074            ),
17075            (
17076                &[
17077                    b"TS.MRANGE",
17078                    b"-",
17079                    b"+",
17080                    b"FILTER",
17081                    b"room=kitchen",
17082                    b"GROUPBY",
17083                    b"room",
17084                    b"REDUCE",
17085                    b"sum",
17086                ],
17087                "%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\
17088                 $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\
17089                 *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",
17090            ),
17091            // The labels hold only the pair the group was made on, because the
17092            // reducer and the sources have somewhere else to go.
17093            (
17094                &[
17095                    b"TS.MRANGE",
17096                    b"-",
17097                    b"+",
17098                    b"WITHLABELS",
17099                    b"FILTER",
17100                    b"room=kitchen",
17101                    b"GROUPBY",
17102                    b"room",
17103                    b"REDUCE",
17104                    b"max",
17105                ],
17106                "%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\
17107                 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
17108                 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
17109                 *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",
17110            ),
17111            (
17112                &[
17113                    b"TS.MRANGE",
17114                    b"-",
17115                    b"+",
17116                    b"FILTER",
17117                    b"room=kitchen",
17118                    b"GROUPBY",
17119                    b"nope",
17120                    b"REDUCE",
17121                    b"sum",
17122                ],
17123                "%0\r\n",
17124            ),
17125        ];
17126        for (argv, want) in cases {
17127            let got = f.run(argv);
17128            assert_eq!(&got, want, "{argv:?}");
17129        }
17130    }
17131
17132    /// `TS.CREATERULE`, whose refusals come in an order of their own.
17133    #[test]
17134    fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
17135        let mut f = Fixture::new();
17136        f.run(&[b"TS.CREATE", b"src"]);
17137        f.run(&[b"TS.CREATE", b"dst"]);
17138        f.run(&[b"SET", b"plain", b"v"]);
17139        let cases: &[(&[&[u8]], &str)] = &[
17140            // The width is read before the reduction, the reduction before the
17141            // width being above zero, and all three before either key is looked
17142            // at, so a command that is wrong twice complains about the first.
17143            (
17144                &[
17145                    b"TS.CREATERULE",
17146                    b"src",
17147                    b"dst",
17148                    b"AGGREGATION",
17149                    b"nope",
17150                    b"x",
17151                ],
17152                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
17153            ),
17154            (
17155                &[
17156                    b"TS.CREATERULE",
17157                    b"src",
17158                    b"dst",
17159                    b"AGGREGATION",
17160                    b"nope",
17161                    b"10",
17162                ],
17163                "-ERR TSDB: Unknown aggregation type\r\n",
17164            ),
17165            (
17166                &[
17167                    b"TS.CREATERULE",
17168                    b"src",
17169                    b"dst",
17170                    b"AGGREGATION",
17171                    b"avg",
17172                    b"0",
17173                ],
17174                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
17175            ),
17176            (
17177                &[
17178                    b"TS.CREATERULE",
17179                    b"src",
17180                    b"dst",
17181                    b"AGGREGATION",
17182                    b"avg",
17183                    b"10",
17184                    b"x",
17185                ],
17186                "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
17187            ),
17188            (
17189                &[
17190                    b"TS.CREATERULE",
17191                    b"src",
17192                    b"src",
17193                    b"AGGREGATION",
17194                    b"avg",
17195                    b"10",
17196                ],
17197                "-ERR TSDB: the source key and destination key should be different\r\n",
17198            ),
17199            // A key holding something else answers the same as a key that is not
17200            // there at all, because the source is looked up first and neither of
17201            // them is a series.
17202            (
17203                &[
17204                    b"TS.CREATERULE",
17205                    b"nope",
17206                    b"plain",
17207                    b"AGGREGATION",
17208                    b"avg",
17209                    b"10",
17210                ],
17211                "-ERR TSDB: the key does not exist\r\n",
17212            ),
17213            (
17214                &[
17215                    b"TS.CREATERULE",
17216                    b"src",
17217                    b"nope",
17218                    b"AGGREGATION",
17219                    b"avg",
17220                    b"10",
17221                ],
17222                "-ERR TSDB: the key does not exist\r\n",
17223            ),
17224            // A keyword other than AGGREGATION is an arity error rather than a
17225            // syntax one, because the arity is all that is checked.
17226            (
17227                &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
17228                "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
17229            ),
17230            (
17231                &[
17232                    b"TS.CREATERULE",
17233                    b"src",
17234                    b"dst",
17235                    b"AGGREGATION",
17236                    b"avg",
17237                    b"10",
17238                ],
17239                "+OK\r\n",
17240            ),
17241            // The link is now in place, so the same rule again is refused from
17242            // the destination's end.
17243            (
17244                &[
17245                    b"TS.CREATERULE",
17246                    b"src",
17247                    b"dst",
17248                    b"AGGREGATION",
17249                    b"avg",
17250                    b"10",
17251                ],
17252                "-ERR TSDB: the destination key already has a src rule\r\n",
17253            ),
17254            // A source that is already someone's destination, and a destination
17255            // that is already someone's source, are two different sentences.
17256            (
17257                &[
17258                    b"TS.CREATERULE",
17259                    b"dst",
17260                    b"src",
17261                    b"AGGREGATION",
17262                    b"avg",
17263                    b"10",
17264                ],
17265                "-ERR TSDB: the source key already has a source rule\r\n",
17266            ),
17267            (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
17268            (
17269                &[b"TS.DELETERULE", b"src", b"dst"],
17270                "-ERR TSDB: compaction rule does not exist\r\n",
17271            ),
17272            // The source is looked up and the destination is not, so a missing
17273            // destination is a missing rule and a missing source is a missing
17274            // key, which is the other way round from `TS.CREATERULE`.
17275            (
17276                &[b"TS.DELETERULE", b"src", b"nope"],
17277                "-ERR TSDB: compaction rule does not exist\r\n",
17278            ),
17279            (
17280                &[b"TS.DELETERULE", b"nope", b"dst"],
17281                "-ERR TSDB: the key does not exist\r\n",
17282            ),
17283        ];
17284        for (argv, want) in cases {
17285            let got = f.run(argv);
17286            assert_eq!(&got, want, "{argv:?}");
17287        }
17288    }
17289
17290    /// What a rule writes, which is every bucket but the one it is filling.
17291    #[test]
17292    fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
17293        let mut f = Fixture::new();
17294        f.run(&[b"TS.CREATE", b"src"]);
17295        f.run(&[b"TS.CREATE", b"dst"]);
17296        // The readings written before the rule was made are not folded, so the
17297        // destination is still empty after the first two.
17298        f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
17299        f.run(&[
17300            b"TS.CREATERULE",
17301            b"src",
17302            b"dst",
17303            b"AGGREGATION",
17304            b"sum",
17305            b"100",
17306        ]);
17307        f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
17308        assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
17309        // The bucket the rule is filling holds only what it was given, so it is
17310        // 2 rather than 3, and it is written when a reading lands past it.
17311        assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
17312        f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
17313        assert_eq!(
17314            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17315            "*1\r\n*2\r\n:0\r\n+2\r\n"
17316        );
17317        // A reading into a bucket that has already been written works that
17318        // bucket out again over everything the source now holds.
17319        f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
17320        assert_eq!(
17321            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17322            "*1\r\n*2\r\n:0\r\n+11\r\n"
17323        );
17324        // Deleting from the source works the buckets it touched out again and
17325        // reopens the newest one, so `LATEST` starts from the whole bucket.
17326        assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
17327        assert_eq!(
17328            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
17329            "*1\r\n*2\r\n:0\r\n+8\r\n"
17330        );
17331        assert_eq!(
17332            f.run(&[b"TS.GET", b"dst", b"LATEST"]),
17333            "*2\r\n:100\r\n+4\r\n"
17334        );
17335        // The link shows on both ends, and dropping either key takes it down.
17336        assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
17337        f.run(&[b"DEL", b"dst"]);
17338        assert_eq!(
17339            f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
17340            "-ERR TSDB: compaction rule does not exist\r\n"
17341        );
17342    }
17343
17344    /// The three shapes an `XADD` id can take, and the one rule behind all of
17345    /// them.
17346    #[test]
17347    fn xadd_ids_only_ever_go_up() {
17348        let mut f = Fixture::new();
17349        // A bare millisecond is that millisecond and sequence zero.
17350        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
17351        // And `5-*` is the next free sequence inside it.
17352        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
17353        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
17354        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
17355        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17356
17357        assert!(
17358            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
17359                .contains("equal or smaller")
17360        );
17361        assert!(
17362            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
17363                .contains("must be greater than 0-0")
17364        );
17365        assert!(
17366            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
17367                .contains("Invalid stream ID")
17368        );
17369        // The pairs have to be pairs, and Redis calls an odd one an arity error
17370        // rather than a syntax error even though the table has already passed.
17371        assert!(
17372            f.run(&[b"XADD", b"s", b"*", b"a"])
17373                .contains("wrong number of arguments")
17374        );
17375
17376        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
17377        // producer can tell nobody is consuming this yet from the write landed.
17378        assert_eq!(
17379            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
17380            "$-1\r\n"
17381        );
17382        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
17383        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
17384        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
17385    }
17386
17387    /// The trim options, which are three keywords that disagree about how many
17388    /// arguments they take.
17389    #[test]
17390    fn trimming_reads_its_options_the_way_redis_does() {
17391        let mut f = Fixture::new();
17392        for i in 1..=10u32 {
17393            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17394        }
17395        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
17396        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
17397        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
17398        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17399
17400        // One argument after the keyword and the `~` is read as the threshold,
17401        // which is what a real server does and is the reason this is a number
17402        // complaint and not a syntax one.
17403        assert!(
17404            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
17405                .contains("not an integer")
17406        );
17407        assert!(
17408            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
17409                .contains("MAXLEN argument must be >= 0")
17410        );
17411        // The strategy check runs before the approximation check, so a LIMIT
17412        // with neither is told about the missing strategy.
17413        assert!(
17414            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
17415                .contains("without specifying a trimming strategy")
17416        );
17417        assert!(
17418            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
17419                .contains("without the special ~ option")
17420        );
17421        assert!(
17422            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
17423                .contains("at the same time are not compatible")
17424        );
17425        // NOMKSTREAM is XADD's and XTRIM does not take it.
17426        assert!(
17427            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
17428                .contains("syntax error")
17429        );
17430        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
17431    }
17432
17433    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
17434    #[test]
17435    fn xrange_looks_the_key_up_before_it_reads_the_count() {
17436        let mut f = Fixture::new();
17437        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
17438        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
17439
17440        assert_eq!(
17441            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17442            "*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\
17443             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17444        );
17445        assert_eq!(
17446            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
17447            "*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"
17448        );
17449        // The exclusive bound is stepped after the missing sequence is filled
17450        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
17451        // `6-1` is still in the range.
17452        assert_eq!(
17453            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
17454            "*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\
17455             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
17456        );
17457        assert_eq!(
17458            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
17459            "*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"
17460        );
17461        assert!(
17462            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
17463                .contains("Invalid stream ID")
17464        );
17465
17466        // The two kinds of nothing. A key that is not there is an empty array
17467        // and a key that is there with a count of zero is a null array, because
17468        // the lookup happens first.
17469        assert_eq!(
17470            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
17471            "*0\r\n"
17472        );
17473        assert_eq!(
17474            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
17475            "*-1\r\n"
17476        );
17477        f.run(&[b"SET", b"str", b"v"]);
17478        assert!(
17479            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
17480                .starts_with("-WRONGTYPE")
17481        );
17482        // The count is read in a loop, so the last one wins.
17483        assert_eq!(
17484            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
17485            "*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"
17486        );
17487    }
17488
17489    /// `XDEL` and `XACK` check every id before they touch any of them.
17490    #[test]
17491    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
17492        let mut f = Fixture::new();
17493        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17494        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17495        assert!(
17496            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
17497                .contains("Invalid stream ID")
17498        );
17499        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17500        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
17501        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
17502        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
17503        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
17504    }
17505
17506    /// `XGROUP`, and the two different complaints it makes about arguments.
17507    #[test]
17508    fn xgroup_has_an_arity_per_subcommand() {
17509        let mut f = Fixture::new();
17510        assert!(
17511            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17512                .contains("requires the key")
17513        );
17514        assert_eq!(
17515            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
17516            "+OK\r\n"
17517        );
17518        // A second CREATE is BUSYGROUP and not an ordinary error, because a
17519        // client racing another one to make a group branches on the prefix.
17520        assert!(
17521            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
17522                .starts_with("-BUSYGROUP")
17523        );
17524        assert_eq!(
17525            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17526            ":1\r\n"
17527        );
17528        assert_eq!(
17529            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
17530            ":0\r\n"
17531        );
17532        assert_eq!(
17533            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
17534            ":0\r\n"
17535        );
17536
17537        // Below the subcommand's own arity is an arity error naming the pair.
17538        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
17539        assert!(
17540            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
17541            "{short}"
17542        );
17543        // At or above it in a shape the handler will not take is the other one.
17544        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
17545        assert!(
17546            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
17547            "{odd}"
17548        );
17549        assert!(
17550            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
17551                .contains("Try XGROUP HELP")
17552        );
17553
17554        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
17555        assert!(
17556            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
17557                .starts_with("-NOGROUP")
17558        );
17559        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
17560        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
17561        assert!(
17562            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
17563                .contains("requires the key")
17564        );
17565    }
17566
17567    /// A group read, an acknowledgement, and what is left in between.
17568    #[test]
17569    fn xreadgroup_hands_out_and_xack_takes_back() {
17570        let mut f = Fixture::new();
17571        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17572        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17573        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17574
17575        let first = f.run(&[
17576            b"XREADGROUP",
17577            b"GROUP",
17578            b"g",
17579            b"c1",
17580            b"COUNT",
17581            b"1",
17582            b"STREAMS",
17583            b"s",
17584            b">",
17585        ]);
17586        assert_eq!(
17587            first,
17588            "*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"
17589        );
17590        // A history read names its stream even with nothing to show, which is
17591        // the difference between it and a `>` read that found nothing.
17592        assert_eq!(
17593            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
17594            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
17595        );
17596        assert_eq!(
17597            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17598            "*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"
17599        );
17600
17601        assert_eq!(
17602            f.run(&[b"XPENDING", b"s", b"g"]),
17603            "*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"
17604        );
17605        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17606        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17607        // Empty is four nulls and not a zero with three empty things.
17608        assert_eq!(
17609            f.run(&[b"XPENDING", b"s", b"g"]),
17610            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17611        );
17612
17613        // A history read of an entry that has since been deleted is the id with
17614        // a null beside it, so the consumer can still acknowledge it.
17615        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17616        f.run(&[b"XDEL", b"s", b"2-1"]);
17617        assert_eq!(
17618            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17619            "*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"
17620        );
17621
17622        // The group lookup runs before the id parse, so a `+` at a stream with
17623        // no such group is told about the group and not about the id.
17624        assert!(
17625            f.run(&[
17626                b"XREADGROUP",
17627                b"GROUP",
17628                b"nope",
17629                b"c",
17630                b"STREAMS",
17631                b"s",
17632                b"+"
17633            ])
17634            .starts_with("-NOGROUP")
17635        );
17636        assert!(
17637            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17638                .contains("meaningless in the context of XREADGROUP")
17639        );
17640        assert!(
17641            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17642                .contains("only supported by XREADGROUP")
17643        );
17644        assert!(
17645            f.run(&[
17646                b"XREADGROUP",
17647                b"GROUP",
17648                b"g",
17649                b"c",
17650                b"STREAMS",
17651                b"s",
17652                b"a",
17653                b"b"
17654            ])
17655            .contains("Unbalanced 'xreadgroup' list of streams")
17656        );
17657    }
17658
17659    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
17660    /// answer.
17661    #[test]
17662    fn xread_with_no_block_writes_the_null_itself() {
17663        let mut f = Fixture::new();
17664        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17665        assert_eq!(
17666            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17667            "*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"
17668        );
17669        // Nothing new is a null array and not an empty one, and a stream with
17670        // nothing new is left out rather than sent with an empty list.
17671        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17672        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17673        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17674        assert_eq!(
17675            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17676            "*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"
17677        );
17678        // `$` is the last id, so nothing that is already there comes back.
17679        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17680        // And `+` is the last entry, whatever COUNT says.
17681        assert_eq!(
17682            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17683            "*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"
17684        );
17685        // A count of zero means unlimited here, which is the opposite of what it
17686        // means to XRANGE.
17687        assert_eq!(
17688            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17689            "*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"
17690        );
17691        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
17692        assert!(
17693            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17694                .contains("not an integer")
17695        );
17696        assert!(
17697            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17698                .contains("timeout is negative")
17699        );
17700        assert!(
17701            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17702                .contains("Unbalanced 'xread' list of streams")
17703        );
17704    }
17705
17706    /// A blocked reader, and the two ways it stops being blocked.
17707    #[test]
17708    fn a_blocked_xread_wakes_on_the_next_entry() {
17709        let mut f = Fixture::new();
17710        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17711        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17712        assert_eq!(flow, Flow::Block);
17713        assert!(reply.is_empty());
17714
17715        // Everybody parked on the stream gets the entry, because a read takes
17716        // nothing away. That is the difference between this and BLPOP. Two
17717        // clients rather than one twice, since a client that is waiting is not
17718        // reading and cannot block again.
17719        f.session = Session::new(8);
17720        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17721        assert_eq!(flow, Flow::Block);
17722        assert_eq!(f.server.parked(), 2);
17723
17724        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17725        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";
17726        for client in [7, 8] {
17727            let mut out = Out::new(Proto::Resp2);
17728            assert!(f.server.serve_waiter(client, 0, &mut out));
17729            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17730        }
17731
17732        // And a deadline that runs out is a null array, the same as a plain
17733        // XREAD that found nothing.
17734        f.server.forget_waiters(7);
17735        f.server.forget_waiters(8);
17736        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17737        assert_eq!(flow, Flow::Block);
17738        let mut out = Out::new(Proto::Resp2);
17739        assert!(!f.server.serve_waiter(8, 0, &mut out));
17740        assert!(out.as_slice().is_empty());
17741        assert!(f.server.serve_waiter(8, u64::MAX, &mut out));
17742        assert_eq!(
17743            core::str::from_utf8(out.as_slice()).expect("ascii"),
17744            "*-1\r\n"
17745        );
17746    }
17747
17748    /// A blocked group reader whose group is destroyed under it.
17749    #[test]
17750    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17751        let mut f = Fixture::new();
17752        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17753        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17754        let (flow, _) = f.flow(&[
17755            b"XREADGROUP",
17756            b"GROUP",
17757            b"g",
17758            b"c",
17759            b"BLOCK",
17760            b"0",
17761            b"STREAMS",
17762            b"s",
17763            b">",
17764        ]);
17765        assert_eq!(flow, Flow::Block);
17766
17767        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17768        let mut out = Out::new(Proto::Resp2);
17769        assert!(f.server.serve_waiter(7, 0, &mut out));
17770        // The ordinary sentence and not a special one about having been parked,
17771        // which is what a running 8.10 sends.
17772        assert_eq!(
17773            core::str::from_utf8(out.as_slice()).expect("ascii"),
17774            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17775        );
17776    }
17777
17778    /// `XCLAIM`, whose argument shape is the odd one in the group.
17779    #[test]
17780    fn xclaim_reads_ids_until_one_will_not_parse() {
17781        let mut f = Fixture::new();
17782        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17783        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17784        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17785        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17786
17787        // Everything after the first argument that is not an id is an option, so
17788        // a `-` is an unrecognised option and not a bad id.
17789        assert!(
17790            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17791                .contains("Unrecognized XCLAIM option '-'")
17792        );
17793        assert_eq!(
17794            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17795            "*1\r\n$3\r\n1-1\r\n"
17796        );
17797        // An id that is pending but whose entry has gone is an empty answer, and
17798        // it leaves the pending list on the way past.
17799        f.run(&[b"XDEL", b"s", b"2-1"]);
17800        assert_eq!(
17801            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17802            "*0\r\n"
17803        );
17804        assert!(
17805            f.run(&[b"XPENDING", b"s", b"g"])
17806                .starts_with("*4\r\n:1\r\n")
17807        );
17808        assert!(
17809            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17810                .starts_with("-NOGROUP")
17811        );
17812        assert!(
17813            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17814                .contains("Invalid min-idle-time argument for XCLAIM")
17815        );
17816    }
17817
17818    /// `XAUTOCLAIM`, and the third value nobody expects.
17819    #[test]
17820    fn xautoclaim_reports_what_it_dropped() {
17821        let mut f = Fixture::new();
17822        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17823        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17824        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17825        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17826        f.run(&[b"XDEL", b"s", b"1-1"]);
17827
17828        // The cursor, what was claimed, and what was dropped for no longer being
17829        // in the stream. The third one is what makes a sweep converge.
17830        assert_eq!(
17831            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17832            "*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"
17833        );
17834        assert!(
17835            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17836                .contains("COUNT must be > 0")
17837        );
17838        assert!(
17839            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17840                .starts_with("-NOGROUP")
17841        );
17842    }
17843
17844    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
17845    #[test]
17846    fn xdelex_answers_one_integer_an_id() {
17847        let mut f = Fixture::new();
17848        for i in 1..=4 {
17849            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17850        }
17851        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17852        f.run(&[
17853            b"XREADGROUP",
17854            b"GROUP",
17855            b"g",
17856            b"c",
17857            b"COUNT",
17858            b"2",
17859            b"STREAMS",
17860            b"s",
17861            b">",
17862        ]);
17863
17864        // One means gone and minus one means it was not there to start with.
17865        assert_eq!(
17866            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17867            "*2\r\n:1\r\n:-1\r\n"
17868        );
17869        // `KEEPREF` leaves the pending entry behind, so the group still counts
17870        // the one it was handed even though the entry has gone.
17871        assert!(
17872            f.run(&[b"XPENDING", b"s", b"g"])
17873                .starts_with("*4\r\n:2\r\n")
17874        );
17875        // `DELREF` takes it out of every pending list on the way past.
17876        assert_eq!(
17877            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17878            "*1\r\n:1\r\n"
17879        );
17880        // `1-1` is still in the list, because the delete before it said KEEPREF.
17881        assert_eq!(
17882            f.run(&[b"XPENDING", b"s", b"g"]),
17883            "*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"
17884        );
17885
17886        // Two means somebody still wants it, and the question is wider than the
17887        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
17888        // refused even though no consumer has ever been handed it.
17889        assert_eq!(
17890            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17891            "*2\r\n:2\r\n:2\r\n"
17892        );
17893
17894        // A key that is not there answers minus ones without reading the IDs.
17895        assert_eq!(
17896            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17897            "*2\r\n:-1\r\n:-1\r\n"
17898        );
17899        // A key that is there validates every ID before deleting any of them.
17900        assert!(
17901            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17902                .starts_with("-ERR Invalid stream ID")
17903        );
17904        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17905
17906        assert!(
17907            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17908                .contains("Number of IDs must be a positive integer")
17909        );
17910        assert!(
17911            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17912                .contains("The `numids` parameter must match the number of arguments")
17913        );
17914        // The condition is one word, so a second one is a syntax error, and so
17915        // is one ID more than the count promised.
17916        assert!(
17917            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17918                .starts_with("-ERR syntax error")
17919        );
17920        assert!(
17921            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17922                .starts_with("-ERR syntax error")
17923        );
17924        // The key is looked up first, so the wrong type beats the syntax.
17925        f.run(&[b"SET", b"str", b"v"]);
17926        assert!(
17927            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17928                .starts_with("-WRONGTYPE")
17929        );
17930    }
17931
17932    /// `XACKDEL`, whose reply is about the pending list and not about the log.
17933    #[test]
17934    fn xackdel_reports_what_the_group_was_holding() {
17935        let mut f = Fixture::new();
17936        for i in 1..=3 {
17937            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17938        }
17939        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17940        f.run(&[
17941            b"XREADGROUP",
17942            b"GROUP",
17943            b"g",
17944            b"c",
17945            b"COUNT",
17946            b"1",
17947            b"STREAMS",
17948            b"s",
17949            b">",
17950        ]);
17951
17952        // Minus one is not about the stream: `2-1` is sitting there unread and
17953        // still answers minus one, because the group was not holding it. It also
17954        // stays, since only an ID that was acknowledged is deleted.
17955        assert_eq!(
17956            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17957            "*2\r\n:1\r\n:-1\r\n"
17958        );
17959        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17960
17961        // A missing group is minus one an ID and not a NOGROUP.
17962        assert_eq!(
17963            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17964            "*1\r\n:-1\r\n"
17965        );
17966        assert_eq!(
17967            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17968            "*1\r\n:-1\r\n"
17969        );
17970
17971        // The acknowledgement happens whatever the condition says, so an ACKED
17972        // that answers two has still emptied the pending list.
17973        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17974        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17975        assert_eq!(
17976            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17977            "*1\r\n:2\r\n"
17978        );
17979        assert_eq!(
17980            f.run(&[b"XPENDING", b"s", b"g"]),
17981            "*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"
17982        );
17983        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17984    }
17985
17986    /// `XNACK`, which hands an entry back to nobody.
17987    #[test]
17988    fn xnack_releases_an_entry_for_the_next_claim() {
17989        let mut f = Fixture::new();
17990        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17991        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17992        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17993        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17994        // Twice, so the delivery count is two and the words have something to
17995        // do with it.
17996        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17997
17998        assert_eq!(
17999            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
18000            ":1\r\n"
18001        );
18002        // No owner, no idle time, and the count left where it was. A released
18003        // entry reads as idle for longer than any min-idle-time, which is what
18004        // puts it at the front of the next claim.
18005        assert_eq!(
18006            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
18007            "*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"
18008        );
18009        // The consumer no longer holds it, so a filtered XPENDING skips it.
18010        assert_eq!(
18011            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
18012            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
18013        );
18014        // The bookmark did not move, so a `>` read will not hand it out again.
18015        assert_eq!(
18016            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
18017            "*-1\r\n"
18018        );
18019        // A claim at any min-idle-time takes it.
18020        assert_eq!(
18021            f.run(&[
18022                b"XAUTOCLAIM",
18023                b"s",
18024                b"g",
18025                b"c2",
18026                b"99999999",
18027                b"-",
18028                b"JUSTID"
18029            ]),
18030            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
18031        );
18032
18033        // `SILENT` takes one off the count rather than putting it back to zero,
18034        // which only shows on an entry that has been handed out more than once.
18035        // It was delivered and then claimed, so it is on two and goes to one.
18036        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
18037        assert!(
18038            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
18039                .contains(":-1\r\n:1\r\n")
18040        );
18041        // And it stops at zero rather than wrapping.
18042        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
18043        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
18044        assert!(
18045            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
18046                .contains(":-1\r\n:0\r\n")
18047        );
18048        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
18049        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
18050        assert!(
18051            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
18052                .contains(":9223372036854775807\r\n")
18053        );
18054        f.run(&[
18055            b"XNACK",
18056            b"s",
18057            b"g",
18058            b"FATAL",
18059            b"IDS",
18060            b"1",
18061            b"1-1",
18062            b"RETRYCOUNT",
18063            b"3",
18064        ]);
18065        assert!(
18066            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
18067                .contains(":-1\r\n:3\r\n")
18068        );
18069
18070        // Releasing something the group is not holding is zero, and `FORCE`
18071        // makes the pending entry rather than answering zero. A forced entry
18072        // starts at zero, since there was no earlier count to keep.
18073        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
18074        assert_eq!(
18075            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
18076            ":0\r\n"
18077        );
18078        assert_eq!(
18079            f.run(&[
18080                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
18081            ]),
18082            ":1\r\n"
18083        );
18084        assert!(
18085            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
18086                .contains(":-1\r\n:0\r\n")
18087        );
18088        // `FORCE` on an ID the stream does not have is still zero.
18089        assert_eq!(
18090            f.run(&[
18091                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
18092            ]),
18093            ":0\r\n"
18094        );
18095
18096        // The group is looked up before the mode word, and it raises rather
18097        // than answering per ID the way the two delete commands do.
18098        assert_eq!(
18099            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
18100            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
18101        );
18102        assert!(
18103            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
18104                .starts_with("-ERR")
18105        );
18106        // Its own sentences, which are not the ones XDELEX uses.
18107        assert!(
18108            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
18109                .contains("numids must be a positive integer")
18110        );
18111        assert!(
18112            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
18113                .contains("number of IDs doesn't match numids")
18114        );
18115        // Everything past the counted IDs is an option, so one too many is an
18116        // option nobody recognises and not a count that does not add up.
18117        assert!(
18118            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
18119                .contains("Unrecognized XNACK option '2-1'")
18120        );
18121    }
18122
18123    /// `XINFO`, which is where the shape of the storage shows through.
18124    #[test]
18125    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
18126        let mut f = Fixture::new();
18127        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18128        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
18129        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
18130        f.run(&[
18131            b"XREADGROUP",
18132            b"GROUP",
18133            b"g",
18134            b"c1",
18135            b"COUNT",
18136            b"1",
18137            b"STREAMS",
18138            b"s",
18139            b">",
18140        ]);
18141
18142        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18143        // Ten pairs, since the six idempotency fields have nothing behind them
18144        // here and a zero would claim they had. That is D-27.
18145        assert!(info.starts_with("*20\r\n"), "{info}");
18146        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
18147        assert!(
18148            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
18149            "{info}"
18150        );
18151        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
18152        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
18153
18154        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
18155        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
18156        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
18157        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
18158        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
18159
18160        // A consumer that has never been given anything reports minus one for
18161        // inactive rather than the moment it turned up, which is what tells a
18162        // worker that is stuck from one that has nothing to do.
18163        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
18164        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
18165        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
18166        assert!(
18167            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
18168            "{consumers}"
18169        );
18170        // And in name order, which the storage does not hold them in.
18171        let c1 = consumers.find("c1").unwrap();
18172        let c2 = consumers.find("c2").unwrap();
18173        assert!(c1 < c2, "{consumers}");
18174
18175        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
18176        assert!(full.starts_with("*18\r\n"), "{full}");
18177        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
18178        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
18179
18180        assert!(
18181            f.run(&[b"XINFO", b"STREAM", b"missing"])
18182                .contains("no such key")
18183        );
18184        assert!(
18185            f.run(&[b"XINFO", b"GROUPS", b"missing"])
18186                .contains("no such key")
18187        );
18188        assert!(
18189            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
18190                .starts_with("-NOGROUP")
18191        );
18192        assert!(
18193            f.run(&[b"XINFO", b"NOSUCH", b"s"])
18194                .contains("Try XINFO HELP")
18195        );
18196        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
18197        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
18198    }
18199
18200    /// `XPENDING`'s long form, which reads its arguments by counting them.
18201    #[test]
18202    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
18203        let mut f = Fixture::new();
18204        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18205        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
18206        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
18207
18208        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
18209        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");
18210        assert_eq!(
18211            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
18212            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
18213        );
18214        // A consumer nobody has heard of holds nothing rather than erroring.
18215        assert_eq!(
18216            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
18217            "*0\r\n"
18218        );
18219        assert_eq!(
18220            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
18221            list
18222        );
18223        // IDLE is only read at position three.
18224        assert!(
18225            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
18226                .contains("syntax error")
18227        );
18228        assert!(
18229            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
18230                .contains("syntax error")
18231        );
18232        assert_eq!(
18233            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
18234            "*0\r\n"
18235        );
18236        assert!(
18237            f.run(&[b"XPENDING", b"missing", b"g"])
18238                .starts_with("-NOGROUP")
18239        );
18240    }
18241
18242    /// `XSETID`, which is three counters and two refusals.
18243    #[test]
18244    fn xsetid_will_not_go_below_what_is_there() {
18245        let mut f = Fixture::new();
18246        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
18247        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
18248        assert_eq!(
18249            f.run(&[
18250                b"XSETID",
18251                b"s",
18252                b"10-1",
18253                b"ENTRIESADDED",
18254                b"7",
18255                b"MAXDELETEDID",
18256                b"9-1"
18257            ]),
18258            "+OK\r\n"
18259        );
18260        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
18261        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
18262        assert!(
18263            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
18264            "{info}"
18265        );
18266
18267        assert!(
18268            f.run(&[b"XSETID", b"s", b"1-1"])
18269                .contains("smaller than the target stream top item")
18270        );
18271        assert!(
18272            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
18273                .contains("entries_added must be positive")
18274        );
18275        assert!(
18276            f.run(&[b"XSETID", b"missing", b"1-1"])
18277                .contains("no such key")
18278        );
18279    }
18280
18281    /// RESP3, where the two reads answer a map and the entries stay an array.
18282    #[test]
18283    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
18284        let mut f = Fixture::new();
18285        f.run(&[b"HELLO", b"3"]);
18286        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
18287        // A map header and then the key and the entries side by side, with no
18288        // two element array wrapping the pair.
18289        assert_eq!(
18290            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
18291            "%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"
18292        );
18293        // The fields are still one flat array and not a map, which is Redis's
18294        // shape and is what every consumer written before RESP3 expects.
18295        assert_eq!(
18296            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
18297            "*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"
18298        );
18299        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
18300    }
18301
18302    /// A store to migrate values into, so a test can watch the inversion.
18303    ///
18304    /// A vector rather than a file for the same reason the tier's own tests use
18305    /// one: the file work has not attached a real store yet, and what this is
18306    /// checking is the policy above the store rather than the store.
18307    struct Mem {
18308        blobs: Vec<Vec<u8>>,
18309    }
18310
18311    impl yo_kv::cold::Blocks for Mem {
18312        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
18313            self.blobs.push(bytes.to_vec());
18314            Ok(yo_common::Addr::new(
18315                yo_common::Space::Log,
18316                (self.blobs.len() - 1) as u64,
18317            ))
18318        }
18319
18320        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
18321            self.blobs
18322                .get(at.offset() as usize)
18323                .map(Vec::as_slice)
18324                .ok_or_else(|| {
18325                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
18326                })
18327        }
18328
18329        fn bytes(&self) -> u64 {
18330            self.blobs.iter().map(|b| b.len() as u64).sum()
18331        }
18332    }
18333
18334    /// A server holding several segments of strings, with somewhere to put them.
18335    ///
18336    /// Answers the fixture and what it was holding when it stopped filling.
18337    fn filled(attach: bool) -> (Fixture, usize) {
18338        let mut f = Fixture::new();
18339        if attach {
18340            f.server
18341                .striped(0)
18342                .hold_stripe(0)
18343                .attach(Box::new(Mem { blobs: Vec::new() }));
18344        }
18345        let val = vec![b'v'; 256];
18346        for i in 0..24000u32 {
18347            let k = format!("key:{i:08}");
18348            f.run(&[b"SET", k.as_bytes(), &val]);
18349        }
18350        let full = f.server.memory_bytes();
18351        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
18352        (f, full)
18353    }
18354
18355    /// Write until the server is under `limit` or the writes run out.
18356    ///
18357    /// The same shape the eviction test uses. A memory limit is enforced in
18358    /// front of a command, so nothing happens until something is written, and
18359    /// the budget means one command does not do the whole job.
18360    fn press(f: &mut Fixture, limit: usize) {
18361        let val = vec![b'v'; 256];
18362        for i in 0..3000u32 {
18363            let k = format!("new:{i:08}");
18364            assert_eq!(
18365                f.run(&[b"SET", k.as_bytes(), &val]),
18366                "+OK\r\n",
18367                "write {i} was refused"
18368            );
18369            f.server.refresh_memory();
18370            if f.server.memory_bytes() <= limit {
18371                return;
18372            }
18373        }
18374        panic!(
18375            "it never got under: {} against {limit}",
18376            f.server.memory_bytes()
18377        );
18378    }
18379
18380    #[test]
18381    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
18382        let mut f = Fixture::new();
18383        assert_eq!(
18384            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18385            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
18386            "no limit is the default"
18387        );
18388        // The same memory value parser `maxmemory` uses, and the same trap in
18389        // it, plus the one spelling that means no limit at all.
18390        for (typed, bytes) in [
18391            (&b"0"[..], "0"),
18392            (b"1024", "1024"),
18393            (b"1k", "1000"),
18394            (b"1gb", "1073741824"),
18395            (b"-1", "-1"),
18396        ] {
18397            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
18398            assert_eq!(
18399                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
18400                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
18401                "set {}",
18402                String::from_utf8_lossy(typed)
18403            );
18404        }
18405        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
18406            assert_eq!(
18407                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
18408                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
18409                "refused {}",
18410                String::from_utf8_lossy(bad)
18411            );
18412        }
18413        // Nothing is attached, so the answer to a memory limit is still Redis's.
18414        let info = f.run(&[b"INFO", b"memory"]);
18415        assert!(info.contains("maxstore:-1"), "{info}");
18416        assert!(info.contains("yo_memory_regime:evict"), "{info}");
18417        assert!(info.contains("yo_store_bytes:0"), "{info}");
18418    }
18419
18420    #[test]
18421    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
18422        // The inversion. The same pressure that makes a Redis server throw keys
18423        // away makes this one move values to the file, and afterwards every key
18424        // is still there and still answers with what was stored in it.
18425        let (mut f, full) = filled(true);
18426        let keys = f.run(&[b"DBSIZE"]);
18427        assert!(
18428            f.run(&[b"INFO", b"memory"])
18429                .contains("yo_memory_regime:migrate"),
18430            "a database with somewhere to put values migrates"
18431        );
18432
18433        let limit = full - 2 * 1024 * 1024;
18434        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18435        f.run(&[
18436            b"CONFIG",
18437            b"SET",
18438            b"maxmemory",
18439            limit.to_string().as_bytes(),
18440        ]);
18441        press(&mut f, limit);
18442
18443        assert!(
18444            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18445            "nothing was thrown away"
18446        );
18447        let after: usize = f.run(&[b"DBSIZE"])[1..]
18448            .trim_end()
18449            .parse()
18450            .expect("a count");
18451        let before: usize = keys[1..].trim_end().parse().expect("a count");
18452        assert!(after > before, "the keys that came in are all still here");
18453        assert!(
18454            f.server.store_bytes() > 0,
18455            "and what came out of memory went to the file"
18456        );
18457        // And the values read back, which is the part that makes it a migration
18458        // rather than a loss.
18459        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
18460        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
18461        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
18462    }
18463
18464    #[test]
18465    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
18466        // The documented setting for a drop in cache. A file that may hold
18467        // nothing cannot be migrated to, so eviction is all that is left, and
18468        // the server behaves exactly as it did before any of this existed.
18469        let (mut f, full) = filled(true);
18470        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
18471        assert!(
18472            f.run(&[b"INFO", b"memory"])
18473                .contains("yo_memory_regime:evict"),
18474            "nothing may go to the file"
18475        );
18476
18477        let limit = full - 2 * 1024 * 1024;
18478        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18479        f.run(&[
18480            b"CONFIG",
18481            b"SET",
18482            b"maxmemory",
18483            limit.to_string().as_bytes(),
18484        ]);
18485        press(&mut f, limit);
18486
18487        assert!(
18488            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18489            "keys were thrown away, which is what was asked for"
18490        );
18491        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
18492    }
18493
18494    #[test]
18495    fn a_full_file_goes_back_to_evicting() {
18496        // A storage limit reached is a storage limit, and eviction is the right
18497        // answer to one. The budget here is a few kilobytes, so the first round
18498        // of migration fills it and everything after that is evicted.
18499        let (mut f, full) = filled(true);
18500        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
18501        let limit = full - 2 * 1024 * 1024;
18502        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
18503        f.run(&[
18504            b"CONFIG",
18505            b"SET",
18506            b"maxmemory",
18507            limit.to_string().as_bytes(),
18508        ]);
18509        press(&mut f, limit);
18510
18511        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
18512        assert!(
18513            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
18514            "and then it started evicting"
18515        );
18516        assert!(
18517            f.run(&[b"INFO", b"memory"])
18518                .contains("yo_memory_regime:evict"),
18519            "and it says so"
18520        );
18521    }
18522    // ------------------------------------------------------------- stripes
18523
18524    /// Every string command, run twice: once on a database that is one keyspace
18525    /// and once on a database that is eight, with the same commands in the same
18526    /// order and the replies compared byte for byte.
18527    ///
18528    /// This is the whole claim the striping rests on. A key belongs to one
18529    /// stripe and to no other, so the answer to a command cannot depend on how
18530    /// many stripes there are, and the way to check that is to ask the same
18531    /// question of two servers that differ in nothing else.
18532    ///
18533    /// The keys are chosen to land on different stripes rather than to look
18534    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
18535    /// those three keys are not all on the same one, and at eight stripes three
18536    /// keys land together about one time in fifty.
18537    #[test]
18538    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
18539        let script: &[&[&[u8]]] = &[
18540            // The single key commands, which are the ones that get handed one
18541            // stripe at the dispatch site.
18542            &[b"SET", b"k1", b"v1"],
18543            &[b"SET", b"k2", b"v2"],
18544            &[b"GET", b"k1"],
18545            &[b"GET", b"nothing"],
18546            &[b"GETSET", b"k1", b"v1b"],
18547            &[b"SETNX", b"k1", b"no"],
18548            &[b"SETNX", b"k3", b"yes"],
18549            &[b"APPEND", b"k3", b"!"],
18550            &[b"STRLEN", b"k3"],
18551            &[b"SETRANGE", b"k3", b"1", b"XY"],
18552            &[b"GETRANGE", b"k3", b"0", b"-1"],
18553            &[b"INCR", b"n1"],
18554            &[b"INCRBY", b"n1", b"41"],
18555            &[b"DECRBY", b"n1", b"2"],
18556            &[b"INCRBYFLOAT", b"f1", b"1.5"],
18557            &[b"SETEX", b"e1", b"100", b"v"],
18558            &[b"PSETEX", b"e2", b"100000", b"v"],
18559            &[b"GETEX", b"e1", b"PERSIST"],
18560            &[b"GETDEL", b"k2"],
18561            &[b"GET", b"k2"],
18562            &[b"DIGEST", b"k1"],
18563            &[b"DELEX", b"k3"],
18564            // The five that name more than one key, which are the ones that
18565            // cannot be handed one stripe at all.
18566            &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
18567            &[b"MGET", b"a", b"b", b"c", b"missing"],
18568            &[b"MSETNX", b"d", b"4", b"e", b"5"],
18569            &[b"MSETNX", b"e", b"6", b"f", b"7"],
18570            &[b"MGET", b"d", b"e", b"f"],
18571            &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
18572            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
18573            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
18574            &[b"MGET", b"g", b"h"],
18575            &[b"SET", b"s1", b"ohmytext"],
18576            &[b"SET", b"s2", b"mynewtext"],
18577            &[b"LCS", b"s1", b"s2"],
18578            &[b"LCS", b"s1", b"s2", b"LEN"],
18579            &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
18580            &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
18581            &[b"LCS", b"s1", b"gone"],
18582            // And the errors, which have to be the same errors.
18583            &[b"MSET", b"odd"],
18584            &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
18585            &[b"MGET"],
18586        ];
18587
18588        let mut one = Fixture::new();
18589        let mut many = Fixture::striped(8);
18590        for parts in script {
18591            let a = one.run(parts);
18592            let b = many.run(parts);
18593            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18594        }
18595    }
18596
18597    /// The keys of an `MSET` really do end up on different stripes.
18598    ///
18599    /// Without this the test above could pass on a server whose stripe number
18600    /// happened to be a constant, which is a striped database in name only.
18601    #[test]
18602    fn a_striped_database_spreads_the_keys_it_is_given() {
18603        let mut f = Fixture::striped(8);
18604        for i in 0..256 {
18605            let key = format!("key:{i}");
18606            f.run(&[b"SET", key.as_bytes(), b"v"]);
18607        }
18608        assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18609    }
18610
18611    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
18612    /// that is not a string comes back nil and the rest of the reply is intact.
18613    #[test]
18614    fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18615        let mut one = Fixture::new();
18616        let mut many = Fixture::striped(8);
18617        for f in [&mut one, &mut many] {
18618            f.run(&[b"SET", b"str", b"v"]);
18619            // Planted rather than pushed. `RPUSH` belongs to the list group,
18620            // which has not been taught about stripes yet and would refuse the
18621            // wide server. What is under test is what `MGET` does when it walks
18622            // onto a key that is not a string, and that does not care how the
18623            // key got there.
18624            f.server
18625                .striped(0)
18626                .hold(b"list")
18627                .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18628                .expect("a new list");
18629        }
18630        assert_eq!(
18631            one.run(&[b"MGET", b"str", b"list", b"gone"]),
18632            many.run(&[b"MGET", b"str", b"list", b"gone"])
18633        );
18634    }
18635
18636    /// The same claim for the keyspace group, and the same way of checking it.
18637    ///
18638    /// `SORT` is not in the script because it is the one command in that file
18639    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
18640    /// `RANDOMKEY` are not in it either, because those three do not promise an
18641    /// order and comparing two replies byte for byte would be asserting one.
18642    /// They get tests of their own below.
18643    #[test]
18644    fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18645        let script: &[&[&[u8]]] = &[
18646            &[b"SET", b"k1", b"v1"],
18647            &[b"SET", b"k2", b"v2"],
18648            &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18649            &[b"TYPE", b"k1"],
18650            &[b"TYPE", b"gone"],
18651            &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18652            &[b"EXPIRE", b"k1", b"100"],
18653            &[b"TTL", b"k1"],
18654            &[b"EXPIRE", b"k1", b"200", b"NX"],
18655            &[b"PERSIST", b"k1"],
18656            &[b"TTL", b"k1"],
18657            &[b"PEXPIREAT", b"k2", b"1900000000000"],
18658            &[b"EXPIRETIME", b"k2"],
18659            &[b"PEXPIRETIME", b"k2"],
18660            &[b"PERSIST", b"k2"],
18661            &[b"OBJECT", b"ENCODING", b"k1"],
18662            &[b"OBJECT", b"REFCOUNT", b"k1"],
18663            &[b"OBJECT", b"IDLETIME", b"k1"],
18664            &[b"OBJECT", b"FREQ", b"k1"],
18665            &[b"OBJECT", b"ENCODING", b"gone"],
18666            &[b"OBJECT", b"HELP"],
18667            &[b"RENAME", b"k1", b"k9"],
18668            &[b"GET", b"k9"],
18669            &[b"RENAME", b"gone", b"x"],
18670            &[b"RENAMENX", b"k9", b"k2"],
18671            &[b"RENAMENX", b"k9", b"k8"],
18672            &[b"GET", b"k8"],
18673            &[b"COPY", b"k8", b"c1"],
18674            &[b"COPY", b"k8", b"c1"],
18675            &[b"COPY", b"k8", b"c1", b"REPLACE"],
18676            &[b"COPY", b"k8", b"k8"],
18677            &[b"COPY", b"gone", b"c2"],
18678            &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18679            &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18680            &[b"MOVE", b"c1", b"1"],
18681            &[b"MOVE", b"c1", b"1"],
18682            &[b"MOVE", b"k8", b"0"],
18683            &[b"DEL", b"k2", b"gone"],
18684            &[b"UNLINK", b"k8", b"k8"],
18685            &[b"DBSIZE"],
18686        ];
18687
18688        let mut one = Fixture::new();
18689        let mut many = Fixture::striped(8);
18690        for parts in script {
18691            let a = one.run(parts);
18692            let b = many.run(parts);
18693            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18694        }
18695
18696        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
18697        // payload is taken from the store rather than parsed back out of a
18698        // reply that is not text. Both servers dump the same key and the bytes
18699        // are the same bytes, which is the first half of what is being checked
18700        // here.
18701        for f in [&mut one, &mut many] {
18702            f.run(&[b"SET", b"d1", b"payload"]);
18703            let payload = f
18704                .server
18705                .striped(0)
18706                .hold(b"d1")
18707                .dump(b"d1")
18708                .expect("a key that is there");
18709            assert!(
18710                f.run(&[b"DUMP", b"d1"])
18711                    .starts_with(&format!("${}", payload.len())),
18712                "a payload of the length the store gave"
18713            );
18714            assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18715            assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18716            assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18717            assert_eq!(
18718                f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18719                "-BUSYKEY Target key name already exists.\r\n"
18720            );
18721            assert_eq!(
18722                f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18723                "-ERR DUMP payload version or checksum are wrong\r\n"
18724            );
18725        }
18726    }
18727
18728    /// A `SCAN` of a database of eight stripes comes back with all of it.
18729    ///
18730    /// The cursor is the thing under test. It has to carry the stripe as well
18731    /// as the place in it, so a client that stops at one stripe and comes back
18732    /// carries on in that stripe and not at the top of the database, and the
18733    /// walk has to end once rather than eight times.
18734    #[test]
18735    fn a_scan_of_a_striped_database_walks_all_of_it() {
18736        let mut f = Fixture::striped(8);
18737        for i in 0..500 {
18738            let key = format!("key:{i}");
18739            f.run(&[b"SET", key.as_bytes(), b"v"]);
18740        }
18741
18742        let mut seen = Vec::new();
18743        let mut cursor = "0".to_owned();
18744        let mut calls = 0;
18745        loop {
18746            let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18747            let (next, keys) = scan_reply(&reply);
18748            seen.extend(keys);
18749            cursor = next;
18750            calls += 1;
18751            assert!(calls < 5_000, "a scan that will not finish");
18752            if cursor == "0" {
18753                break;
18754            }
18755        }
18756        seen.sort();
18757        assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18758        assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18759
18760        // And the options still work when the walk is over several stripes,
18761        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
18762        // applied by each stripe on the way.
18763        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18764        let (_, keys) = scan_reply(&reply);
18765        assert_eq!(keys.len(), 10, "key:40 through key:49");
18766        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18767        let (_, keys) = scan_reply(&reply);
18768        assert!(keys.is_empty(), "nothing here is a list");
18769    }
18770
18771    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
18772    ///
18773    /// The draw picks the stripe first, so the thing that can go wrong is that
18774    /// it always picks the same one, and two hundred draws over eight stripes
18775    /// would make that obvious.
18776    #[test]
18777    fn a_random_key_can_come_from_any_stripe() {
18778        let mut f = Fixture::striped(8);
18779        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18780        for i in 0..200 {
18781            let key = format!("key:{i}");
18782            f.run(&[b"SET", key.as_bytes(), b"v"]);
18783        }
18784        let mut homes = std::collections::HashSet::new();
18785        for _ in 0..200 {
18786            let got = f.run(&[b"RANDOMKEY"]);
18787            let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18788            assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18789            homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18790        }
18791        assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18792    }
18793
18794    /// Two keys that are not on the same stripe, which is what `RENAME` and
18795    /// `COPY` have to cope with and what a test has to arrange rather than
18796    /// hope for.
18797    fn apart(f: &mut Fixture, src: &str) -> String {
18798        let home = f.server.striped(0).stripe_of(src.as_bytes());
18799        for i in 0..1_000 {
18800            let dst = format!("dst:{i}");
18801            if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18802                return dst;
18803            }
18804        }
18805        panic!("eight stripes and a thousand keys all landed in one place");
18806    }
18807
18808    /// A rename whose two keys are on two stripes moves the value, the deadline
18809    /// and, for a collection, the body itself.
18810    #[test]
18811    fn a_rename_across_stripes_takes_everything_with_it() {
18812        let mut f = Fixture::striped(8);
18813        let dst = apart(&mut f, "src");
18814        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18815
18816        f.run(&[b"SET", src, b"v"]);
18817        f.run(&[b"EXPIRE", src, b"100"]);
18818        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18819        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18820        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18821        assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18822
18823        // A list, because a string lives in its record and a collection lives
18824        // in a slab, and the second of those is the one that can be left
18825        // behind. Planted through the store, since the list group has not been
18826        // taught about stripes yet.
18827        f.server
18828            .striped(0)
18829            .hold(src)
18830            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18831            .expect("a new list");
18832        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18833        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18834        assert_eq!(
18835            f.server.striped(0).hold(dst).llen(dst).expect("a list"),
18836            2,
18837            "the members are on the stripe the key moved to"
18838        );
18839
18840        // And `RENAMENX` still refuses a destination that is taken, which is
18841        // the one answer the cross stripe path has to work out for itself.
18842        f.run(&[b"SET", src, b"v"]);
18843        assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18844        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18845        assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18846    }
18847
18848    /// And a copy across two stripes leaves both keys behind it.
18849    #[test]
18850    fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18851        let mut f = Fixture::striped(8);
18852        let dst = apart(&mut f, "src");
18853        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18854
18855        f.run(&[b"SET", src, b"v"]);
18856        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18857        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18858        assert_eq!(
18859            f.run(&[b"COPY", src, dst]),
18860            ":0\r\n",
18861            "the destination is taken"
18862        );
18863        f.run(&[b"SET", src, b"w"]);
18864        assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18865        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18866
18867        // A collection is cloned rather than moved, so both keys have a body of
18868        // their own afterwards and writing to one does not show up in the
18869        // other.
18870        f.run(&[b"DEL", src, dst]);
18871        f.server
18872            .striped(0)
18873            .hold(src)
18874            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18875            .expect("a new list");
18876        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18877        f.server
18878            .striped(0)
18879            .hold(src)
18880            .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18881            .expect("a list that is there");
18882        assert_eq!(f.server.striped(0).hold(src).llen(src).expect("a list"), 3);
18883        assert_eq!(f.server.striped(0).hold(dst).llen(dst).expect("a list"), 2);
18884    }
18885
18886    /// Every bitmap command, on one stripe and on eight, replies compared byte
18887    /// for byte.
18888    ///
18889    /// `BITOP` is the one that names more than one key and it is where the work
18890    /// went. The rest are single key commands that now find their own stripe,
18891    /// and they are here because the cheapest way to be sure the routing is
18892    /// right is to ask.
18893    #[test]
18894    fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18895        let script: &[&[&[u8]]] = &[
18896            &[b"SET", b"k1", b"foobar"],
18897            &[b"SETBIT", b"b1", b"7", b"1"],
18898            &[b"SETBIT", b"b1", b"7", b"0"],
18899            &[b"GETBIT", b"k1", b"6"],
18900            &[b"GETBIT", b"k1", b"100"],
18901            &[b"BITCOUNT", b"k1"],
18902            &[b"BITCOUNT", b"k1", b"0", b"0"],
18903            &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18904            &[b"BITPOS", b"k1", b"1"],
18905            &[b"BITPOS", b"k1", b"0", b"2"],
18906            &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18907            &[
18908                b"BITFIELD",
18909                b"bf",
18910                b"SET",
18911                b"u8",
18912                b"0",
18913                b"255",
18914                b"GET",
18915                b"u8",
18916                b"0",
18917            ],
18918            &[
18919                b"BITFIELD",
18920                b"bf",
18921                b"OVERFLOW",
18922                b"SAT",
18923                b"INCRBY",
18924                b"u8",
18925                b"0",
18926                b"10",
18927            ],
18928            &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18929            // The multi key one, over sources that are not on one stripe unless
18930            // eight stripes have folded into one.
18931            &[b"SET", b"s1", b"abc"],
18932            &[b"SET", b"s2", b"abd"],
18933            &[b"SET", b"s3", b"a"],
18934            &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18935            &[b"GET", b"d1"],
18936            &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18937            &[b"GET", b"d2"],
18938            &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18939            &[b"STRLEN", b"d3"],
18940            &[b"BITOP", b"NOT", b"d4", b"s1"],
18941            &[b"STRLEN", b"d4"],
18942            &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18943            &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18944            &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18945            &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18946            // A source that is not there reads as empty, and a result with
18947            // nothing in it deletes the destination rather than writing one.
18948            &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18949            &[b"EXISTS", b"d1"],
18950            &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18951            &[b"GET", b"d9"],
18952            // And the errors, which have to be the same errors. The key that
18953            // is not a string is planted below rather than pushed here, since
18954            // the list group has not been taught about stripes yet.
18955            &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18956            &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18957            &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18958            &[b"BITOP", b"DIFF", b"d1", b"s1"],
18959            &[b"BITOP", b"NOPE", b"d1", b"s1"],
18960            &[b"BITCOUNT", b"list"],
18961            &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18962        ];
18963
18964        let mut one = Fixture::new();
18965        let mut many = Fixture::striped(8);
18966        for f in [&mut one, &mut many] {
18967            plant_list(f, b"list");
18968        }
18969        for parts in script {
18970            let a = one.run(parts);
18971            let b = many.run(parts);
18972            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18973        }
18974    }
18975
18976    /// A list under `key`, put there through the store.
18977    ///
18978    /// What a test does when it wants a key of the wrong type on a striped
18979    /// server, because the command that would make one is in a group that has
18980    /// not been taught about stripes yet.
18981    fn plant_list(f: &mut Fixture, key: &[u8]) {
18982        f.server
18983            .striped(0)
18984            .hold(key)
18985            .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18986            .expect("a new list");
18987    }
18988
18989    /// A `BITOP` whose keys are on two stripes reads both of them.
18990    ///
18991    /// The test above spreads its keys by hashing and would still pass if one
18992    /// stripe were doing all the work, since the answers would be the same. This
18993    /// one puts the destination and the two sources where they are known not to
18994    /// share a stripe.
18995    #[test]
18996    fn a_bitop_across_stripes_reads_every_source() {
18997        let mut f = Fixture::striped(8);
18998        let other = apart(&mut f, "src");
18999        let (src, far) = (b"src".as_slice(), other.as_bytes());
19000        assert_ne!(
19001            f.server.striped(0).stripe_of(src),
19002            f.server.striped(0).stripe_of(far),
19003            "the two keys are the point of the test"
19004        );
19005
19006        f.run(&[b"SET", src, b"abc"]);
19007        f.run(&[b"SET", far, b"abd"]);
19008        assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
19009        assert_eq!(
19010            f.run(&[b"GET", far]),
19011            "$3\r\nab`\r\n",
19012            "a destination that is also a source"
19013        );
19014        f.run(&[b"SET", far, b"abd"]);
19015        assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
19016        assert_eq!(
19017            f.run(&[b"GET", src]),
19018            "$3\r\n\0\0\x07\r\n",
19019            "and the other way round"
19020        );
19021
19022        // A result of nothing deletes a destination on whatever stripe it is
19023        // on, and a source of the wrong type is refused before anything is
19024        // written.
19025        f.run(&[b"SET", src, b"abc"]);
19026        f.run(&[b"DEL", far]);
19027        assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
19028        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
19029        f.run(&[b"SET", src, b"abc"]);
19030        f.run(&[b"DEL", far]);
19031        plant_list(&mut f, far);
19032        assert_eq!(
19033            f.run(&[b"BITOP", b"OR", b"out", src, far]),
19034            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19035        );
19036        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
19037    }
19038
19039    /// Every HyperLogLog command, on one stripe and on eight.
19040    #[test]
19041    fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
19042        let script: &[&[&[u8]]] = &[
19043            &[b"PFADD", b"h1", b"a", b"b", b"c"],
19044            &[b"PFADD", b"h1", b"a"],
19045            &[b"PFADD", b"h2"],
19046            &[b"PFADD", b"h2", b"c", b"d", b"e"],
19047            &[b"PFCOUNT", b"h1"],
19048            &[b"PFCOUNT", b"h2"],
19049            &[b"PFCOUNT", b"missing"],
19050            // The two that name more than one key.
19051            &[b"PFCOUNT", b"h1", b"h2"],
19052            &[b"PFCOUNT", b"h1", b"missing"],
19053            &[b"PFMERGE", b"m", b"h1", b"h2"],
19054            &[b"PFCOUNT", b"m"],
19055            &[b"STRLEN", b"m"],
19056            &[b"PFMERGE", b"m"],
19057            &[b"PFCOUNT", b"m"],
19058            &[b"PFMERGE", b"m2", b"missing"],
19059            &[b"PFCOUNT", b"m2"],
19060            // The debugging ones, which are single key and change what they
19061            // look at.
19062            &[b"PFDEBUG", b"ENCODING", b"h1"],
19063            &[b"PFDEBUG", b"DECODE", b"h1"],
19064            &[b"PFDEBUG", b"TODENSE", b"h1"],
19065            &[b"PFDEBUG", b"ENCODING", b"h1"],
19066            &[b"PFDEBUG", b"TODENSE", b"h1"],
19067            &[b"PFCOUNT", b"h1", b"h2"],
19068            &[b"PFSELFTEST"],
19069            // And the errors.
19070            &[b"SET", b"plain", b"not a sketch at all"],
19071            &[b"PFADD", b"plain", b"a"],
19072            &[b"PFCOUNT", b"plain"],
19073            &[b"PFCOUNT", b"h1", b"plain"],
19074            &[b"PFMERGE", b"plain", b"h1"],
19075            &[b"PFMERGE", b"m", b"plain"],
19076            &[b"PFDEBUG", b"ENCODING", b"gone"],
19077            &[b"PFDEBUG", b"NOPE", b"h1"],
19078        ];
19079
19080        let mut one = Fixture::new();
19081        let mut many = Fixture::striped(8);
19082        for parts in script {
19083            let a = one.run(parts);
19084            let b = many.run(parts);
19085            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19086        }
19087    }
19088
19089    /// Every set command, on one stripe and on eight.
19090    ///
19091    /// The commands that answer members answer them in whatever order the set
19092    /// or the table they were built in holds them, so those replies are
19093    /// compared as sets. Everything else is compared byte for byte. Two servers
19094    /// agreeing on the order would be a fact about the tables and not about the
19095    /// answer, and asserting it would make this test fail for a reason nobody
19096    /// cares about.
19097    #[test]
19098    fn the_set_group_answers_the_same_however_many_stripes_there_are() {
19099        const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
19100        let script: &[&[&[u8]]] = &[
19101            &[b"SADD", b"s1", b"a", b"b", b"c"],
19102            &[b"SADD", b"s1", b"a"],
19103            &[b"SADD", b"s2", b"b", b"c", b"d"],
19104            &[b"SADD", b"ints", b"1", b"2", b"3"],
19105            &[b"SCARD", b"s1"],
19106            &[b"SISMEMBER", b"s1", b"a"],
19107            &[b"SISMEMBER", b"s1", b"z"],
19108            &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
19109            &[b"SMEMBERS", b"s1"],
19110            &[b"SREM", b"s1", b"c"],
19111            &[b"SADD", b"s1", b"c"],
19112            &[b"SSCAN", b"s1", b"0"],
19113            &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
19114            // The two draws, on a set of one member, which is the only shape
19115            // whose answer two servers have to agree on.
19116            &[b"SADD", b"one", b"m"],
19117            &[b"SRANDMEMBER", b"one"],
19118            &[b"SRANDMEMBER", b"one", b"-3"],
19119            &[b"SRANDMEMBER", b"gone"],
19120            &[b"SPOP", b"one"],
19121            &[b"SPOP", b"one"],
19122            &[b"SPOP", b"gone", b"2"],
19123            // The one that names two keys.
19124            &[b"SMOVE", b"s1", b"s2", b"a"],
19125            &[b"SMOVE", b"s1", b"s2", b"zzz"],
19126            &[b"SMOVE", b"gone", b"s2", b"a"],
19127            &[b"SMEMBERS", b"s1"],
19128            &[b"SMEMBERS", b"s2"],
19129            // The algebra.
19130            &[b"SINTER", b"s1", b"s2"],
19131            &[b"SUNION", b"s1", b"s2"],
19132            &[b"SDIFF", b"s2", b"s1"],
19133            &[b"SINTER", b"s1", b"gone"],
19134            &[b"SUNION", b"s1", b"gone"],
19135            &[b"SDIFF", b"gone", b"s1"],
19136            &[b"SINTER", b"ints", b"s1"],
19137            &[b"SINTERCARD", b"2", b"s1", b"s2"],
19138            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
19139            &[b"SUNIONCARD", b"2", b"s1", b"s2"],
19140            &[b"SDIFFCARD", b"2", b"s2", b"s1"],
19141            &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
19142            &[b"SMEMBERS", b"d1"],
19143            &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
19144            &[b"SCARD", b"d2"],
19145            &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
19146            &[b"SCARD", b"d3"],
19147            // An empty result deletes the destination rather than storing a
19148            // set with nothing in it.
19149            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
19150            &[b"EXISTS", b"d4"],
19151            // And a destination that is also a source.
19152            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
19153            &[b"SCARD", b"s2"],
19154            // The errors, which have to be the same errors.
19155            &[b"SET", b"str", b"v"],
19156            &[b"SADD", b"str", b"a"],
19157            &[b"SINTER", b"s1", b"str"],
19158            &[b"SINTERSTORE", b"d5", b"s1", b"str"],
19159            &[b"EXISTS", b"d5"],
19160            &[b"SMOVE", b"str", b"s2", b"a"],
19161            &[b"SMOVE", b"s1", b"str", b"b"],
19162            &[b"SMOVE", b"gone", b"str", b"b"],
19163            &[b"SINTERCARD", b"0", b"s1"],
19164            &[b"SINTERCARD", b"3", b"s1", b"s2"],
19165            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
19166            &[b"SPOP", b"s1", b"-1"],
19167        ];
19168
19169        let mut one = Fixture::new();
19170        let mut many = Fixture::striped(8);
19171        for parts in script {
19172            let a = one.run(parts);
19173            let b = many.run(parts);
19174            let name = String::from_utf8_lossy(parts[0]).to_uppercase();
19175            if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
19176                assert_eq!(sorted(&a), sorted(&b), "{name}");
19177            } else {
19178                assert_eq!(a, b, "{name}");
19179            }
19180        }
19181    }
19182
19183    /// The algebra over sets that are known to be on different stripes.
19184    #[test]
19185    fn a_set_operation_across_stripes_reads_every_set() {
19186        let mut f = Fixture::striped(8);
19187        let second = apart(&mut f, "s1");
19188        let third = apart(&mut f, &second);
19189        let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
19190
19191        f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
19192        f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
19193        assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
19194        assert_eq!(
19195            sorted(&f.run(&[b"SUNION", s1, s2])),
19196            ["a", "b", "c", "d"],
19197            "a union of two stripes is both of them"
19198        );
19199        assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
19200        assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
19201        assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
19202        assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
19203
19204        // A destination on a third stripe, and then one that is also a source.
19205        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
19206        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
19207        assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
19208        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
19209        assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
19210        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
19211
19212        // An empty result deletes a destination wherever it is, and a key of
19213        // the wrong type stops the command before the destination is touched.
19214        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
19215        assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
19216        f.run(&[b"SET", s3, b"v"]);
19217        assert_eq!(
19218            f.run(&[b"SINTER", s1, s3]),
19219            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19220        );
19221        assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
19222    }
19223
19224    /// An `SMOVE` whose two keys are on two stripes.
19225    #[test]
19226    fn a_move_across_stripes_takes_the_member_with_it() {
19227        let mut f = Fixture::striped(8);
19228        let other = apart(&mut f, "src");
19229        let (src, dst) = (b"src".as_slice(), other.as_bytes());
19230
19231        f.run(&[b"SADD", src, b"a", b"b"]);
19232        f.run(&[b"SADD", dst, b"c"]);
19233        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
19234        assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
19235        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
19236        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
19237
19238        // A destination that is not there is created on its own stripe, and a
19239        // source that loses its last member is deleted from its own.
19240        f.run(&[b"DEL", dst]);
19241        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
19242        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
19243        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
19244
19245        // And a source that is not there answers zero without ever asking what
19246        // the destination holds, which is Redis's order and not the obvious
19247        // one.
19248        f.run(&[b"SET", dst, b"v"]);
19249        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
19250        f.run(&[b"SADD", src, b"b"]);
19251        assert_eq!(
19252            f.run(&[b"SMOVE", src, dst, b"b"]),
19253            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19254        );
19255    }
19256
19257    /// A count and a merge over sketches that are known to be on two stripes.
19258    #[test]
19259    fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
19260        let mut f = Fixture::striped(8);
19261        let other = apart(&mut f, "src");
19262        let (src, far) = (b"src".as_slice(), other.as_bytes());
19263
19264        for i in 0..150 {
19265            let ele = format!("e:{i}");
19266            f.run(&[b"PFADD", src, ele.as_bytes()]);
19267        }
19268        for i in 150..200 {
19269            let ele = format!("e:{i}");
19270            f.run(&[b"PFADD", far, ele.as_bytes()]);
19271        }
19272        // The three numbers a real server gives for these elements, which are
19273        // the numbers the single stripe tests in the keyspace crate check too.
19274        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
19275        assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
19276        assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
19277
19278        // A merge whose destination is on a third stripe, and then one that
19279        // writes into a source.
19280        let dest = apart(&mut f, &other);
19281        assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
19282        assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
19283        assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
19284        assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
19285        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
19286    }
19287
19288    /// Every sorted set command, on one stripe and on eight.
19289    ///
19290    /// Every reply here is compared byte for byte, unlike the set group, because
19291    /// a sorted set answers in rank order and members sharing a score come out
19292    /// in the order of their bytes. There is nothing left for the table the
19293    /// answer was built in to decide.
19294    #[test]
19295    fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
19296        let script: &[&[&[u8]]] = &[
19297            &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
19298            &[b"ZADD", b"z1", b"NX", b"9", b"a"],
19299            &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
19300            &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
19301            &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
19302            &[b"ZINCRBY", b"z1", b"1.5", b"b"],
19303            &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
19304            &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
19305            &[b"ZADD", b"one", b"1", b"m"],
19306            &[b"ZCARD", b"z1"],
19307            &[b"ZCARD", b"gone"],
19308            &[b"ZSCORE", b"z1", b"a"],
19309            &[b"ZSCORE", b"z1", b"zz"],
19310            &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
19311            &[b"ZRANK", b"z1", b"c"],
19312            &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
19313            &[b"ZREVRANK", b"z1", b"c"],
19314            &[b"ZRANK", b"z1", b"gone"],
19315            &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
19316            &[b"ZCOUNT", b"z1", b"(1", b"3"],
19317            &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
19318            // The range commands, which are one parse and one walk.
19319            &[b"ZRANGE", b"z1", b"0", b"-1"],
19320            &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
19321            &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
19322            &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
19323            &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
19324            &[b"ZREVRANGE", b"z1", b"0", b"-1"],
19325            &[
19326                b"ZRANGEBYSCORE",
19327                b"z1",
19328                b"-inf",
19329                b"+inf",
19330                b"LIMIT",
19331                b"1",
19332                b"1",
19333            ],
19334            &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
19335            &[b"ZSCAN", b"z1", b"0"],
19336            &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
19337            // The draw, on a sorted set of one member, which is the only shape
19338            // whose answer two servers have to agree on.
19339            &[b"ZRANDMEMBER", b"one"],
19340            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
19341            &[b"ZRANDMEMBER", b"gone"],
19342            // The one that copies a window into another key.
19343            &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
19344            &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
19345            &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
19346            &[b"EXISTS", b"d0"],
19347            // The algebra, in both its shapes.
19348            &[b"ZUNION", b"2", b"z1", b"z2"],
19349            &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
19350            &[
19351                b"ZUNION",
19352                b"2",
19353                b"z1",
19354                b"z2",
19355                b"WEIGHTS",
19356                b"2",
19357                b"3",
19358                b"AGGREGATE",
19359                b"MAX",
19360                b"WITHSCORES",
19361            ],
19362            &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
19363            &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
19364            &[b"ZDIFF", b"2", b"gone", b"z1"],
19365            &[b"ZINTERCARD", b"2", b"z1", b"z2"],
19366            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
19367            &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
19368            &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
19369            &[
19370                b"ZINTERSTORE",
19371                b"d2",
19372                b"2",
19373                b"z1",
19374                b"z2",
19375                b"AGGREGATE",
19376                b"MIN",
19377            ],
19378            &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
19379            &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
19380            &[b"ZCARD", b"d3"],
19381            // An empty result deletes the destination rather than storing a
19382            // sorted set with nothing in it.
19383            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
19384            &[b"EXISTS", b"d4"],
19385            // A plain set is a sorted set where every score is one, so it is a
19386            // legal input to all of these.
19387            &[b"SADD", b"plain", b"a", b"x"],
19388            &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
19389            &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
19390            // And a destination that is also a source.
19391            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
19392            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
19393            // The three removals and the two pops.
19394            &[b"ZREM", b"d5", b"x", b"nothere"],
19395            &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
19396            &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
19397            &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
19398            &[b"ZPOPMIN", b"z1"],
19399            &[b"ZPOPMAX", b"z1", b"2"],
19400            &[b"ZPOPMIN", b"gone"],
19401            &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
19402            &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
19403            // The errors, which have to be the same errors.
19404            &[b"SET", b"str", b"v"],
19405            &[b"ZADD", b"str", b"1", b"a"],
19406            &[b"ZSCORE", b"str", b"a"],
19407            &[b"ZADD", b"z1", b"nan", b"a"],
19408            &[b"ZUNION", b"2", b"z1", b"str"],
19409            &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
19410            &[b"EXISTS", b"d6"],
19411            &[b"ZINTERCARD", b"0", b"z1"],
19412            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
19413            &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
19414            &[b"ZMPOP", b"1", b"str", b"MIN"],
19415            &[b"ZPOPMIN", b"z1", b"-1"],
19416        ];
19417
19418        let mut one = Fixture::new();
19419        let mut many = Fixture::striped(8);
19420        for parts in script {
19421            let a = one.run(parts);
19422            let b = many.run(parts);
19423            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19424        }
19425    }
19426
19427    /// The algebra over sorted sets that are known to be on different stripes.
19428    #[test]
19429    fn a_sorted_set_operation_across_stripes_reads_every_input() {
19430        let mut f = Fixture::striped(8);
19431        let second = apart(&mut f, "z1");
19432        let third = apart(&mut f, &second);
19433        let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
19434
19435        f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
19436        f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
19437        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
19438        // come out in and the answer that says both stripes were read.
19439        assert_eq!(
19440            f.run(&[b"ZUNION", b"2", z1, z2]),
19441            "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
19442        );
19443        assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
19444        assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
19445        assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
19446        assert_eq!(
19447            f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
19448            ":1\r\n"
19449        );
19450
19451        // A destination on a third stripe, and the weights and the aggregate
19452        // reaching every input.
19453        assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
19454        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
19455        assert_eq!(
19456            f.run(&[
19457                b"ZUNIONSTORE",
19458                z3,
19459                b"2",
19460                z1,
19461                z2,
19462                b"WEIGHTS",
19463                b"2",
19464                b"3",
19465                b"AGGREGATE",
19466                b"MAX"
19467            ]),
19468            ":3\r\n"
19469        );
19470        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
19471        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
19472        assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
19473        assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
19474        assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
19475
19476        // A pop over keys on several stripes takes from the first one that has
19477        // anything, which is what makes the order of the keys matter.
19478        let popped = format!(
19479            "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
19480            second.len()
19481        );
19482        assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
19483        f.run(&[b"ZADD", z2, b"3", b"b"]);
19484
19485        // An empty result deletes a destination wherever it is, and an input of
19486        // the wrong type stops the command before the destination is touched.
19487        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
19488        assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
19489        f.run(&[b"SET", z3, b"v"]);
19490        assert_eq!(
19491            f.run(&[b"ZUNION", b"2", z1, z3]),
19492            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19493        );
19494        assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
19495
19496        // And a destination that is also a source works across stripes for the
19497        // reason it works on one: the whole result is built before anything is
19498        // written.
19499        assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
19500        assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
19501        assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
19502    }
19503
19504    /// A `ZRANGESTORE` whose two keys are on two stripes.
19505    #[test]
19506    fn a_range_store_across_stripes_copies_the_window() {
19507        let mut f = Fixture::striped(8);
19508        let other = apart(&mut f, "src");
19509        let third = apart(&mut f, &other);
19510        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19511
19512        f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
19513        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
19514        assert_eq!(
19515            f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
19516            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
19517        );
19518        assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
19519
19520        // A window walked backwards takes the other end of the sorted set and
19521        // still stores what it took in score order.
19522        assert_eq!(
19523            f.run(&[
19524                b"ZRANGESTORE",
19525                dst,
19526                src,
19527                b"+inf",
19528                b"-inf",
19529                b"BYSCORE",
19530                b"REV",
19531                b"LIMIT",
19532                b"0",
19533                b"2"
19534            ]),
19535            ":2\r\n"
19536        );
19537        assert_eq!(
19538            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19539            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19540        );
19541
19542        // An empty window deletes the destination on its own stripe, and a
19543        // source of the wrong type is refused before the destination is touched.
19544        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
19545        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19546        f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
19547        f.run(&[b"SET", plain, b"v"]);
19548        assert_eq!(
19549            f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
19550            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19551        );
19552        assert_eq!(
19553            f.run(&[b"ZCARD", dst]),
19554            ":3\r\n",
19555            "and left the destination"
19556        );
19557    }
19558
19559    /// Every list command, on one stripe and on eight.
19560    ///
19561    /// The blocking six are in here too, both when they can be answered on the
19562    /// spot and when they cannot, since a command that parks its client writes
19563    /// nothing at all and two servers have to agree about that as much as they
19564    /// agree about a reply.
19565    #[test]
19566    fn the_list_group_answers_the_same_however_many_stripes_there_are() {
19567        let script: &[&[&[u8]]] = &[
19568            &[b"RPUSH", b"l1", b"a", b"b", b"c"],
19569            &[b"LPUSH", b"l1", b"z"],
19570            &[b"RPUSHX", b"l1", b"d"],
19571            &[b"LPUSHX", b"gone", b"x"],
19572            &[b"RPUSHX", b"gone", b"x"],
19573            &[b"LLEN", b"l1"],
19574            &[b"LLEN", b"gone"],
19575            &[b"LRANGE", b"l1", b"0", b"-1"],
19576            &[b"LRANGE", b"l1", b"1", b"2"],
19577            &[b"LRANGE", b"l1", b"5", b"9"],
19578            &[b"LINDEX", b"l1", b"0"],
19579            &[b"LINDEX", b"l1", b"-1"],
19580            &[b"LINDEX", b"l1", b"99"],
19581            &[b"LSET", b"l1", b"0", b"y"],
19582            &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
19583            &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
19584            &[b"LPOS", b"l1", b"b"],
19585            &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
19586            &[b"LPOS", b"l1", b"nothere"],
19587            &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
19588            &[b"LREM", b"l1", b"1", b"aa"],
19589            &[b"LTRIM", b"l1", b"0", b"3"],
19590            &[b"LRANGE", b"l1", b"0", b"-1"],
19591            &[b"LPOP", b"l1"],
19592            &[b"RPOP", b"l1"],
19593            &[b"LPOP", b"l1", b"2"],
19594            &[b"LPOP", b"gone"],
19595            &[b"LPOP", b"gone", b"2"],
19596            &[b"EXISTS", b"l1"],
19597            // The ones that name two keys, and the one that takes a block of
19598            // elements rather than the one on the end.
19599            &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
19600            &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
19601            &[b"RPOPLPUSH", b"src", b"dst"],
19602            &[b"LRANGE", b"dst", b"0", b"-1"],
19603            &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
19604            &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
19605            &[
19606                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19607            ],
19608            &[
19609                b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19610            ],
19611            &[b"LRANGE", b"dst", b"0", b"-1"],
19612            &[
19613                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19614            ],
19615            &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19616            &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19617            &[b"LMPOP", b"1", b"gone", b"LEFT"],
19618            // The blocking ones, first with something there to answer them and
19619            // then with nothing, which parks the client and writes nothing.
19620            &[b"RPUSH", b"q", b"a", b"b", b"c"],
19621            &[b"BLPOP", b"gone", b"q", b"0"],
19622            &[b"BRPOP", b"q", b"0"],
19623            &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19624            &[b"RPUSH", b"q", b"x", b"y", b"z"],
19625            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19626            &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19627            &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19628            &[b"BLPOP", b"q", b"0"],
19629            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19630            // The errors, which have to be the same errors.
19631            &[b"SET", b"plain", b"v"],
19632            &[b"LPUSH", b"plain", b"a"],
19633            &[b"LLEN", b"plain"],
19634            &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19635            &[b"LRANGE", b"dst", b"0", b"-1"],
19636            &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19637            &[b"LSET", b"gone", b"0", b"v"],
19638            &[b"LSET", b"dst", b"99", b"v"],
19639            &[b"LPOP", b"dst", b"-1"],
19640            &[b"LMPOP", b"0", b"dst", b"LEFT"],
19641            &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19642        ];
19643
19644        let mut one = Fixture::new();
19645        let mut many = Fixture::striped(8);
19646        for parts in script {
19647            let a = one.run(parts);
19648            let b = many.run(parts);
19649            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19650        }
19651    }
19652
19653    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
19654    #[test]
19655    fn a_list_move_across_stripes_takes_the_elements_with_it() {
19656        let mut f = Fixture::striped(8);
19657        let other = apart(&mut f, "src");
19658        let third = apart(&mut f, &other);
19659        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19660
19661        f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19662        assert_eq!(
19663            f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19664            "$1\r\na\r\n"
19665        );
19666        assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19667        assert_eq!(
19668            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19669            "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19670            "one went on each end of the destination"
19671        );
19672        assert_eq!(
19673            f.run(&[b"LRANGE", src, b"0", b"-1"]),
19674            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19675        );
19676
19677        // A block of them, which under BULK arrives in the order it left.
19678        assert_eq!(
19679            f.run(&[
19680                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19681            ]),
19682            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19683        );
19684        assert_eq!(
19685            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19686            "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19687        );
19688        assert_eq!(
19689            f.run(&[b"EXISTS", src]),
19690            ":0\r\n",
19691            "and the source is gone with its last element"
19692        );
19693
19694        // An `EXACTLY` the source cannot fill moves nothing, and a source that
19695        // is not there at all is the two kinds of nothing the two commands have.
19696        f.run(&[b"RPUSH", src, b"e", b"f"]);
19697        assert_eq!(
19698            f.run(&[
19699                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19700            ]),
19701            "*-1\r\n"
19702        );
19703        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19704        assert_eq!(
19705            f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19706            "$-1\r\n"
19707        );
19708        assert_eq!(
19709            f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19710            "*-1\r\n"
19711        );
19712
19713        // A destination of the wrong type is refused before anything is taken,
19714        // which is the order that matters most here, since an element already
19715        // out of the source would have nowhere to go back to.
19716        f.run(&[b"SET", plain, b"v"]);
19717        assert_eq!(
19718            f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19719            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19720        );
19721        assert_eq!(
19722            f.run(&[b"LLEN", src]),
19723            ":2\r\n",
19724            "and left the source alone"
19725        );
19726        assert_eq!(
19727            f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19728            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19729        );
19730        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19731    }
19732
19733    /// A parked client served by a push that landed on another stripe.
19734    ///
19735    /// A waiter remembers the database and not the stripe, which is the point:
19736    /// serving it runs the same attempt the command ran, and the attempt finds
19737    /// the stripe each of its keys is on for itself.
19738    #[test]
19739    fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19740        let mut f = Fixture::striped(8);
19741        let other = apart(&mut f, "q");
19742        let (q, far) = (b"q".as_slice(), other.as_bytes());
19743
19744        assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19745        assert_eq!(f.server.parked(), 1);
19746        f.run(&[b"RPUSH", far, b"v"]);
19747        let mut out = Out::new(Proto::Resp2);
19748        assert!(f.server.serve_waiter(7, 0, &mut out));
19749        let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19750        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19751        assert_eq!(
19752            f.run(&[b"EXISTS", far]),
19753            ":0\r\n",
19754            "and it took the element with it"
19755        );
19756
19757        // And a move across two stripes is served the same way, by the push
19758        // that fills its source.
19759        f.server.forget_waiters(7);
19760        assert_eq!(
19761            f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19762            Flow::Block
19763        );
19764        f.run(&[b"RPUSH", q, b"w"]);
19765        let mut out = Out::new(Proto::Resp2);
19766        assert!(f.server.serve_waiter(7, 0, &mut out));
19767        assert_eq!(
19768            core::str::from_utf8(out.as_slice()).expect("ascii"),
19769            "$1\r\nw\r\n"
19770        );
19771        assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19772    }
19773
19774    /// Every stream command, on one stripe and on eight.
19775    ///
19776    /// Every ID is written out rather than left to the clock, so the two servers
19777    /// are being compared on what they store and not on how long the test took
19778    /// to get from one of them to the other.
19779    #[test]
19780    fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19781        let script: &[&[&[u8]]] = &[
19782            &[b"XADD", b"s", b"1-1", b"a", b"1"],
19783            &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19784            &[b"XADD", b"s", b"3-1", b"d", b"4"],
19785            &[b"XADD", b"s", b"1-1", b"e", b"5"],
19786            &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19787            &[b"XLEN", b"s"],
19788            &[b"XLEN", b"gone"],
19789            &[b"XRANGE", b"s", b"-", b"+"],
19790            &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19791            &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19792            &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19793            &[b"XREVRANGE", b"s", b"+", b"-"],
19794            &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19795            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19796            &[b"XREAD", b"STREAMS", b"s", b"$"],
19797            // The groups, which is where most of the state is.
19798            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19799            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19800            &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19801            &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19802            &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19803            &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19804            &[
19805                b"XREADGROUP",
19806                b"GROUP",
19807                b"g",
19808                b"c1",
19809                b"COUNT",
19810                b"1",
19811                b"STREAMS",
19812                b"s",
19813                b"0",
19814            ],
19815            &[
19816                b"XREADGROUP",
19817                b"GROUP",
19818                b"nope",
19819                b"c1",
19820                b"STREAMS",
19821                b"s",
19822                b">",
19823            ],
19824            &[b"XPENDING", b"s", b"g"],
19825            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19826            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19827            &[b"XPENDING", b"s", b"nope"],
19828            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19829            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19830            &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19831            &[b"XACK", b"s", b"g", b"1-1"],
19832            &[b"XACK", b"s", b"g", b"1-1"],
19833            &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19834            &[b"XPENDING", b"s", b"g"],
19835            &[b"XINFO", b"STREAM", b"s"],
19836            &[b"XINFO", b"GROUPS", b"s"],
19837            &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19838            &[b"XINFO", b"STREAM", b"gone"],
19839            // Deleting, trimming and moving the ID on.
19840            &[b"XDEL", b"s", b"3-1"],
19841            &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19842            &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19843            &[b"XADD", b"s", b"9-1", b"z", b"9"],
19844            &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19845            &[b"XTRIM", b"s", b"MINID", b"9"],
19846            &[b"XSETID", b"s", b"99-1"],
19847            &[b"XSETID", b"s", b"1-1"],
19848            &[b"XLEN", b"s"],
19849            &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19850            &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19851            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19852            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19853            // And the errors.
19854            &[b"SET", b"plain", b"v"],
19855            &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19856            &[b"XLEN", b"plain"],
19857            &[b"XREAD", b"STREAMS", b"plain", b"0"],
19858            &[b"XRANGE", b"s", b"bogus", b"+"],
19859            &[b"XADD", b"s", b"1-1", b"a"],
19860            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19861            &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19862        ];
19863
19864        let mut one = Fixture::new();
19865        let mut many = Fixture::striped(8);
19866        for parts in script {
19867            let a = one.run(parts);
19868            let b = many.run(parts);
19869            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19870        }
19871    }
19872
19873    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
19874    ///
19875    /// Nothing is shared between the two streams, so the only thing this can go
19876    /// wrong at is looking both of them up, which is exactly what a read that
19877    /// held one database and walked it would get wrong.
19878    #[test]
19879    fn a_stream_read_across_stripes_reads_every_key() {
19880        let mut f = Fixture::striped(8);
19881        let other = apart(&mut f, "s1");
19882        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19883
19884        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19885        f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19886        let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19887        assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19888        assert!(got.contains("1-1"), "the first one is in there: {got}");
19889        assert!(got.contains("2-1"), "and so is the second: {got}");
19890
19891        // A group read looks its group up on every key before it reads any of
19892        // them, so a group that is missing on the far key stops the near one.
19893        f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19894        let got = f.run(&[
19895            b"XREADGROUP",
19896            b"GROUP",
19897            b"g",
19898            b"c",
19899            b"STREAMS",
19900            s1,
19901            s2,
19902            b">",
19903            b">",
19904        ]);
19905        assert!(got.starts_with("-NOGROUP"), "{got}");
19906        assert_eq!(
19907            f.run(&[b"XPENDING", s1, b"g"]),
19908            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19909            "and read nothing from the key that did have the group"
19910        );
19911
19912        f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19913        let got = f.run(&[
19914            b"XREADGROUP",
19915            b"GROUP",
19916            b"g",
19917            b"c",
19918            b"STREAMS",
19919            s1,
19920            s2,
19921            b">",
19922            b">",
19923        ]);
19924        assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19925    }
19926
19927    /// A client parked on an `XREAD` woken by an entry on another stripe.
19928    #[test]
19929    fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19930        let mut f = Fixture::striped(8);
19931        let other = apart(&mut f, "s1");
19932        let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19933        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19934        f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19935
19936        assert_eq!(
19937            f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19938                .0,
19939            Flow::Block
19940        );
19941        f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19942        let mut out = Out::new(Proto::Resp2);
19943        assert!(f.server.serve_waiter(7, 0, &mut out));
19944        let want = format!(
19945            "*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",
19946            other.len()
19947        );
19948        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19949    }
19950
19951    /// Every JSON command, on one stripe and on eight.
19952    #[test]
19953    fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19954        let script: &[&[&[u8]]] = &[
19955            &[
19956                b"JSON.SET",
19957                b"d",
19958                b"$",
19959                br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19960            ],
19961            &[b"JSON.SET", b"d", b"$.a", b"2"],
19962            &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19963            &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19964            &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19965            &[b"JSON.GET", b"d"],
19966            &[b"JSON.GET", b"d", b"$.b"],
19967            &[b"JSON.GET", b"gone", b"$"],
19968            &[b"JSON.TYPE", b"d", b"$.b"],
19969            &[b"JSON.TYPE", b"d", b"$.s"],
19970            &[b"JSON.TOGGLE", b"d", b"$.t"],
19971            &[b"JSON.ARRLEN", b"d", b"$.b"],
19972            &[b"JSON.OBJLEN", b"d", b"$"],
19973            &[b"JSON.OBJKEYS", b"d", b"$"],
19974            &[b"JSON.STRLEN", b"d", b"$.s"],
19975            &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19976            &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19977            &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19978            &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19979            &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19980            &[b"JSON.ARRPOP", b"d", b"$.b"],
19981            &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19982            &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19983            &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19984            &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19985            &[b"JSON.RESP", b"d", b"$.b"],
19986            &[b"JSON.DEBUG", b"MEMORY", b"d"],
19987            &[b"JSON.CLEAR", b"d", b"$.b"],
19988            &[b"JSON.DEL", b"d", b"$.m"],
19989            &[b"JSON.FORGET", b"d", b"$.nothere"],
19990            // The two that name more than one key.
19991            &[
19992                b"JSON.MSET",
19993                b"m1",
19994                b"$",
19995                b"1",
19996                b"m2",
19997                b"$",
19998                b"2",
19999                b"m3",
20000                b"$",
20001                b"3",
20002            ],
20003            &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
20004            &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
20005            &[b"JSON.GET", b"m1", b"$"],
20006            &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
20007            &[b"JSON.GET", b"m2", b"$"],
20008            // And the errors.
20009            &[b"SET", b"plain", b"v"],
20010            &[b"JSON.GET", b"plain", b"$"],
20011            &[b"JSON.SET", b"plain", b"$", b"1"],
20012            &[b"JSON.MGET", b"m1", b"plain", b"$"],
20013            &[b"JSON.SET", b"d", b"$.b", b"["],
20014            &[b"JSON.DEL", b"plain"],
20015        ];
20016
20017        let mut one = Fixture::new();
20018        let mut many = Fixture::striped(8);
20019        for parts in script {
20020            let a = one.run(parts);
20021            let b = many.run(parts);
20022            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20023        }
20024    }
20025
20026    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
20027    ///
20028    /// `JSON.MSET` works every triple out against the keyspace as it was before
20029    /// the command and writes nothing until all of them are known to work, so
20030    /// the thing to check is that a triple that cannot be written stops the
20031    /// ones on other stripes as well as the ones on its own.
20032    #[test]
20033    fn a_json_multi_write_across_stripes_reaches_every_key() {
20034        let mut f = Fixture::striped(8);
20035        let second = apart(&mut f, "m1");
20036        let third = apart(&mut f, &second);
20037        let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
20038
20039        assert_eq!(
20040            f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
20041            "+OK\r\n"
20042        );
20043        assert_eq!(
20044            f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
20045            "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
20046        );
20047
20048        // A value that is not JSON is refused before anything is written, and
20049        // the key on the far stripe keeps what it had.
20050        assert_eq!(
20051            f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
20052            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
20053        );
20054        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
20055
20056        // A path that names nowhere is not an error. That triple is skipped,
20057        // the ones on the other stripes are still written, and the reply is a
20058        // nil rather than OK.
20059        assert_eq!(
20060            f.run(&[
20061                b"JSON.MSET",
20062                m1,
20063                b"$",
20064                b"9",
20065                m2,
20066                b"$.deep",
20067                b"9",
20068                m3,
20069                b"$",
20070                b"7"
20071            ]),
20072            "$-1\r\n"
20073        );
20074        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
20075        assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
20076        assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
20077    }
20078
20079    /// Every geospatial command, on one stripe and on eight.
20080    #[test]
20081    fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
20082        let script: &[&[&[u8]]] = &[
20083            &[
20084                b"GEOADD",
20085                b"g",
20086                b"13.361389",
20087                b"38.115556",
20088                b"palermo",
20089                b"15.087269",
20090                b"37.502669",
20091                b"catania",
20092            ],
20093            &[
20094                b"GEOADD",
20095                b"g",
20096                b"NX",
20097                b"13.361389",
20098                b"38.115556",
20099                b"palermo",
20100            ],
20101            &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
20102            &[b"GEOPOS", b"g", b"palermo", b"nothere"],
20103            &[b"GEOHASH", b"g", b"palermo", b"catania"],
20104            &[b"GEODIST", b"g", b"palermo", b"catania"],
20105            &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
20106            &[b"GEODIST", b"g", b"palermo", b"nothere"],
20107            &[
20108                b"GEOSEARCH",
20109                b"g",
20110                b"FROMLONLAT",
20111                b"15",
20112                b"37",
20113                b"BYRADIUS",
20114                b"200",
20115                b"KM",
20116                b"ASC",
20117                b"WITHCOORD",
20118                b"WITHDIST",
20119                b"WITHHASH",
20120            ],
20121            &[
20122                b"GEOSEARCH",
20123                b"g",
20124                b"FROMMEMBER",
20125                b"palermo",
20126                b"BYBOX",
20127                b"400",
20128                b"400",
20129                b"KM",
20130                b"DESC",
20131            ],
20132            &[
20133                b"GEORADIUS",
20134                b"g",
20135                b"15",
20136                b"37",
20137                b"200",
20138                b"KM",
20139                b"COUNT",
20140                b"1",
20141            ],
20142            &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
20143            &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
20144            &[
20145                b"GEOSEARCHSTORE",
20146                b"dst",
20147                b"g",
20148                b"FROMLONLAT",
20149                b"15",
20150                b"37",
20151                b"BYRADIUS",
20152                b"200",
20153                b"KM",
20154            ],
20155            &[b"ZRANGE", b"dst", b"0", b"-1"],
20156            &[
20157                b"GEOSEARCHSTORE",
20158                b"dst",
20159                b"g",
20160                b"FROMLONLAT",
20161                b"15",
20162                b"37",
20163                b"BYRADIUS",
20164                b"1",
20165                b"M",
20166                b"STOREDIST",
20167            ],
20168            &[b"EXISTS", b"dst"],
20169            &[
20170                b"GEORADIUS",
20171                b"g",
20172                b"15",
20173                b"37",
20174                b"200",
20175                b"KM",
20176                b"STORE",
20177                b"dst",
20178            ],
20179            &[b"ZCARD", b"dst"],
20180            // And the errors.
20181            &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
20182            &[b"SET", b"plain", b"v"],
20183            &[b"GEOPOS", b"plain", b"a"],
20184            &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
20185            &[
20186                b"GEOSEARCHSTORE",
20187                b"dst",
20188                b"g",
20189                b"FROMLONLAT",
20190                b"15",
20191                b"37",
20192                b"BYRADIUS",
20193                b"200",
20194                b"KM",
20195                b"WITHCOORD",
20196            ],
20197        ];
20198
20199        let mut one = Fixture::new();
20200        let mut many = Fixture::striped(8);
20201        for parts in script {
20202            let a = one.run(parts);
20203            let b = many.run(parts);
20204            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20205        }
20206    }
20207
20208    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
20209    #[test]
20210    fn a_geo_search_store_across_stripes_writes_what_it_found() {
20211        let mut f = Fixture::striped(8);
20212        let other = apart(&mut f, "g");
20213        let third = apart(&mut f, &other);
20214        let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
20215
20216        f.run(&[
20217            b"GEOADD",
20218            g,
20219            b"13.361389",
20220            b"38.115556",
20221            b"palermo",
20222            b"15.087269",
20223            b"37.502669",
20224            b"catania",
20225        ]);
20226        assert_eq!(
20227            f.run(&[
20228                b"GEOSEARCHSTORE",
20229                dst,
20230                g,
20231                b"FROMLONLAT",
20232                b"15",
20233                b"37",
20234                b"BYRADIUS",
20235                b"200",
20236                b"KM",
20237                b"ASC",
20238            ]),
20239            ":2\r\n"
20240        );
20241        assert_eq!(
20242            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
20243            "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
20244            "the geohash is the score, so the order is not the search order"
20245        );
20246        assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
20247
20248        // `STOREDIST` stores the distance in the unit the search was asked in,
20249        // which is the destination stripe's sorted set and not the source's.
20250        assert_eq!(
20251            f.run(&[
20252                b"GEOSEARCHSTORE",
20253                dst,
20254                g,
20255                b"FROMMEMBER",
20256                b"palermo",
20257                b"BYRADIUS",
20258                b"200",
20259                b"KM",
20260                b"STOREDIST",
20261            ]),
20262            ":2\r\n"
20263        );
20264        assert_eq!(
20265            f.run(&[b"ZSCORE", dst, b"palermo"]),
20266            "$1\r\n0\r\n",
20267            "the centre is nought away from itself"
20268        );
20269
20270        // A search that found nothing deletes the destination on its own
20271        // stripe, and a source of the wrong type is refused with the
20272        // destination left alone.
20273        assert_eq!(
20274            f.run(&[
20275                b"GEOSEARCHSTORE",
20276                dst,
20277                g,
20278                b"FROMLONLAT",
20279                b"0",
20280                b"0",
20281                b"BYRADIUS",
20282                b"1",
20283                b"M",
20284            ]),
20285            ":0\r\n"
20286        );
20287        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
20288        f.run(&[
20289            b"GEOSEARCHSTORE",
20290            dst,
20291            g,
20292            b"FROMLONLAT",
20293            b"15",
20294            b"37",
20295            b"BYRADIUS",
20296            b"200",
20297            b"KM",
20298        ]);
20299        f.run(&[b"SET", plain, b"v"]);
20300        assert_eq!(
20301            f.run(&[
20302                b"GEOSEARCHSTORE",
20303                dst,
20304                plain,
20305                b"FROMLONLAT",
20306                b"15",
20307                b"37",
20308                b"BYRADIUS",
20309                b"200",
20310                b"KM",
20311            ]),
20312            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
20313        );
20314        assert_eq!(
20315            f.run(&[b"ZCARD", dst]),
20316            ":2\r\n",
20317            "and left the destination"
20318        );
20319    }
20320
20321    /// Every time series command, on one stripe and on eight.
20322    ///
20323    /// Every timestamp is written out rather than left to the clock, so the two
20324    /// servers are compared on the samples they hold and not on how long the
20325    /// test took to get from one of them to the other.
20326    #[test]
20327    fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
20328        let script: &[&[&[u8]]] = &[
20329            &[
20330                b"TS.CREATE",
20331                b"ts:a",
20332                b"LABELS",
20333                b"sensor",
20334                b"a",
20335                b"room",
20336                b"1",
20337            ],
20338            &[b"TS.CREATE", b"ts:a"],
20339            &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
20340            &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
20341            &[
20342                b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
20343            ],
20344            &[
20345                b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
20346            ],
20347            &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
20348            &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
20349            &[b"TS.GET", b"ts:a"],
20350            &[b"TS.GET", b"gone"],
20351            &[b"TS.RANGE", b"ts:a", b"-", b"+"],
20352            &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
20353            &[
20354                b"TS.RANGE",
20355                b"ts:a",
20356                b"-",
20357                b"+",
20358                b"AGGREGATION",
20359                b"avg",
20360                b"2000",
20361            ],
20362            &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
20363            &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20364            &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
20365            &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
20366            &[b"TS.READ", b"ts:a", b"0"],
20367            &[b"TS.READ", b"ts:a", b"+"],
20368            // The filters, which are the ones that have to walk every stripe.
20369            &[b"TS.QUERYINDEX", b"sensor=a"],
20370            &[b"TS.QUERYINDEX", b"room=1"],
20371            &[b"TS.QUERYINDEX", b"room=9"],
20372            &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
20373            &[
20374                b"TS.QUERYLABELS",
20375                b"VALUES",
20376                b"sensor",
20377                b"FILTER",
20378                b"room=1",
20379            ],
20380            &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
20381            &[
20382                b"TS.MGET",
20383                b"SELECTED_LABELS",
20384                b"sensor",
20385                b"FILTER",
20386                b"sensor=a",
20387            ],
20388            &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
20389            &[
20390                b"TS.MREVRANGE",
20391                b"-",
20392                b"+",
20393                b"WITHLABELS",
20394                b"FILTER",
20395                b"sensor=a",
20396            ],
20397            &[
20398                b"TS.MRANGE",
20399                b"-",
20400                b"+",
20401                b"FILTER",
20402                b"room=1",
20403                b"GROUPBY",
20404                b"room",
20405                b"REDUCE",
20406                b"max",
20407            ],
20408            &[b"TS.INFO", b"ts:a"],
20409            // And a rule, which is the one thing here that names two keys.
20410            &[
20411                b"TS.CREATERULE",
20412                b"ts:a",
20413                b"ts:down",
20414                b"AGGREGATION",
20415                b"avg",
20416                b"1000",
20417            ],
20418            &[b"TS.CREATE", b"ts:down"],
20419            &[
20420                b"TS.CREATERULE",
20421                b"ts:a",
20422                b"ts:down",
20423                b"AGGREGATION",
20424                b"avg",
20425                b"1000",
20426            ],
20427            &[b"TS.ADD", b"ts:a", b"5000", b"4"],
20428            &[b"TS.ADD", b"ts:a", b"6000", b"5"],
20429            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20430            &[b"TS.GET", b"ts:down", b"LATEST"],
20431            &[b"TS.INFO", b"ts:down"],
20432            &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
20433            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
20434            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20435            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
20436            &[b"TS.DEL", b"ts:a", b"0", b"1000"],
20437            // And the errors.
20438            &[b"SET", b"plain", b"v"],
20439            &[b"TS.ADD", b"plain", b"1", b"1"],
20440            &[b"TS.GET", b"plain"],
20441            &[b"TS.READ", b"plain", b"0"],
20442            &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
20443            &[b"TS.RANGE", b"gone", b"-", b"+"],
20444            &[b"TS.INFO", b"gone"],
20445        ];
20446
20447        let mut one = Fixture::new();
20448        let mut many = Fixture::striped(8);
20449        for parts in script {
20450            let a = one.run(parts);
20451            let b = many.run(parts);
20452            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20453        }
20454    }
20455
20456    /// A compaction rule whose two ends are on two stripes.
20457    ///
20458    /// This is the one thing in the family that walks from a key to another key,
20459    /// and it walks it in both directions: a sample on the source closes a
20460    /// bucket on the destination, a `LATEST` read on the destination folds the
20461    /// bucket the source is still filling, and a delete on the source rewrites
20462    /// what the destination already held. The same script is run against a
20463    /// server one stripe wide, where the two keys share a store, and against one
20464    /// eight stripes wide, where they do not.
20465    #[test]
20466    fn a_compaction_rule_across_stripes_reaches_both_ends() {
20467        let mut many = Fixture::striped(8);
20468        let other = apart(&mut many, "src");
20469        let (src, dst) = (b"src".as_slice(), other.as_bytes());
20470        let mut one = Fixture::new();
20471        let mut both = |parts: &[&[u8]]| {
20472            let a = one.run(parts);
20473            let b = many.run(parts);
20474            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20475            a
20476        };
20477
20478        both(&[b"TS.CREATE", src]);
20479        both(&[b"TS.CREATE", dst]);
20480        assert_eq!(
20481            both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
20482            "+OK\r\n"
20483        );
20484        both(&[b"TS.ADD", src, b"1000", b"1"]);
20485        both(&[b"TS.ADD", src, b"1500", b"3"]);
20486        // The bucket the source is filling is not written down yet, and asking
20487        // for it works it out off the source.
20488        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20489        let open = both(&[b"TS.GET", dst, b"LATEST"]);
20490        assert!(open.contains(":1000"), "the open bucket is folded: {open}");
20491
20492        // A sample past the bucket closes it, which is the write that has to
20493        // land on the other stripe.
20494        both(&[b"TS.ADD", src, b"2000", b"5"]);
20495        let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
20496        assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
20497        assert!(got.contains(":1000"), "{got}");
20498
20499        // And a delete on the source takes it away again.
20500        both(&[b"TS.DEL", src, b"1000", b"1999"]);
20501        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
20502
20503        // Both ends still know about each other, and the link comes apart from
20504        // the source.
20505        assert!(
20506            both(&[b"TS.INFO", dst]).contains("src"),
20507            "the source is named"
20508        );
20509        assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
20510        assert_eq!(
20511            both(&[b"TS.DELETERULE", src, dst]),
20512            "-ERR TSDB: compaction rule does not exist\r\n"
20513        );
20514    }
20515
20516    /// A label filter takes the series it names wherever they landed.
20517    #[test]
20518    fn a_label_query_across_stripes_finds_every_series() {
20519        let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
20520        let mut many = Fixture::striped(8);
20521        let mut homes: Vec<usize> = names
20522            .iter()
20523            .map(|name| many.server.striped(0).stripe_of(name))
20524            .collect();
20525        homes.sort_unstable();
20526        homes.dedup();
20527        assert!(homes.len() > 1, "the six keys are not all on one stripe");
20528
20529        let mut one = Fixture::new();
20530        let mut both = |parts: &[&[u8]]| {
20531            let a = one.run(parts);
20532            let b = many.run(parts);
20533            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20534            a
20535        };
20536        for name in &names {
20537            both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
20538            both(&[b"TS.ADD", name, b"1000", b"1"]);
20539        }
20540
20541        let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
20542        assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
20543        assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20544        assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
20545        assert_eq!(
20546            both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
20547            "*1\r\n$4\r\nroom\r\n"
20548        );
20549    }
20550
20551    /// Every hash command, and the field import beside it, on one stripe and on
20552    /// eight.
20553    ///
20554    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
20555    /// stripes do not draw the same numbers, so the only draw here is off a hash
20556    /// holding one field, where every generator gives the same answer.
20557    #[test]
20558    fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
20559        let script: &[&[&[u8]]] = &[
20560            &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
20561            &[b"HMSET", b"h", b"c", b"3"],
20562            &[b"HSETNX", b"h", b"a", b"9"],
20563            &[b"HSETNX", b"h", b"d", b"4"],
20564            &[b"HGET", b"h", b"a"],
20565            &[b"HGET", b"h", b"nope"],
20566            &[b"HMGET", b"h", b"a", b"nope"],
20567            &[b"HLEN", b"h"],
20568            &[b"HEXISTS", b"h", b"a"],
20569            &[b"HSTRLEN", b"h", b"a"],
20570            &[b"HGETALL", b"h"],
20571            &[b"HKEYS", b"h"],
20572            &[b"HVALS", b"h"],
20573            &[b"HINCRBY", b"h", b"a", b"5"],
20574            &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
20575            &[b"HSCAN", b"h", b"0"],
20576            &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
20577            &[b"HSCAN", b"h", b"0", b"NOVALUES"],
20578            &[b"HDEL", b"h", b"d"],
20579            &[b"HSET", b"one", b"f", b"v"],
20580            &[b"HRANDFIELD", b"one"],
20581            &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
20582            // The field deadlines.
20583            &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
20584            &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
20585            &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
20586            &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20587            &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
20588            &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
20589            &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
20590            &[b"HGET", b"h", b"b"],
20591            // The three that came later and word everything their own way.
20592            &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
20593            &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
20594            &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
20595            &[b"HGET", b"h", b"e"],
20596            // And the import, whose key is the third word.
20597            &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
20598            &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
20599            &[b"HGETALL", b"imp"],
20600            &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
20601            &[b"HIMPORT", b"DISCARD", b"fs"],
20602            // And the errors.
20603            &[b"SET", b"plain", b"v"],
20604            &[b"HSET", b"plain", b"a", b"1"],
20605            &[b"HGETALL", b"plain"],
20606            &[b"HGET", b"gone", b"a"],
20607            &[b"HINCRBY", b"h", b"a", b"nan"],
20608        ];
20609
20610        let mut one = Fixture::new();
20611        let mut many = Fixture::striped(8);
20612        // The field deadlines are absolute milliseconds worked out from the
20613        // clock, so both servers are put on the same one rather than left to
20614        // read the wall a moment apart.
20615        one.server.set_clock_ms(1_700_000_000_000);
20616        many.server.set_clock_ms(1_700_000_000_000);
20617        for parts in script {
20618            let a = one.run(parts);
20619            let b = many.run(parts);
20620            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20621        }
20622    }
20623
20624    /// Every array command, on one stripe and on eight.
20625    #[test]
20626    fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20627        let script: &[&[&[u8]]] = &[
20628            &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20629            &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20630            &[b"ARGET", b"a", b"1"],
20631            &[b"ARGET", b"a", b"99"],
20632            &[b"ARMGET", b"a", b"0", b"5", b"99"],
20633            &[b"ARGETRANGE", b"a", b"0", b"7"],
20634            &[b"ARLEN", b"a"],
20635            &[b"ARCOUNT", b"a"],
20636            &[b"ARINSERT", b"a", b"m", b"n"],
20637            &[b"ARSCAN", b"a", b"0", b"20"],
20638            &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20639            &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20640            &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20641            &[b"ARLASTITEMS", b"a", b"2"],
20642            &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20643            &[b"ARNEXT", b"a"],
20644            &[b"ARSEEK", b"a", b"3"],
20645            &[b"AROP", b"a", b"0", b"20", b"USED"],
20646            &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20647            &[b"ARINFO", b"a"],
20648            &[b"ARINFO", b"a", b"FULL"],
20649            &[b"ARDEL", b"a", b"0"],
20650            &[b"ARDELRANGE", b"a", b"1", b"2"],
20651            &[b"ARCOUNT", b"a"],
20652            &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20653            &[b"ARGETRANGE", b"r", b"0", b"9"],
20654            // And the errors.
20655            &[b"SET", b"plain", b"v"],
20656            &[b"ARGET", b"plain", b"0"],
20657            &[b"ARSET", b"plain", b"0", b"v"],
20658            &[b"ARGET", b"gone", b"0"],
20659            &[b"ARSET", b"a", b"bad", b"v"],
20660        ];
20661
20662        let mut one = Fixture::new();
20663        let mut many = Fixture::striped(8);
20664        for parts in script {
20665            let a = one.run(parts);
20666            let b = many.run(parts);
20667            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20668        }
20669    }
20670
20671    /// Every graph and vector set command, on one stripe and on eight.
20672    ///
20673    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
20674    /// not: it draws from the stripe's generator, and the stripes do not share
20675    /// one.
20676    #[test]
20677    fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20678        let script: &[&[&[u8]]] = &[
20679            &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20680            &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20681            &[b"G.NADD", b"g", b"n3"],
20682            &[b"G.NGET", b"g", b"n1"],
20683            &[b"G.NGET", b"g", b"gone"],
20684            &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20685            &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20686            &[b"G.OUT", b"g", b"n1", b"knows"],
20687            &[b"G.IN", b"g", b"n2", b"knows"],
20688            &[b"G.DEG", b"g", b"n1", b"knows"],
20689            &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20690            &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20691            &[b"G.PATH", b"g", b"n1", b"n3"],
20692            &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20693            &[b"G.NDEL", b"g", b"n3"],
20694            &[b"G.NGET", b"g", b"n3"],
20695            // The vector set, which is one index under one key.
20696            &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20697            &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20698            &[b"VCARD", b"v"],
20699            &[b"VDIM", b"v"],
20700            &[b"VEMB", b"v", b"e1"],
20701            &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20702            &[b"VSIM", b"v", b"ELE", b"e1"],
20703            &[b"VISMEMBER", b"v", b"e1"],
20704            &[b"VISMEMBER", b"v", b"gone"],
20705            &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20706            &[b"VGETATTR", b"v", b"e1"],
20707            &[b"VRANGE", b"v", b"-", b"+"],
20708            &[b"VLINKS", b"v", b"e1"],
20709            &[b"VINFO", b"v"],
20710            &[b"VREM", b"v", b"e2"],
20711            &[b"VCARD", b"v"],
20712            // And the errors.
20713            &[b"SET", b"plain", b"v"],
20714            &[b"G.NGET", b"plain", b"n1"],
20715            &[b"VCARD", b"plain"],
20716            &[b"G.NADD", b"gone2", b"n"],
20717            &[b"VEMB", b"gone3", b"e"],
20718        ];
20719
20720        let mut one = Fixture::new();
20721        let mut many = Fixture::striped(8);
20722        for parts in script {
20723            let a = one.run(parts);
20724            let b = many.run(parts);
20725            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20726        }
20727    }
20728
20729    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
20730    /// command, on one stripe and on eight.
20731    #[test]
20732    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20733        let script: &[&[&[u8]]] = &[
20734            // The bloom filter.
20735            &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20736            &[b"BF.ADD", b"bf", b"a"],
20737            &[b"BF.ADD", b"bf", b"a"],
20738            &[b"BF.MADD", b"bf", b"b", b"c"],
20739            &[b"BF.EXISTS", b"bf", b"a"],
20740            &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20741            &[b"BF.CARD", b"bf"],
20742            &[b"BF.INFO", b"bf"],
20743            &[b"BF.INFO", b"bf", b"CAPACITY"],
20744            &[b"BF.DEBUG", b"bf"],
20745            &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20746            &[b"BF.EXISTS", b"made", b"x"],
20747            &[b"BF.SCANDUMP", b"bf", b"0"],
20748            // The cuckoo filter.
20749            &[b"CF.RESERVE", b"cf", b"100"],
20750            &[b"CF.ADD", b"cf", b"a"],
20751            &[b"CF.ADDNX", b"cf", b"a"],
20752            &[b"CF.COUNT", b"cf", b"a"],
20753            &[b"CF.EXISTS", b"cf", b"a"],
20754            &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20755            &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20756            &[b"CF.DEL", b"cf", b"a"],
20757            &[b"CF.COMPACT", b"cf"],
20758            &[b"CF.INFO", b"cf"],
20759            &[b"CF.DEBUG", b"cf"],
20760            &[b"CF.SCANDUMP", b"cf", b"0"],
20761            // The count min sketch.
20762            &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20763            &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20764            &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20765            &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20766            &[b"CMS.INFO", b"cms"],
20767            // The top k sketch.
20768            &[b"TOPK.RESERVE", b"tk", b"3"],
20769            &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20770            &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20771            &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20772            &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20773            &[b"TOPK.LIST", b"tk"],
20774            &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20775            &[b"TOPK.INFO", b"tk"],
20776            // The t digest.
20777            &[b"TDIGEST.CREATE", b"td"],
20778            &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20779            &[b"TDIGEST.MIN", b"td"],
20780            &[b"TDIGEST.MAX", b"td"],
20781            &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20782            &[b"TDIGEST.CDF", b"td", b"3"],
20783            &[b"TDIGEST.RANK", b"td", b"3"],
20784            &[b"TDIGEST.REVRANK", b"td", b"3"],
20785            &[b"TDIGEST.BYRANK", b"td", b"0"],
20786            &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20787            &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20788            &[b"TDIGEST.INFO", b"td"],
20789            &[b"TDIGEST.RESET", b"td"],
20790            &[b"TDIGEST.MIN", b"td"],
20791            // And the errors.
20792            &[b"SET", b"plain", b"v"],
20793            &[b"BF.ADD", b"plain", b"a"],
20794            &[b"CF.ADD", b"plain", b"a"],
20795            &[b"CMS.QUERY", b"plain", b"a"],
20796            &[b"TOPK.ADD", b"plain", b"a"],
20797            &[b"TDIGEST.ADD", b"plain", b"1"],
20798            &[b"CMS.INFO", b"gone"],
20799            &[b"TOPK.INFO", b"gone"],
20800            &[b"TDIGEST.INFO", b"gone"],
20801        ];
20802
20803        let mut one = Fixture::new();
20804        let mut many = Fixture::striped(8);
20805        for parts in script {
20806            let a = one.run(parts);
20807            let b = many.run(parts);
20808            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20809        }
20810    }
20811
20812    /// The two sketch merges, with their sources on stripes of their own.
20813    ///
20814    /// These are the only two commands in the ten groups that name more than one
20815    /// key, and both read a run of sources and write a destination, so both go
20816    /// wrong in the same way if a merge holds one store and looks every source up
20817    /// in it.
20818    #[test]
20819    fn a_sketch_merge_across_stripes_reads_every_source() {
20820        let mut many = Fixture::striped(8);
20821        let other = apart(&mut many, "s1");
20822        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20823        let mut one = Fixture::new();
20824        let mut both = |parts: &[&[u8]]| {
20825            let a = one.run(parts);
20826            let b = many.run(parts);
20827            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20828            a
20829        };
20830
20831        // The count min sketch. The destination has to be the sources' shape,
20832        // and it is named first, so all three keys are read before anything is
20833        // written.
20834        for key in [b"cd".as_slice(), s1, s2] {
20835            both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20836        }
20837        both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20838        both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20839        assert_eq!(
20840            both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20841            "+OK\r\n",
20842            "the merge took both sources"
20843        );
20844        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20845        // And with weights, which are read against the sources in order.
20846        both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20847        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20848        // A source that is not a sketch is answered before anything is written.
20849        both(&[b"SET", b"plain", b"v"]);
20850        assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20851        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20852
20853        // The t digest, which builds its destination and then puts it in place.
20854        // The two source keys are used again here, so what they held goes first.
20855        both(&[b"FLUSHALL"]);
20856        both(&[b"TDIGEST.CREATE", b"td"]);
20857        both(&[b"TDIGEST.CREATE", s1]);
20858        both(&[b"TDIGEST.CREATE", s2]);
20859        both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20860        both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20861        assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20862        assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20863        assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20864    }
20865
20866    /// Every shape of `SORT`, on one stripe and on eight.
20867    ///
20868    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
20869    /// destination are four different names and nothing lines them up, so on
20870    /// eight stripes this script is reading and writing all over the database
20871    /// while on one it is doing what it always did.
20872    #[test]
20873    fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20874        let script: &[&[&[u8]]] = &[
20875            &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20876            &[b"SORT", b"l"],
20877            &[b"SORT", b"l", b"DESC"],
20878            &[b"SORT", b"l", b"ALPHA"],
20879            &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20880            &[b"SORT_RO", b"l"],
20881            // A weight per element, so the order comes off keys the command
20882            // never named.
20883            &[
20884                b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20885            ],
20886            &[b"SORT", b"l", b"BY", b"w_*"],
20887            &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20888            &[b"DEL", b"w_2"],
20889            &[b"SORT", b"l", b"BY", b"w_*"],
20890            // And the answer off another set of keys again, with `#` mixed in
20891            // so the rows are not all lookups.
20892            &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20893            &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20894            // A pattern that reaches into a hash, which is another key again.
20895            &[b"HSET", b"h_1", b"f", b"9"],
20896            &[b"HSET", b"h_2", b"f", b"8"],
20897            &[b"HSET", b"h_3", b"f", b"7"],
20898            &[b"HSET", b"h_10", b"f", b"6"],
20899            &[b"SORT", b"l", b"BY", b"h_*->f"],
20900            &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20901            // The destination, which is a fourth place to land.
20902            &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20903            &[b"LRANGE", b"out", b"0", b"-1"],
20904            &[b"SORT", b"l", b"STORE", b"l"],
20905            &[b"LRANGE", b"l", b"0", b"-1"],
20906            // An empty result takes the destination away rather than leaving a
20907            // list of nothing behind.
20908            &[b"SORT", b"missing", b"STORE", b"out"],
20909            &[b"EXISTS", b"out"],
20910            // A set and a sorted set sort the same way a list does, and a set
20911            // written to a destination is sorted even when nothing asked.
20912            &[b"SADD", b"s", b"c", b"a", b"b"],
20913            &[b"SORT", b"s", b"ALPHA"],
20914            &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20915            &[b"LRANGE", b"out", b"0", b"-1"],
20916            &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20917            &[b"SORT", b"z", b"BY", b"nosort"],
20918            &[b"SORT", b"z", b"ALPHA", b"DESC"],
20919            // And the two ways it refuses: a key of the wrong type, and an
20920            // element that is not a number under a numeric sort.
20921            &[b"SET", b"str", b"v"],
20922            &[b"SORT", b"str"],
20923            &[b"RPUSH", b"words", b"one", b"two"],
20924            &[b"SORT", b"words"],
20925            &[b"SORT_RO", b"l", b"STORE", b"out"],
20926        ];
20927
20928        let mut one = Fixture::new();
20929        let mut many = Fixture::striped(8);
20930        for parts in script {
20931            let a = one.run(parts);
20932            let b = many.run(parts);
20933            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20934        }
20935    }
20936
20937    /// One `SORT` whose four kinds of key are on stripes of their own.
20938    ///
20939    /// The script above spreads keys around by writing enough of them, and this
20940    /// one checks the spread rather than trusting it: the list, the weight key
20941    /// for one of its elements and the destination are asserted to be in three
20942    /// places before the command runs.
20943    #[test]
20944    fn a_sort_across_stripes_reads_every_pattern_key() {
20945        let mut f = Fixture::striped(8);
20946        let out = apart(&mut f, "l");
20947        let (list, dest) = (b"l".as_slice(), out.as_bytes());
20948
20949        f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20950        f.run(&[
20951            b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20952        ]);
20953        f.run(&[
20954            b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20955        ]);
20956
20957        // The weights are four keys and they are not all in one place, which is
20958        // the thing that would go unnoticed if the command held a stripe.
20959        let db = f.server.striped(0);
20960        let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20961            .iter()
20962            .map(|k| db.stripe_of(k.as_slice()))
20963            .collect();
20964        assert!(
20965            weights.iter().any(|s| *s != weights[0]),
20966            "the four weight keys all landed on one stripe, so this proves nothing"
20967        );
20968
20969        assert_eq!(
20970            f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20971            "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20972            "the order came off the weights and the answer off the data keys"
20973        );
20974        assert_eq!(
20975            f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20976            ":4\r\n"
20977        );
20978        assert_eq!(
20979            f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20980            "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20981            "the destination is on a stripe of its own and got the whole answer"
20982        );
20983    }
20984
20985    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
20986    /// decide what shape it is stored in.
20987    ///
20988    /// This is the setting that would go wrong quietly. A stripe that kept the
20989    /// old ladder would hold the same hash in a different encoding from the
20990    /// stripe next to it, and the only thing that would ever say so is
20991    /// `OBJECT ENCODING`, which is why the check is on that.
20992    #[test]
20993    fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20994        let mut f = Fixture::striped(8);
20995        let other = apart(&mut f, "h");
20996        let (first, second) = (b"h".as_slice(), other.as_bytes());
20997
20998        assert_eq!(
20999            f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
21000            "+OK\r\n"
21001        );
21002        assert_eq!(
21003            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
21004            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
21005            "the read comes off one stripe and has to answer for all of them"
21006        );
21007        for key in [first, second] {
21008            f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
21009            assert_eq!(
21010                f.run(&[b"OBJECT", b"ENCODING", key]),
21011                "$8\r\nlistpack\r\n",
21012                "two fields is still under the ladder"
21013            );
21014            f.run(&[b"HSET", key, b"c", b"3"]);
21015            assert_eq!(
21016                f.run(&[b"OBJECT", b"ENCODING", key]),
21017                "$9\r\nhashtable\r\n",
21018                "three fields is over it, on whichever stripe the key is on"
21019            );
21020        }
21021
21022        // And the policy, which every stripe has to agree about for the same
21023        // reason: an eviction draws from one stripe at a time.
21024        assert_eq!(
21025            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
21026            "+OK\r\n"
21027        );
21028        let db = f.server.striped(0);
21029        assert!(
21030            (0..db.width()).all(|i| db.hold_stripe(i).policy().name() == "allkeys-lru"),
21031            "a stripe kept the old policy"
21032        );
21033    }
21034
21035    /// What an index holds, as the two numbers `FT.INFO` reports about it.
21036    ///
21037    /// Read off the registry rather than parsed back out of an `FT.INFO` reply,
21038    /// because the reply is thirty odd fields and these two are the ones the
21039    /// keyspace hook moves.
21040    fn held(f: &Fixture, name: &[u8]) -> (usize, u32) {
21041        let search = f.server.search.lock();
21042        let index = search.named(name).expect("the index is there");
21043        (index.held.docs.len(), index.held.docs.last())
21044    }
21045
21046    /// A hash written under an index's prefix reaches it, and one written
21047    /// outside the prefix does not.
21048    #[test]
21049    fn a_hash_that_is_written_reaches_the_index_that_follows_it() {
21050        let mut f = Fixture::new();
21051        f.run(&[
21052            b"FT.CREATE",
21053            b"ix",
21054            b"PREFIX",
21055            b"1",
21056            b"p:",
21057            b"SCHEMA",
21058            b"t",
21059            b"TEXT",
21060        ]);
21061        f.run(&[b"HSET", b"p:1", b"t", b"running dogs"]);
21062        assert_eq!(held(&f, b"ix"), (1, 1));
21063        f.run(&[b"HSET", b"other:1", b"t", b"running dogs"]);
21064        assert_eq!(held(&f, b"ix"), (1, 1));
21065
21066        // Every field of the key and not the one the command named, since a
21067        // document is read from nothing every time.
21068        f.run(&[b"HSET", b"p:1", b"u", b"beta"]);
21069        f.run(&[b"HDEL", b"p:1", b"u"]);
21070        assert_eq!(held(&f, b"ix"), (1, 3));
21071        let search = f.server.search.lock();
21072        let index = search.named(b"ix").expect("there");
21073        assert_eq!(index.held.docs.id(b"p:1"), Some(3));
21074    }
21075
21076    /// A fresh index reads the keys that were already there, and walks past a
21077    /// key of the wrong type without counting a failure.
21078    #[test]
21079    fn a_fresh_index_reads_the_keys_that_were_already_there() {
21080        let mut f = Fixture::new();
21081        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21082        f.run(&[b"SET", b"p:str", b"not a hash"]);
21083        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
21084        f.run(&[
21085            b"FT.CREATE",
21086            b"ix",
21087            b"PREFIX",
21088            b"1",
21089            b"p:",
21090            b"SCHEMA",
21091            b"t",
21092            b"TEXT",
21093        ]);
21094
21095        assert_eq!(held(&f, b"ix"), (1, 1));
21096        let search = f.server.search.lock();
21097        let index = search.named(b"ix").expect("there");
21098        assert_eq!(index.trouble.whole().failures(), 0);
21099    }
21100
21101    /// `SKIPINITIALSCAN` leaves what was there alone, and a later write to one
21102    /// of those keys still lands.
21103    #[test]
21104    fn an_index_that_skipped_the_scan_fills_up_on_the_next_write() {
21105        let mut f = Fixture::new();
21106        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21107        f.run(&[
21108            b"FT.CREATE",
21109            b"ix",
21110            b"PREFIX",
21111            b"1",
21112            b"p:",
21113            b"SKIPINITIALSCAN",
21114            b"SCHEMA",
21115            b"t",
21116            b"TEXT",
21117        ]);
21118        assert_eq!(held(&f, b"ix"), (0, 0));
21119        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21120        assert_eq!(held(&f, b"ix"), (1, 1));
21121    }
21122
21123    /// A command that changed nothing leaves the document where it was, which
21124    /// is not the same as a command that was not a write.
21125    ///
21126    /// All five of these were measured against 8.10.1. Writing the same value
21127    /// again moves the number and a deadline set for later does not, which is
21128    /// the pair that makes the rule "the fields are not what they were" rather
21129    /// than "this was a write".
21130    #[test]
21131    fn only_a_real_change_gives_the_document_a_new_number() {
21132        let mut f = Fixture::new();
21133        f.run(&[
21134            b"FT.CREATE",
21135            b"ix",
21136            b"PREFIX",
21137            b"1",
21138            b"p:",
21139            b"SCHEMA",
21140            b"t",
21141            b"TEXT",
21142        ]);
21143        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21144        assert_eq!(held(&f, b"ix"), (1, 1));
21145
21146        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21147        assert_eq!(held(&f, b"ix"), (1, 2), "the same value still rewrites");
21148
21149        for quiet in [
21150            vec![b"HSETNX".as_slice(), b"p:1", b"t", b"other"],
21151            vec![b"HDEL".as_slice(), b"p:1", b"nosuch"],
21152            vec![b"HGET".as_slice(), b"p:1", b"t"],
21153            vec![b"HGETALL".as_slice(), b"p:1"],
21154            vec![b"HEXPIRE".as_slice(), b"p:1", b"100", b"FIELDS", b"1", b"t"],
21155            vec![b"HPERSIST".as_slice(), b"p:1", b"FIELDS", b"1", b"t"],
21156            vec![
21157                b"HGETEX".as_slice(),
21158                b"p:1",
21159                b"EX",
21160                b"100",
21161                b"FIELDS",
21162                b"1",
21163                b"t",
21164            ],
21165            vec![b"HGETDEL".as_slice(), b"p:1", b"FIELDS", b"1", b"nosuch"],
21166        ] {
21167            f.run(&quiet);
21168            assert_eq!(held(&f, b"ix"), (1, 2), "{:?} moved the document", quiet[0]);
21169        }
21170
21171        // And the ones that do change something.
21172        f.run(&[b"HSET", b"p:2", b"n", b"1"]);
21173        f.run(&[b"HINCRBY", b"p:2", b"n", b"1"]);
21174        assert_eq!(held(&f, b"ix"), (2, 4));
21175        // A deadline that has already passed takes the field away, and taking
21176        // the last field away takes the key and the document with it. The
21177        // number still moves on the way past, because the field going and the
21178        // key going are two separate pieces of news and the first of them
21179        // writes the document one last time.
21180        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"n"]);
21181        assert_eq!(held(&f, b"ix"), (1, 5));
21182    }
21183
21184    /// The two ways of emptying a hash, which do not leave the same thing
21185    /// behind. `HDEL` of the last field spends no number and is counted as a
21186    /// refusal, and a deadline that has already passed spends one on a document
21187    /// nobody sees and is counted as nothing. Measured against 8.10.1 and not
21188    /// something anyone would guess.
21189    #[test]
21190    fn a_key_emptied_by_a_deadline_spends_a_number_and_one_emptied_by_hdel_does_not() {
21191        /// The index's own failure count.
21192        fn refused(f: &Fixture, name: &[u8]) -> u64 {
21193            let search = f.server.search.lock();
21194            let index = search.named(name).expect("the index is there");
21195            index.trouble.whole().failures()
21196        }
21197
21198        let mut f = Fixture::new();
21199        f.run(&[
21200            b"FT.CREATE",
21201            b"ix",
21202            b"PREFIX",
21203            b"1",
21204            b"p:",
21205            b"SCHEMA",
21206            b"t",
21207            b"TEXT",
21208        ]);
21209        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21210        assert_eq!(held(&f, b"ix"), (1, 1));
21211        f.run(&[b"HDEL", b"p:1", b"t"]);
21212        assert_eq!(
21213            held(&f, b"ix"),
21214            (0, 1),
21215            "HDEL of the last field spends none"
21216        );
21217        assert_eq!(refused(&f, b"ix"), 1, "and is counted as a refusal");
21218
21219        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21220        assert_eq!(held(&f, b"ix"), (1, 2));
21221        f.run(&[b"HEXPIRE", b"p:2", b"0", b"FIELDS", b"1", b"t"]);
21222        assert_eq!(held(&f, b"ix"), (0, 3), "a deadline spends one");
21223        assert_eq!(refused(&f, b"ix"), 1, "and is counted as nothing");
21224
21225        f.run(&[b"HSET", b"p:3", b"t", b"alpha"]);
21226        assert_eq!(held(&f, b"ix"), (1, 4));
21227        f.run(&[b"HGETDEL", b"p:3", b"FIELDS", b"1", b"t"]);
21228        assert_eq!(held(&f, b"ix"), (0, 5), "and so does HGETDEL");
21229
21230        // Two fields and one command is one rewrite and not two, whichever way
21231        // the fields go.
21232        f.run(&[b"HSET", b"p:4", b"t", b"alpha", b"u", b"beta"]);
21233        assert_eq!(held(&f, b"ix"), (1, 6));
21234        f.run(&[b"HEXPIRE", b"p:4", b"0", b"FIELDS", b"2", b"t", b"u"]);
21235        assert_eq!(held(&f, b"ix"), (0, 7));
21236        assert_eq!(refused(&f, b"ix"), 1);
21237    }
21238
21239    /// `HSETEX` with a deadline that has already passed is two pieces of news
21240    /// from one command, so the number moves twice and the value never reaches
21241    /// the index.
21242    #[test]
21243    fn a_field_written_already_past_its_deadline_moves_the_number_twice() {
21244        let mut f = Fixture::new();
21245        f.run(&[
21246            b"FT.CREATE",
21247            b"ix",
21248            b"PREFIX",
21249            b"1",
21250            b"p:",
21251            b"SCHEMA",
21252            b"t",
21253            b"TEXT",
21254            b"u",
21255            b"TEXT",
21256        ]);
21257        f.run(&[b"HSET", b"p:1", b"u", b"keepme"]);
21258        assert_eq!(held(&f, b"ix"), (1, 1));
21259        f.run(&[
21260            b"HSETEX", b"p:1", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21261        ]);
21262        assert_eq!(
21263            held(&f, b"ix"),
21264            (1, 3),
21265            "the key lived and the field did not"
21266        );
21267
21268        // And the same when the key does not survive it.
21269        f.run(&[b"HSET", b"p:2", b"t", b"alpha"]);
21270        assert_eq!(held(&f, b"ix"), (2, 4));
21271        f.run(&[
21272            b"HSETEX", b"p:2", b"EXAT", b"1", b"FIELDS", b"1", b"t", b"zqx",
21273        ]);
21274        assert_eq!(held(&f, b"ix"), (1, 6));
21275    }
21276
21277    /// The number one key is indexed under, or `None` when it holds no
21278    /// document.
21279    fn number(f: &Fixture, name: &[u8], key: &[u8]) -> Option<u32> {
21280        let search = f.server.search.lock();
21281        let index = search.named(name).expect("the index is there");
21282        index.held.docs.id(key)
21283    }
21284
21285    /// An index over `p:` with one document under `p:1`, which is where four of
21286    /// the tests below start.
21287    fn indexed() -> Fixture {
21288        let mut f = Fixture::new();
21289        f.run(&[
21290            b"FT.CREATE",
21291            b"ix",
21292            b"PREFIX",
21293            b"1",
21294            b"p:",
21295            b"SCHEMA",
21296            b"t",
21297            b"TEXT",
21298        ]);
21299        f.run(&[b"HSET", b"p:1", b"t", b"alpha"]);
21300        f
21301    }
21302
21303    /// Every way a keyspace command takes a key away leaves no document behind,
21304    /// and none of them spends a number or is counted as a refusal.
21305    #[test]
21306    fn a_key_a_keyspace_command_takes_away_loses_its_document() {
21307        for take in [
21308            vec![b"DEL".as_slice(), b"p:1"],
21309            vec![b"UNLINK".as_slice(), b"p:1"],
21310            vec![b"PEXPIREAT".as_slice(), b"p:1", b"1"],
21311            vec![b"EXPIRE".as_slice(), b"p:1", b"-1"],
21312        ] {
21313            let mut f = indexed();
21314            assert_eq!(held(&f, b"ix"), (1, 1));
21315            f.run(&take);
21316            assert_eq!(held(&f, b"ix"), (0, 1), "{:?} left something", take[0]);
21317            let search = f.server.search.lock();
21318            let index = search.named(b"ix").expect("the index is there");
21319            assert_eq!(index.trouble.whole().failures(), 0, "{:?}", take[0]);
21320        }
21321
21322        // A deadline that has not passed yet is not one of them.
21323        let mut f = indexed();
21324        f.run(&[b"EXPIRE", b"p:1", b"1000"]);
21325        assert_eq!(held(&f, b"ix"), (1, 1));
21326        f.run(&[b"PERSIST", b"p:1"]);
21327        assert_eq!(held(&f, b"ix"), (1, 1));
21328    }
21329
21330    /// A rename inside the prefix keeps the number the document had, which is
21331    /// the one write on a followed key that does not spend one. Out of the
21332    /// prefix is an erase and into it is a fresh reading, both measured.
21333    #[test]
21334    fn a_rename_inside_the_prefix_keeps_the_number_the_document_had() {
21335        let mut f = indexed();
21336        f.run(&[b"RENAME", b"p:1", b"p:2"]);
21337        assert_eq!(held(&f, b"ix"), (1, 1), "nothing was read again");
21338        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
21339        assert_eq!(number(&f, b"ix", b"p:1"), None);
21340
21341        f.run(&[b"RENAME", b"p:2", b"q:1"]);
21342        assert_eq!(held(&f, b"ix"), (0, 1), "out of the prefix is an erase");
21343
21344        f.run(&[b"RENAME", b"q:1", b"p:3"]);
21345        assert_eq!(held(&f, b"ix"), (1, 2), "and into it is a reading");
21346        assert_eq!(number(&f, b"ix", b"p:3"), Some(2));
21347
21348        // `RENAMENX` goes the same way, and the one that answers zero changes
21349        // nothing.
21350        f.run(&[b"HSET", b"p:4", b"t", b"beta"]);
21351        assert_eq!(f.run(&[b"RENAMENX", b"p:3", b"p:4"]), ":0\r\n");
21352        assert_eq!(held(&f, b"ix"), (2, 3));
21353        f.run(&[b"RENAMENX", b"p:3", b"p:5"]);
21354        assert_eq!(number(&f, b"ix", b"p:5"), Some(2));
21355    }
21356
21357    /// A rename over a key that already had a document leaves one document and
21358    /// not two. A real server leaves both, and D-64 is that difference.
21359    #[test]
21360    fn a_rename_over_a_document_leaves_one_of_them() {
21361        let mut f = indexed();
21362        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
21363        assert_eq!(held(&f, b"ix"), (2, 2));
21364        f.run(&[b"RENAME", b"p:1", b"p:2"]);
21365        assert_eq!(held(&f, b"ix"), (1, 2));
21366        assert_eq!(number(&f, b"ix", b"p:2"), Some(1));
21367    }
21368
21369    /// A key that arrives under the prefix by being copied or restored is read
21370    /// as a new document, and one that is written over by something that is not
21371    /// a hash is erased without a word.
21372    #[test]
21373    fn a_key_that_arrives_under_the_prefix_is_read_and_one_overwritten_is_erased() {
21374        let mut f = indexed();
21375        f.run(&[b"HSET", b"q:1", b"t", b"beta"]);
21376        f.run(&[b"COPY", b"q:1", b"p:2"]);
21377        assert_eq!(held(&f, b"ix"), (2, 2));
21378        assert_eq!(number(&f, b"ix", b"p:2"), Some(2));
21379
21380        // Out of the prefix, where the source keeps the document it had.
21381        f.run(&[b"COPY", b"p:1", b"q:2"]);
21382        assert_eq!(held(&f, b"ix"), (2, 2));
21383
21384        // Over a key that has one, which is a new reading and not a rename.
21385        f.run(&[b"COPY", b"q:1", b"p:1", b"REPLACE"]);
21386        assert_eq!(held(&f, b"ix"), (2, 3));
21387        assert_eq!(number(&f, b"ix", b"p:1"), Some(3));
21388
21389        // And a string landing on top of a document takes it away, spending no
21390        // number and counting no failure.
21391        f.run(&[b"SET", b"s:1", b"plain"]);
21392        f.run(&[b"COPY", b"s:1", b"p:1", b"REPLACE"]);
21393        assert_eq!(held(&f, b"ix"), (1, 3));
21394        let dump = f.run(&[b"DUMP", b"q:1"]);
21395        assert!(dump.starts_with('$'), "{dump}");
21396    }
21397
21398    /// The keyspace group reads a key back on database zero whatever database
21399    /// the command ran on, which is measured and is not what the hash commands
21400    /// do. A `COPY` into another database indexes nothing and takes away
21401    /// whatever the destination had, and a `RESTORE` anywhere else is invisible.
21402    #[test]
21403    fn the_keyspace_group_reads_database_zero_whatever_database_it_ran_on() {
21404        let mut f = indexed();
21405        f.run(&[b"HSET", b"p:2", b"t", b"beta"]);
21406        assert_eq!(held(&f, b"ix"), (2, 2));
21407        // Into database one, so the indexes look for `p:2` on database zero,
21408        // find the one that is still there and read it again.
21409        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
21410        assert_eq!(held(&f, b"ix"), (2, 3));
21411        // And with nothing under that name on database zero, the copy leaves
21412        // the index one document lighter than it found it.
21413        f.run(&[b"DEL", b"p:2"]);
21414        assert_eq!(held(&f, b"ix"), (1, 3));
21415        f.run(&[b"COPY", b"p:1", b"p:2", b"DB", b"1", b"REPLACE"]);
21416        assert_eq!(held(&f, b"ix"), (1, 3), "the copy landed out of sight");
21417
21418        // A restore on another database is the same story.
21419        let dump = f.run(&[b"DUMP", b"p:1"]);
21420        assert!(dump.starts_with('$'), "{dump}");
21421        f.run(&[b"SELECT", b"1"]);
21422        f.run(&[b"HSET", b"q:1", b"t", b"gamma"]);
21423        f.run(&[b"RENAME", b"q:1", b"p:3"]);
21424        assert_eq!(held(&f, b"ix"), (1, 3), "and so is a rename");
21425    }
21426
21427    /// `MOVE` is not a change at all, because an index follows a key by name
21428    /// and a write on any database still reaches it.
21429    #[test]
21430    fn a_move_leaves_the_document_where_it_is() {
21431        let mut f = indexed();
21432        f.run(&[b"MOVE", b"p:1", b"1"]);
21433        assert_eq!(held(&f, b"ix"), (1, 1), "the key moved and nothing else");
21434        assert_eq!(number(&f, b"ix", b"p:1"), Some(1));
21435
21436        f.run(&[b"SELECT", b"1"]);
21437        f.run(&[b"HSET", b"p:1", b"t", b"beta"]);
21438        assert_eq!(held(&f, b"ix"), (1, 2), "and a write there still lands");
21439        f.run(&[b"DEL", b"p:1"]);
21440        assert_eq!(held(&f, b"ix"), (0, 2));
21441    }
21442
21443    /// A flush takes every index with it, whichever database it flushed.
21444    #[test]
21445    fn a_flush_drops_the_indexes() {
21446        for flush in [b"FLUSHALL".as_slice(), b"FLUSHDB"] {
21447            let mut f = indexed();
21448            f.run(&[flush]);
21449            assert!(f.server.search.lock().is_empty(), "{flush:?} kept an index");
21450            assert_eq!(f.run(&[b"FT._LIST"]), "*0\r\n");
21451        }
21452
21453        // Even on a database no index ever read, which is what a real server
21454        // does and is not what anyone would guess.
21455        let mut f = indexed();
21456        f.run(&[b"SELECT", b"9"]);
21457        f.run(&[b"FLUSHDB"]);
21458        assert!(f.server.search.lock().is_empty());
21459    }
21460
21461    /// A key that will not read is counted against the index and against the
21462    /// field, and `FT.INFO` says so.
21463    #[test]
21464    fn a_hash_that_will_not_read_is_counted_where_ft_info_reports_it() {
21465        let mut f = Fixture::new();
21466        f.run(&[
21467            b"FT.CREATE",
21468            b"ix",
21469            b"PREFIX",
21470            b"1",
21471            b"p:",
21472            b"SCHEMA",
21473            b"n",
21474            b"NUMERIC",
21475        ]);
21476        f.run(&[b"HSET", b"p:1", b"n", b"notanumber"]);
21477        assert_eq!(held(&f, b"ix"), (0, 0));
21478
21479        let reply = f.run(&[b"FT.INFO", b"ix"]);
21480        assert!(
21481            reply.contains("SEARCH_NUMERIC_VALUE_INVALID Invalid numeric value: 'notanumber'"),
21482            "{reply}"
21483        );
21484        assert!(reply.contains("hash_indexing_failures"), "{reply}");
21485    }
21486
21487    /// An index can only be made on database zero, and the check comes after
21488    /// the `IFNX` shortcut and before everything else.
21489    #[test]
21490    fn an_index_can_only_be_made_on_database_zero() {
21491        let mut f = Fixture::new();
21492        f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]);
21493        f.run(&[b"SELECT", b"1"]);
21494        let refused = "-Cannot create index on db != 0\r\n";
21495        assert_eq!(
21496            f.run(&[b"FT.CREATE", b"jx", b"SCHEMA", b"t", b"TEXT"]),
21497            refused
21498        );
21499        // The name is taken, and it still answers about the database.
21500        assert_eq!(
21501            f.run(&[b"FT.CREATE", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21502            refused
21503        );
21504        // And so does one whose arguments are nonsense.
21505        assert_eq!(
21506            f.run(&[b"FT.CREATE", b"zz", b"BOGUS", b"SCHEMA", b"t", b"TEXT"]),
21507            refused
21508        );
21509        // `IFNX` over a name that is taken is the one that gets through.
21510        assert_eq!(
21511            f.run(&[b"FT._CREATEIFNX", b"ix", b"SCHEMA", b"t", b"TEXT"]),
21512            "+OK\r\n"
21513        );
21514        assert_eq!(f.server.search.lock().len(), 1);
21515    }
21516
21517    /// The scan reads the database the create was run on, and after that the
21518    /// index follows its keys in every database.
21519    ///
21520    /// The asymmetry is a real server's, measured, and it is the sort of thing
21521    /// nobody would arrive at by choosing.
21522    #[test]
21523    fn the_scan_is_one_database_and_the_following_is_all_of_them() {
21524        let mut f = Fixture::new();
21525        f.run(&[b"SELECT", b"1"]);
21526        f.run(&[b"HSET", b"p:9", b"t", b"on one"]);
21527        f.run(&[b"SELECT", b"0"]);
21528        f.run(&[b"HSET", b"p:0", b"t", b"on zero"]);
21529        f.run(&[
21530            b"FT.CREATE",
21531            b"ix",
21532            b"PREFIX",
21533            b"1",
21534            b"p:",
21535            b"SCHEMA",
21536            b"t",
21537            b"TEXT",
21538        ]);
21539        assert_eq!(held(&f, b"ix"), (1, 1), "the scan read database zero only");
21540
21541        f.run(&[b"SELECT", b"1"]);
21542        f.run(&[b"HSET", b"p:8", b"t", b"later"]);
21543        assert_eq!(
21544            held(&f, b"ix"),
21545            (2, 2),
21546            "and then it follows every database"
21547        );
21548    }
21549
21550    /// Four documents over the two kinds of field a query can ask about, which
21551    /// is the corpus the searches below read.
21552    fn corpus(f: &mut Fixture) {
21553        f.run(&[
21554            b"FT.CREATE",
21555            b"sx",
21556            b"PREFIX",
21557            b"1",
21558            b"d:",
21559            b"SCHEMA",
21560            b"t",
21561            b"TEXT",
21562            b"g",
21563            b"TAG",
21564            b"n",
21565            b"NUMERIC",
21566        ]);
21567        for (key, text, tag, number) in [
21568            (b"d:1".as_slice(), "alpha beta", "aa,bb", "1"),
21569            (b"d:2", "alpha gamma", "bb", "2"),
21570            (b"d:3", "delta", "cc", "3"),
21571            (b"d:4", "alpha beta gamma", "aa,cc", "4"),
21572        ] {
21573            f.run(&[
21574                b"HSET",
21575                key,
21576                b"t",
21577                text.as_bytes(),
21578                b"g",
21579                tag.as_bytes(),
21580                b"n",
21581                number.as_bytes(),
21582            ]);
21583        }
21584    }
21585
21586    /// A search answers a total and then a row for every key in the window,
21587    /// with the fields of that key after it.
21588    #[test]
21589    fn a_search_answers_a_total_and_then_the_rows() {
21590        let mut f = Fixture::new();
21591        corpus(&mut f);
21592        assert_eq!(
21593            f.run(&[b"FT.SEARCH", b"sx", b"delta"]),
21594            "*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"
21595        );
21596        // The fields are what the key holds and not what the schema names, so
21597        // a field nobody indexed comes back too.
21598        f.run(&[b"HSET", b"d:3", b"extra", b"more"]);
21599        assert!(f.run(&[b"FT.SEARCH", b"sx", b"delta"]).contains("extra"));
21600        // `NOCONTENT` leaves the keys on their own, and `LIMIT 0 0` leaves
21601        // the total on its own.
21602        assert_eq!(
21603            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
21604            "*2\r\n:1\r\n$3\r\nd:3\r\n"
21605        );
21606        assert_eq!(
21607            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
21608            "*1\r\n:3\r\n"
21609        );
21610    }
21611
21612    /// The window is ten rows when nobody said, and the cap is on how wide it
21613    /// is rather than on where it starts.
21614    #[test]
21615    fn the_window_is_ten_rows_and_a_million_wide_at_most() {
21616        let mut f = Fixture::new();
21617        corpus(&mut f);
21618        assert_eq!(
21619            f.run(&[
21620                b"FT.SEARCH",
21621                b"sx",
21622                b"alpha",
21623                b"NOCONTENT",
21624                b"LIMIT",
21625                b"1",
21626                b"1"
21627            ]),
21628            "*2\r\n:3\r\n$3\r\nd:2\r\n"
21629        );
21630        assert_eq!(
21631            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0"]),
21632            "-SEARCH_PARSE_ARGS LIMIT requires two arguments\r\n"
21633        );
21634        assert_eq!(
21635            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"-1"]),
21636            "-SEARCH_PARSE_ARGS LIMIT needs two numeric arguments\r\n"
21637        );
21638        assert_eq!(
21639            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"0", b"1000001"]),
21640            "-SEARCH_LIMIT_OVER LIMIT exceeds maximum of 1000000\r\n"
21641        );
21642        assert_eq!(
21643            f.run(&[
21644                b"FT.SEARCH",
21645                b"sx",
21646                b"alpha",
21647                b"NOCONTENT",
21648                b"LIMIT",
21649                b"999999",
21650                b"1000000"
21651            ]),
21652            "*1\r\n:3\r\n"
21653        );
21654    }
21655
21656    /// `RETURN 0` reads on the wire like `NOCONTENT` and is not the same
21657    /// thing, because a later `RETURN` puts the fields back and a later
21658    /// `RETURN` after a `NOCONTENT` does not.
21659    #[test]
21660    fn a_return_of_nothing_is_not_the_same_as_nocontent() {
21661        let mut f = Fixture::new();
21662        corpus(&mut f);
21663        let bare = "*2\r\n:1\r\n$3\r\nd:3\r\n";
21664        assert_eq!(
21665            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"0"]),
21666            bare
21667        );
21668        assert_eq!(
21669            f.run(&[
21670                b"FT.SEARCH",
21671                b"sx",
21672                b"delta",
21673                b"NOCONTENT",
21674                b"RETURN",
21675                b"1",
21676                b"t"
21677            ]),
21678            bare
21679        );
21680        assert_eq!(
21681            f.run(&[
21682                b"FT.SEARCH",
21683                b"sx",
21684                b"delta",
21685                b"RETURN",
21686                b"0",
21687                b"RETURN",
21688                b"1",
21689                b"t"
21690            ]),
21691            "*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"
21692        );
21693    }
21694
21695    /// The count after `RETURN` counts words and not fields, so the `AS` and
21696    /// the name after it are two of them.
21697    #[test]
21698    fn the_count_after_return_counts_words() {
21699        let mut f = Fixture::new();
21700        corpus(&mut f);
21701        // Two words is one renamed field, and the name is the one it comes
21702        // back under.
21703        assert_eq!(
21704            f.run(&[
21705                b"FT.SEARCH",
21706                b"sx",
21707                b"delta",
21708                b"RETURN",
21709                b"3",
21710                b"t",
21711                b"AS",
21712                b"x"
21713            ]),
21714            "*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"
21715        );
21716        // A count that stops on the `AS` has nothing to rename to, and one
21717        // that reaches past the last word is short an argument.
21718        assert_eq!(
21719            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"2", b"t", b"AS"]),
21720            "-SEARCH_PARSE_ARGS RETURN path AS name - must be accompanied with NAME\r\n"
21721        );
21722        assert_eq!(
21723            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"3", b"t", b"AS"]),
21724            "-SEARCH_PARSE_ARGS Bad arguments for RETURN: Expected an argument, but none provided\r\n"
21725        );
21726        // A count that stops before the `AS` asks for a field called `AS`,
21727        // which no key holds, and a field the key does not hold is left out
21728        // rather than sent empty.
21729        assert_eq!(
21730            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"AS"]),
21731            "*3\r\n:1\r\n$3\r\nd:3\r\n*0\r\n"
21732        );
21733    }
21734
21735    /// A `FILTER` is a numeric range written outside the query, and it is only
21736    /// the wrong way round on a field the schema holds as a number.
21737    #[test]
21738    fn a_filter_is_a_range_written_outside_the_query() {
21739        let mut f = Fixture::new();
21740        corpus(&mut f);
21741        assert_eq!(
21742            f.run(&[
21743                b"FT.SEARCH",
21744                b"sx",
21745                b"alpha",
21746                b"NOCONTENT",
21747                b"FILTER",
21748                b"n",
21749                b"2",
21750                b"4"
21751            ]),
21752            "*3\r\n:2\r\n$3\r\nd:2\r\n$3\r\nd:4\r\n"
21753        );
21754        assert_eq!(
21755            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2"]),
21756            "-SEARCH_PARSE_ARGS FILTER requires 3 arguments\r\n"
21757        );
21758        assert_eq!(
21759            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"x", b"1"]),
21760            "-SEARCH_PARSE_ARGS Bad lower range: x\r\n"
21761        );
21762        assert_eq!(
21763            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", b"n", b"2", b"1"]),
21764            "-SEARCH_SYNTAX Invalid numeric range (min > max): @n:[2.000000 1.000000]\r\n"
21765        );
21766        // The same range on a field that is not a number at all, and on a
21767        // field that is not there, answers nothing rather than refusing.
21768        for field in [b"g".as_slice(), b"nope"] {
21769            assert_eq!(
21770                f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"FILTER", field, b"2", b"1"]),
21771                "*1\r\n:0\r\n"
21772            );
21773        }
21774    }
21775
21776    /// The index is resolved before the arguments after it are read, so a name
21777    /// that is not there answers about the name whatever else is wrong.
21778    #[test]
21779    fn the_index_is_found_before_the_arguments_are_read() {
21780        let mut f = Fixture::new();
21781        corpus(&mut f);
21782        let missing = "-SEARCH_INDEX_NOT_FOUND Index not found: nope\r\n";
21783        assert_eq!(f.run(&[b"FT.SEARCH", b"nope", b"alpha", b"BOGUS"]), missing);
21784        assert_eq!(
21785            f.run(&[b"FT.EXPLAIN", b"nope", b"alpha", b"BOGUS"]),
21786            missing
21787        );
21788        // And the arguments are read before the query is, so a query that
21789        // will not parse still answers about the argument.
21790        assert_eq!(
21791            f.run(&[b"FT.SEARCH", b"sx", b"@@@", b"BOGUS"]),
21792            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `BOGUS` at position 1 for <main>\r\n"
21793        );
21794    }
21795
21796    /// `INKEYS` filters the answer before the total is taken, which is not
21797    /// where a client would guess it happens.
21798    #[test]
21799    fn inkeys_comes_off_the_total() {
21800        let mut f = Fixture::new();
21801        corpus(&mut f);
21802        assert_eq!(
21803            f.run(&[
21804                b"FT.SEARCH",
21805                b"sx",
21806                b"alpha",
21807                b"NOCONTENT",
21808                b"INKEYS",
21809                b"1",
21810                b"d:1"
21811            ]),
21812            "*2\r\n:1\r\n$3\r\nd:1\r\n"
21813        );
21814        assert_eq!(
21815            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"NOCONTENT", b"INKEYS", b"0"]),
21816            "*1\r\n:0\r\n"
21817        );
21818    }
21819
21820    /// The fields come from the database the session is on, and a row whose
21821    /// key will not load there is dropped from the reply and taken off the
21822    /// total.
21823    ///
21824    /// Measured against a real server, which follows a key on every database
21825    /// and then loads it from one.
21826    #[test]
21827    fn the_fields_are_read_from_the_session_database() {
21828        let mut f = Fixture::new();
21829        corpus(&mut f);
21830        f.run(&[b"SELECT", b"1"]);
21831        f.run(&[b"HSET", b"d:9", b"t", b"delta", b"n", b"9"]);
21832        // Both documents are in the index, and only one of them is in this
21833        // database.
21834        assert_eq!(
21835            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"NOCONTENT"]),
21836            "*3\r\n:2\r\n$3\r\nd:3\r\n$3\r\nd:9\r\n"
21837        );
21838        assert_eq!(
21839            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
21840            "*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"
21841        );
21842    }
21843
21844    /// The deeper protocol answers a map of five rather than an array, with
21845    /// every row a map of its own.
21846    #[test]
21847    fn the_third_protocol_answers_a_map_of_five() {
21848        let mut f = Fixture::new();
21849        corpus(&mut f);
21850        f.out = Out::new(Proto::Resp3);
21851        assert_eq!(
21852            f.run(&[b"FT.SEARCH", b"sx", b"delta", b"RETURN", b"1", b"n"]),
21853            concat!(
21854                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
21855                "%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",
21856                "+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
21857            )
21858        );
21859    }
21860
21861    /// A window of nothing is a client asking for the count on its own, and a
21862    /// window of nothing that starts somewhere else is a contradiction all
21863    /// three commands refuse in the same words.
21864    #[test]
21865    fn a_window_of_nothing_has_to_start_at_the_top() {
21866        let mut f = Fixture::new();
21867        corpus(&mut f);
21868        let refused = "-SEARCH_LIMIT_OVER The `offset` of the LIMIT must be 0 when `num` is 0\r\n";
21869        assert_eq!(
21870            f.run(&[b"FT.SEARCH", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21871            refused
21872        );
21873        assert_eq!(
21874            f.run(&[b"FT.EXPLAIN", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21875            refused
21876        );
21877        assert_eq!(
21878            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"1", b"0"]),
21879            refused
21880        );
21881        assert_eq!(
21882            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LIMIT", b"0", b"0"]),
21883            "*1\r\n:3\r\n"
21884        );
21885    }
21886
21887    /// An aggregation answers a count and then a list of properties for every
21888    /// row, which is empty until something asks for a field.
21889    #[test]
21890    fn an_aggregation_answers_a_count_and_then_the_properties() {
21891        let mut f = Fixture::new();
21892        corpus(&mut f);
21893        assert_eq!(
21894            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha"]),
21895            "*4\r\n:1\r\n*0\r\n*0\r\n*0\r\n"
21896        );
21897        // Every row, and not the ten a search would have cut it down to.
21898        assert_eq!(
21899            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"1", b"@t"]),
21900            concat!(
21901                "*4\r\n:3\r\n*2\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
21902                "*2\r\n$1\r\nt\r\n$11\r\nalpha gamma\r\n",
21903                "*2\r\n$1\r\nt\r\n$16\r\nalpha beta gamma\r\n"
21904            )
21905        );
21906        // Ascending document number, because nothing sorts the answer. The
21907        // second and fourth documents are the ones the window lands on and the
21908        // best scoring one is not among them.
21909        assert_eq!(
21910            f.run(&[
21911                b"FT.AGGREGATE",
21912                b"sx",
21913                b"alpha",
21914                b"LOAD",
21915                b"1",
21916                b"@n",
21917                b"LIMIT",
21918                b"1",
21919                b"2"
21920            ]),
21921            "*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"
21922        );
21923        // A query nothing answers is a count of nothing and no rows at all.
21924        assert_eq!(
21925            f.run(&[b"FT.AGGREGATE", b"sx", b"nope", b"LOAD", b"1", b"@t"]),
21926            "*1\r\n:0\r\n"
21927        );
21928    }
21929
21930    /// `LOAD` counts words rather than fields, names the property after the
21931    /// path unless an `AS` renames it, and reads everything the key holds when
21932    /// it is given a star.
21933    #[test]
21934    fn a_load_counts_words_and_can_rename_what_it_reads() {
21935        let mut f = Fixture::new();
21936        corpus(&mut f);
21937        // Three words, which are the path, the `AS` and the name.
21938        assert_eq!(
21939            f.run(&[
21940                b"FT.AGGREGATE",
21941                b"sx",
21942                b"alpha",
21943                b"LOAD",
21944                b"3",
21945                b"@t",
21946                b"AS",
21947                b"text"
21948            ]),
21949            concat!(
21950                "*4\r\n:3\r\n*2\r\n$4\r\ntext\r\n$10\r\nalpha beta\r\n",
21951                "*2\r\n$4\r\ntext\r\n$11\r\nalpha gamma\r\n",
21952                "*2\r\n$4\r\ntext\r\n$16\r\nalpha beta gamma\r\n"
21953            )
21954        );
21955        assert_eq!(
21956            f.run(&[
21957                b"FT.AGGREGATE",
21958                b"sx",
21959                b"alpha",
21960                b"LOAD",
21961                b"*",
21962                b"LIMIT",
21963                b"0",
21964                b"1"
21965            ]),
21966            concat!(
21967                "*2\r\n:3\r\n*6\r\n$1\r\nt\r\n$10\r\nalpha beta\r\n",
21968                "$1\r\ng\r\n$5\r\naa,bb\r\n$1\r\nn\r\n$1\r\n1\r\n"
21969            )
21970        );
21971        // A field the key does not hold is left out rather than sent empty.
21972        assert_eq!(
21973            f.run(&[
21974                b"FT.AGGREGATE",
21975                b"sx",
21976                b"alpha",
21977                b"LOAD",
21978                b"2",
21979                b"@n",
21980                b"@nope",
21981                b"LIMIT",
21982                b"0",
21983                b"2"
21984            ]),
21985            "*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"
21986        );
21987    }
21988
21989    /// The `LOAD` grammar, which has four ways to go wrong and one of them is
21990    /// only reported once the rest of the argument list has read cleanly.
21991    #[test]
21992    fn a_load_refuses_a_count_it_cannot_use() {
21993        let mut f = Fixture::new();
21994        corpus(&mut f);
21995        let head = "-SEARCH_PARSE_ARGS Bad arguments for LOAD: ";
21996        assert_eq!(
21997            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"x"]),
21998            format!("{head}Expected number of fields or `*`\r\n")
21999        );
22000        assert_eq!(
22001            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"-1", b"@t"]),
22002            format!("{head}Value is outside acceptable bounds\r\n")
22003        );
22004        assert_eq!(
22005            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD", b"5", b"@t"]),
22006            format!("{head}Expected an argument, but none provided\r\n")
22007        );
22008        assert_eq!(
22009            f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", b"LOAD"]),
22010            format!("{head}Expected an argument, but none provided\r\n")
22011        );
22012        // A count that runs out on the `AS` is held back, because the word
22013        // after it is read as an argument of its own and may be worth an error
22014        // of its own. Nothing follows here, so the held back line is the one.
22015        assert_eq!(
22016            f.run(&[
22017                b"FT.AGGREGATE",
22018                b"sx",
22019                b"alpha",
22020                b"LOAD",
22021                b"2",
22022                b"@t",
22023                b"AS"
22024            ]),
22025            "-SEARCH_PARSE_ARGS LOAD path AS name - must be accompanied with NAME\r\n"
22026        );
22027        // And here the word after it is one an aggregation stops taking once a
22028        // step has been read, so that is what the client hears about.
22029        assert_eq!(
22030            f.run(&[
22031                b"FT.AGGREGATE",
22032                b"sx",
22033                b"alpha",
22034                b"LOAD",
22035                b"2",
22036                b"@t",
22037                b"AS",
22038                b"VERBATIM"
22039            ]),
22040            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 5 for <main>\r\n"
22041        );
22042        // A `LOAD 0` is a step that names nothing. It shuts the same door
22043        // without becoming a loader, so the count stays the one a query with no
22044        // `LOAD` gets.
22045        assert_eq!(
22046            f.run(&[
22047                b"FT.AGGREGATE",
22048                b"sx",
22049                b"alpha",
22050                b"LOAD",
22051                b"0",
22052                b"LIMIT",
22053                b"0",
22054                b"1"
22055            ]),
22056            "*2\r\n:1\r\n*0\r\n"
22057        );
22058    }
22059
22060    /// Reading a step of the pipeline stops the words about the search itself
22061    /// being taken, and `LIMIT` and `TIMEOUT` are not steps.
22062    #[test]
22063    fn a_pipeline_step_closes_the_door_on_the_search_words() {
22064        let mut f = Fixture::new();
22065        corpus(&mut f);
22066        assert_eq!(
22067            f.run(&[
22068                b"FT.AGGREGATE",
22069                b"sx",
22070                b"alpha",
22071                b"LOAD",
22072                b"1",
22073                b"@t",
22074                b"VERBATIM"
22075            ]),
22076            "-SEARCH_ARG_UNRECOGNIZED Unknown argument `VERBATIM` at position 4 for <main>\r\n"
22077        );
22078        assert_eq!(
22079            f.run(&[
22080                b"FT.AGGREGATE",
22081                b"sx",
22082                b"alpha",
22083                b"LIMIT",
22084                b"0",
22085                b"1",
22086                b"VERBATIM"
22087            ]),
22088            "*2\r\n:1\r\n*0\r\n"
22089        );
22090        // Three words a search takes that this command names in its refusal
22091        // rather than calling them unknown.
22092        for word in [b"RETURN".as_slice(), b"SUMMARIZE", b"HIGHLIGHT"] {
22093            let name = core::str::from_utf8(word).expect("the three words are text");
22094            assert_eq!(
22095                f.run(&[b"FT.AGGREGATE", b"sx", b"alpha", word]),
22096                format!("-SEARCH_PARSE_ARGS {name} is not supported on FT.AGGREGATE\r\n")
22097            );
22098        }
22099    }
22100
22101    /// `ADDSCORES` writes the score as a property to twelve significant digits
22102    /// where `WITHSCORES` writes it beside the row in full.
22103    #[test]
22104    fn addscores_writes_a_shorter_score_than_withscores() {
22105        let mut f = Fixture::new();
22106        corpus(&mut f);
22107        assert_eq!(
22108            f.run(&[
22109                b"FT.AGGREGATE",
22110                b"sx",
22111                b"alpha",
22112                b"ADDSCORES",
22113                b"LOAD",
22114                b"1",
22115                b"@n",
22116                b"LIMIT",
22117                b"0",
22118                b"2"
22119            ]),
22120            concat!(
22121                "*3\r\n:3\r\n",
22122                "*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",
22123                "*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"
22124            )
22125        );
22126        // `NOCONTENT` takes the properties away and leaves whatever was asked
22127        // for beside them, and a sort key is always null because nothing sorts
22128        // by one yet.
22129        assert_eq!(
22130            f.run(&[
22131                b"FT.AGGREGATE",
22132                b"sx",
22133                b"alpha",
22134                b"NOCONTENT",
22135                b"WITHSCORES",
22136                b"LIMIT",
22137                b"0",
22138                b"2"
22139            ]),
22140            "*3\r\n:1\r\n$18\r\n0.3566749439387324\r\n$18\r\n0.3566749439387324\r\n"
22141        );
22142        assert_eq!(
22143            f.run(&[
22144                b"FT.AGGREGATE",
22145                b"sx",
22146                b"alpha",
22147                b"WITHSORTKEYS",
22148                b"LOAD",
22149                b"1",
22150                b"@n",
22151                b"LIMIT",
22152                b"0",
22153                b"1"
22154            ]),
22155            "*3\r\n:3\r\n$-1\r\n*2\r\n$1\r\nn\r\n$1\r\n1\r\n"
22156        );
22157    }
22158
22159    /// The one scorer that has to see the whole answer first turns the count
22160    /// into the real total and hands the rows back backwards.
22161    #[test]
22162    fn a_normalising_scorer_answers_the_rows_backwards() {
22163        let mut f = Fixture::new();
22164        corpus(&mut f);
22165        assert_eq!(
22166            f.run(&[
22167                b"FT.AGGREGATE",
22168                b"sx",
22169                b"alpha",
22170                b"SCORER",
22171                b"BM25STD.NORM",
22172                b"ADDSCORES",
22173                b"LOAD",
22174                b"1",
22175                b"@n",
22176                b"LIMIT",
22177                b"1",
22178                b"2"
22179            ]),
22180            concat!(
22181                "*3\r\n:3\r\n",
22182                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n2\r\n",
22183                "*4\r\n$7\r\n__score\r\n$1\r\n1\r\n$1\r\nn\r\n$1\r\n1\r\n"
22184            )
22185        );
22186        // Without `ADDSCORES` nothing on the row needs the score, so the rows
22187        // come back the way every other query answers them.
22188        assert_eq!(
22189            f.run(&[
22190                b"FT.AGGREGATE",
22191                b"sx",
22192                b"alpha",
22193                b"SCORER",
22194                b"BM25STD.NORM",
22195                b"LOAD",
22196                b"1",
22197                b"@n",
22198                b"LIMIT",
22199                b"1",
22200                b"2"
22201            ]),
22202            "*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"
22203        );
22204    }
22205
22206    /// The deeper protocol answers the same map of five a search answers, with
22207    /// the `id` gone because an aggregation is about the properties.
22208    #[test]
22209    fn an_aggregation_answers_a_map_of_five_as_well() {
22210        let mut f = Fixture::new();
22211        corpus(&mut f);
22212        f.out = Out::new(Proto::Resp3);
22213        assert_eq!(
22214            f.run(&[
22215                b"FT.AGGREGATE",
22216                b"sx",
22217                b"alpha",
22218                b"ADDSCORES",
22219                b"WITHSCORES",
22220                b"WITHSORTKEYS",
22221                b"LOAD",
22222                b"1",
22223                b"@n",
22224                b"LIMIT",
22225                b"0",
22226                b"1"
22227            ]),
22228            concat!(
22229                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
22230                "%4\r\n+score\r\n,0.3566749439387324\r\n+sortkey\r\n_\r\n",
22231                "+extra_attributes\r\n%2\r\n$7\r\n__score\r\n$14\r\n0.356674943939\r\n",
22232                "$1\r\nn\r\n$1\r\n1\r\n+values\r\n*0\r\n",
22233                "+total_results\r\n:3\r\n+warning\r\n*0\r\n"
22234            )
22235        );
22236        // The count is worked out from the rows the reply reached under this
22237        // protocol, where under RESP2 it is worked out from the first of them.
22238        assert_eq!(
22239            f.run(&[
22240                b"FT.AGGREGATE",
22241                b"sx",
22242                b"alpha",
22243                b"NOCONTENT",
22244                b"LIMIT",
22245                b"0",
22246                b"1"
22247            ]),
22248            concat!(
22249                "%5\r\n+attributes\r\n*0\r\n+format\r\n+STRING\r\n+results\r\n*1\r\n",
22250                "%1\r\n+values\r\n*0\r\n+total_results\r\n:1\r\n+warning\r\n*0\r\n"
22251            )
22252        );
22253    }
22254}