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 json;
69mod keyspace;
70mod lists;
71mod migrate;
72mod scan;
73mod scripting;
74mod search;
75mod server;
76mod sets;
77mod streams;
78mod strings;
79pub mod table;
80mod tdigest;
81mod topk;
82mod ts;
83mod vectors;
84mod vfilter;
85mod zsets;
86
87pub use args::Args;
88pub use blocking::{Parked, Waiters};
89pub use server::parse_memory;
90pub use table::{COMMANDS, Spec, arity_ok, lookup};
91
92use crate::reply::Out;
93use std::path::{Path, PathBuf};
94use yo_common::{Code, Error};
95use yo_kv::cold::Blocks;
96use yo_kv::{Clock, Db, Keyspace};
97use yo_search::Registry;
98
99/// How many databases a server has.
100///
101/// Redis's default is sixteen and its `databases` setting can change it. Ours
102/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
103/// constant. Nothing in the design needs the number to be fixed; nothing yet
104/// needs it not to be.
105pub const DATABASES: usize = 16;
106
107/// Every database's bit in [`Server::dirty`], which is what a fresh server
108/// starts on so that the first maintenance turn asks all of them.
109///
110/// A `u64` holds sixteen bits with room to spare, and the assertion below is
111/// what turns raising [`DATABASES`] past sixty four into a build failure rather
112/// than a shift that silently drops the databases past the end.
113const ALL_DATABASES: u64 = if DATABASES == 64 {
114    u64::MAX
115} else {
116    (1u64 << DATABASES) - 1
117};
118const _: () = assert!(DATABASES <= 64);
119
120/// How many keys one command throws away before it leaves the rest to the next.
121///
122/// A bound and not a loop to the end, because this runs in front of a client
123/// that is waiting for its reply, and a server a long way over its limit would
124/// otherwise hold that client for as long as it took to walk all the way back
125/// under. Sixty four is a batch's worth of commands, so a server that went over
126/// by what one batch allocated comes back under in one command, and a server
127/// whose limit was just cut in half works through it over the next few thousand
128/// rather than in one long stall. Redis bounds the same loop by a time slice
129/// instead of a count and hands the rest to a timer; there is no timer here, so
130/// the rest goes to the next command that runs.
131const EVICT_BUDGET: usize = 64;
132
133/// What a server says to a command that would allocate when it has no room.
134///
135/// Redis's `shared.oomerr`, word for word including the full stop, because
136/// clients match on the `OOM` prefix and people match on the sentence.
137const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
138
139/// What the connection should do after a command.
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum Flow {
142    /// Read the next command.
143    Continue,
144    /// Write what is buffered and then close, which is what `QUIT` asks for.
145    Close,
146    /// Nothing was written and nothing is owed yet.
147    ///
148    /// The client is on the waiter list and its reply comes when a key it named
149    /// has something in it or when its deadline passes, whichever happens first.
150    /// Until then the connection stops reading commands, because a client that
151    /// is waiting for an answer is not a client that has sent another question.
152    Block,
153}
154
155/// The numbers `INFO` reports that this layer cannot see for itself.
156///
157/// The reactor owns the sockets, so the reactor is what knows how many clients
158/// there are. It writes these directly and nothing here does anything with them
159/// except report them.
160#[derive(Debug, Clone, Copy, Default)]
161pub struct Stats {
162    /// Connections open right now.
163    pub clients: u64,
164    /// Connections accepted since the server started.
165    pub connections: u64,
166    /// Commands run since the server started, which this layer counts itself.
167    pub commands: u64,
168}
169
170/// Where the process was started, which is what `dir` defaults to.
171///
172/// A dot if the working directory cannot be read, which happens when it has
173/// been deleted out from under a running process. That is not a reason to
174/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
175/// from the filesystem if anybody asks for one.
176fn working_dir() -> PathBuf {
177    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
178}
179
180/// One command's counters, for `INFO commandstats`.
181///
182/// Three of Redis's five. `usec` and `usec_per_call` are not here because
183/// nothing times a command, and timing one means two clock reads around a call
184/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
185/// has room for it; this does not, and a zero under a name that says microseconds
186/// is worse than an absent field, which is the same rule the rest of `INFO`
187/// follows.
188#[derive(Debug, Clone, Copy, Default)]
189pub struct CommandStat {
190    /// Times the command ran, whatever it answered.
191    pub calls: u64,
192    /// Times it was turned away before it ran, which is the wrong number of
193    /// arguments or no room under `maxmemory`.
194    pub rejected: u64,
195    /// Times it ran and answered with an error.
196    pub failed: u64,
197}
198
199impl CommandStat {
200    /// Whether this command has ever been seen.
201    ///
202    /// A row that has not is left out of the reply, which is what Redis does and
203    /// is why the section is a handful of lines on a working server rather than
204    /// one line per command in the table.
205    const fn seen(&self) -> bool {
206        self.calls != 0 || self.rejected != 0 || self.failed != 0
207    }
208}
209
210/// A counter per command, indexed the way [`table::index_of`] says.
211///
212/// A flat array and not a map, because the dispatcher is already holding the
213/// spec and the spec's position in the table is two addresses subtracted. That
214/// makes the counting a load, an add and a store on a row the previous command
215/// of the same name has already pulled into cache.
216struct CommandStats(Box<[CommandStat]>);
217
218impl Default for CommandStats {
219    fn default() -> CommandStats {
220        CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
221    }
222}
223
224impl CommandStats {
225    /// The row for one command.
226    fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
227        &mut self.0[table::index_of(spec)]
228    }
229}
230
231/// Where a database gets its store from, asked by database number.
232///
233/// `None` means that database cannot have one. The caller owns whatever the
234/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
235/// database, and this crate never learns what any of that is.
236pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
237
238/// Everything a server holds.
239///
240/// One of these per shard thread, not one per process: the databases inside are
241/// not `Sync` and are reached by sending their thread a command. What makes
242/// this a server rather than a shard is that it is the whole of what a
243/// connection can address.
244pub struct Server {
245    dbs: Vec<Db>,
246    /// How many stripes each database is cut into, the same for all of them.
247    ///
248    /// Kept here as well as in each database so that the flat slot arithmetic
249    /// below is a multiply and a divide against a field on the server rather
250    /// than a walk asking each database how wide it is.
251    width: usize,
252    clock: Clock,
253    started_ms: u64,
254    /// Where the next maintenance turn starts looking, so that a database
255    /// under constant write load cannot hold the other fifteen's space.
256    next_db: usize,
257    /// One bit per database, set when a command ran against it.
258    ///
259    /// The maintenance turn after every batch used to ask all sixteen
260    /// databases whether they had anything to collect, and asking costs a load
261    /// and a store in each one. Fifteen of those are cold lines on a server
262    /// where every client is on database zero, which is every server, and the
263    /// answer is no every time. This is the cheap half of the question: a
264    /// database nobody has touched since it last said no cannot have started
265    /// saying yes.
266    dirty: u64,
267    /// What the connections are holding, kept by the engine.
268    conn_bytes: usize,
269    /// The `maxmemory` limit in bytes, zero when there is not one.
270    ///
271    /// Zero is the default and it is the whole reason the check in front of
272    /// every write is one comparison against a field that is already warm.
273    maxmemory: u64,
274    /// Where a database gets a store from the first time it needs one.
275    ///
276    /// A closure and not a store, because there are sixteen databases and a
277    /// server that fills memory on database zero should not have opened
278    /// anything for the other fifteen. Nothing is asked of this until a memory
279    /// limit is actually reached, so a server that never fills memory never
280    /// opens a file, and a server that has no file never has one of these.
281    ///
282    /// `None` from the closure means that database cannot have one, which is
283    /// how the caller says the file it opened has no more room for logs.
284    store: Option<Box<StoreSource>>,
285    /// The `maxstore` limit in bytes, `None` when there is not one.
286    ///
287    /// The storage limit, and the other half of the inversion `14` section 4.1
288    /// describes. `maxmemory` is a limit on memory and the right answer to a
289    /// memory limit on a system with a file under it is to move data to the
290    /// file, not to delete it. Deleting is the right answer to a limit on the
291    /// file, and this is that limit.
292    ///
293    /// Zero is not "no limit" here, which is the one place this reads
294    /// differently from `maxmemory` and is the difference that makes a drop in
295    /// cache possible. A storage budget of zero bytes means nothing may live on
296    /// the file, so migration cannot make room and eviction is the only thing
297    /// left, which is Redis exactly. `None` is no limit and is the default,
298    /// which with `noeviction` means the database grows until the disk is full
299    /// and then writes fail, which is what a database does.
300    maxstore: Option<u64>,
301    /// What [`Server::memory_bytes`] said at the last maintenance turn.
302    ///
303    /// The reading is a walk over every collection in every database and cannot
304    /// go on a command path, so the command path reads this instead and is at
305    /// most one batch behind. What that costs is overshoot: a server can end a
306    /// batch holding one batch's worth of allocation more than its limit before
307    /// anything notices. A batch is 64 commands, so that is bounded by what 64
308    /// commands can allocate and not by how long the server runs.
309    ///
310    /// Only kept up to date when there is a limit to judge it against. A server
311    /// with no `maxmemory` never reads it and never pays for it.
312    used: usize,
313    /// Which database the next eviction draws from.
314    ///
315    /// Its own cursor and not [`Server::next_db`], because eviction and
316    /// compaction move at different rates and sharing one would make the
317    /// database that gets compacted depend on how many keys were evicted.
318    evict_db: usize,
319    /// Which database the next active expiry sweep starts at.
320    ///
321    /// A third cursor for the same reason there is a second one. A sweep runs on
322    /// every turn of the loop and compaction runs when there is dead space, so
323    /// sharing a cursor would make which database gets swept depend on which one
324    /// was last collected.
325    expire_db: usize,
326    /// The millisecond the last active expiry sweep ran on, so the next one on
327    /// the same millisecond does not bother.
328    expire_ms: u64,
329    /// Clients parked on a blocking command.
330    waiters: Waiters,
331    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
332    ///
333    /// Empty on a server nobody has migrated a key out of, which is nearly all
334    /// of them, and it costs a vector's three words to be empty.
335    peers: migrate::Peers,
336    /// The numbers the reactor keeps for `INFO`.
337    pub stats: Stats,
338    /// A counter per command, for `INFO commandstats`.
339    cmdstats: CommandStats,
340    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
341    ///
342    /// Absolute, and resolved once when the server is built rather than every
343    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
344    /// entitled to hand one of them to a copy tool, so a relative path that
345    /// meant something different after a `chdir` would be a path that stops
346    /// working for reasons nobody could see.
347    dir: PathBuf,
348    /// What backup is running, if one is.
349    ///
350    /// On the server and not on a session, because a backup outlives the
351    /// connection that asked for it and any other connection can seal it.
352    backup: backup::State,
353    /// The search indexes and the names pointing at them.
354    ///
355    /// On the server and not on a database, which is the one collection in this
356    /// build that is. A real server keeps its indexes in the search module, the
357    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
358    /// indexes made on database zero. `search.rs` has the rest of why.
359    ///
360    /// A server nobody has made an index on holds two empty vectors here, which
361    /// is six words and no allocation.
362    search: Registry,
363    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
364    ///
365    /// A flag rather than an exit, because the command layer is not what owns
366    /// the process. It runs inside a batch that has other commands behind it
367    /// and inside a driver that has a socket file to take away and a file to
368    /// close, and a server that calls `exit` from a command handler skips all
369    /// of that. So the command says stop and the driver stops, on the same turn
370    /// and through the same door a signal uses.
371    stopping: bool,
372}
373
374impl Server {
375    /// A server with [`DATABASES`] empty databases on the system clock.
376    #[must_use]
377    pub fn new() -> Server {
378        let clock = Clock::system();
379        Server {
380            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
381            width: 1,
382            clock,
383            started_ms: clock.now_ms(),
384            next_db: 0,
385            dirty: ALL_DATABASES,
386            conn_bytes: 0,
387            maxmemory: 0,
388            store: None,
389            maxstore: None,
390            used: 0,
391            evict_db: 0,
392            expire_db: 0,
393            expire_ms: 0,
394            waiters: Waiters::default(),
395            peers: migrate::Peers::default(),
396            stats: Stats::default(),
397            cmdstats: CommandStats::default(),
398            dir: working_dir(),
399            backup: backup::State::default(),
400            search: Registry::new(),
401            stopping: false,
402        }
403    }
404
405    /// A server whose databases are cut into `width` stripes each.
406    ///
407    /// Not reachable from the command line yet. Every command group answers on
408    /// a server of any width now and so does everything that walks a whole
409    /// database, and the tests run each group at a width of one and a width of
410    /// eight and check the two agree.
411    ///
412    /// What is left before this is what `--threads` sets is the engine. A
413    /// database being several objects is what makes more than one thread
414    /// possible, and it is not what makes more than one thread happen.
415    #[must_use]
416    pub fn with_width(width: usize) -> Server {
417        let clock = Clock::system();
418        let mut server = Server::new();
419        server.dbs = (0..DATABASES)
420            .map(|_| Db::with_clock(clock, width))
421            .collect();
422        server.width = server.dbs[0].width();
423        server
424    }
425
426    /// A server on a clock the caller moves by hand, for tests.
427    #[must_use]
428    pub fn with_clock(clock: Clock) -> Server {
429        Server {
430            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
431            width: 1,
432            clock,
433            started_ms: clock.now_ms(),
434            next_db: 0,
435            dirty: ALL_DATABASES,
436            conn_bytes: 0,
437            maxmemory: 0,
438            store: None,
439            maxstore: None,
440            used: 0,
441            evict_db: 0,
442            expire_db: 0,
443            expire_ms: 0,
444            waiters: Waiters::default(),
445            peers: migrate::Peers::default(),
446            stats: Stats::default(),
447            cmdstats: CommandStats::default(),
448            dir: working_dir(),
449            backup: backup::State::default(),
450            search: Registry::new(),
451            stopping: false,
452        }
453    }
454
455    /// One database, by index.
456    ///
457    /// A caller that knows which key it wants names the one stripe the key is
458    /// on rather than working over the whole thing, which is what `at` and its
459    /// neighbours on [`Db`] are for. A caller that is about a database rather
460    /// than about a key, which is the snapshot walk and a setting, works over
461    /// all of them.
462    ///
463    /// The borrow is mutable, so the database is marked as having had something
464    /// run against it. Anything that only reads has [`Server::striped_ref`] and
465    /// does not come through here.
466    ///
467    /// # Panics
468    ///
469    /// If `i` is not a database. `SELECT` is the only way a client changes the
470    /// index and it checks, so an index that is out of range here is a bug in
471    /// the caller and not something a client can ask for.
472    pub fn striped(&mut self, i: usize) -> &mut Db {
473        self.dirty |= 1u64 << i;
474        &mut self.dbs[i]
475    }
476
477    /// Every keyspace on the server, which is every stripe of every database.
478    ///
479    /// What the aggregates walk. A total over the whole server is a total over
480    /// all of these and the stripe boundaries do not appear in it, which is
481    /// what makes the numbers `INFO` reports the same numbers whatever the
482    /// server was cut into.
483    fn keyspaces(&self) -> impl Iterator<Item = &Keyspace> {
484        self.dbs.iter().flat_map(Db::stripes)
485    }
486
487    /// The same, mutably.
488    fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
489        self.dbs.iter_mut().flat_map(Db::stripes_mut)
490    }
491
492    /// How many keyspaces there are, counting every stripe of every database.
493    ///
494    /// The maintenance turns walk these rather than the databases, because a
495    /// stripe is the thing that holds an arena and a deadline heap and so it is
496    /// the thing that has anything to collect.
497    const fn slots(&self) -> usize {
498        DATABASES * self.width
499    }
500
501    /// Which database slot `i` belongs to.
502    const fn slot_db(&self, i: usize) -> usize {
503        i / self.width
504    }
505
506    /// Keyspace `i` of [`Server::slots`].
507    fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
508        let (db, stripe) = (i / self.width, i % self.width);
509        self.dbs[db].stripe_mut(stripe)
510    }
511
512    /// The same, without taking it mutably.
513    fn slot(&self, i: usize) -> &Keyspace {
514        let (db, stripe) = (i / self.width, i % self.width);
515        self.dbs[db].stripe(stripe)
516    }
517
518    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
519    #[must_use]
520    pub fn dir(&self) -> &Path {
521        &self.dir
522    }
523
524    /// Point the server at a different directory, which `yodb serve --dir` does.
525    ///
526    /// Only before it is serving. There is no `CONFIG SET dir` here and there
527    /// is none on a real server either without turning protected configs on,
528    /// for the good reason that moving it out from under a running backup would
529    /// leave files nothing can find again.
530    pub fn set_dir(&mut self, dir: PathBuf) {
531        self.dir = dir;
532    }
533
534    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
535    ///
536    /// Once per batch, from the same maintenance turn that collects the arena.
537    /// It reads two fields and returns on a server that has never taken a
538    /// backup, which is nearly all of them.
539    pub fn backup_expire(&mut self) {
540        backup::expire(self);
541    }
542
543    /// Ask for the server to stop, which is what `SHUTDOWN` does.
544    ///
545    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
546    /// or ends the process, because none of those belong to this layer, and a
547    /// batch that is halfway through still has to finish and be written out.
548    pub fn stop(&mut self) {
549        self.stopping = true;
550    }
551
552    /// Whether somebody has asked the server to stop.
553    ///
554    /// Read once per turn by the loop, next to the flag a signal sets. The two
555    /// mean the same thing and are separate only because one arrives from the
556    /// operating system and the other from a client.
557    #[must_use]
558    pub fn stopping(&self) -> bool {
559        self.stopping
560    }
561
562    /// One database, by index, without taking it mutably.
563    ///
564    /// What the prefetch stage needs. It runs for all 64 commands in a batch
565    /// before any of them executes, so it cannot hold the mutable borrow `run`
566    /// is about to want, and it does not need one: warming a cache line reads
567    /// nothing and changes nothing.
568    #[must_use]
569    pub fn striped_ref(&self, i: usize) -> &Db {
570        &self.dbs[i]
571    }
572
573    /// The stripe that answers for a database when a setting is read back.
574    ///
575    /// A ladder setting and an eviction policy are one number on a real server,
576    /// and the fact that every stripe of every database carries a copy of it is
577    /// ours rather than the client's problem. A write puts the same value on
578    /// every one of them, so any stripe answers for all of them and this is the
579    /// first one.
580    fn settings(&self) -> &Keyspace {
581        self.dbs[0].stripe(0)
582    }
583
584    /// Take a new clock reading and give it to every database.
585    ///
586    /// Once per turn of the event loop, which is the only place time moves. A
587    /// command asking what the time is gets the answer the whole batch got, so
588    /// two keys written by the same batch expire together (`04` section 3).
589    pub fn refresh_clock(&mut self) {
590        self.clock.refresh();
591        let now = self.clock.now_ms();
592        for db in &mut self.dbs {
593            db.set_clock_ms(now);
594        }
595    }
596
597    /// Move every clock here on by `ms`, for tests about expiry.
598    ///
599    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
600    /// except that it moves from wherever the clock is rather than to a stated
601    /// moment, which is what a test that wants a key to have expired asks for.
602    pub fn advance_clock_ms(&mut self, ms: u64) {
603        let now = self.clock.now_ms() + ms;
604        self.set_clock_ms(now);
605    }
606
607    /// Move every clock here to `ms` by hand, for tests about expiry.
608    ///
609    /// A test cannot wait a hundred seconds and a test that waits a hundred
610    /// milliseconds is a test that fails on a loaded machine, so time moves on
611    /// request. The system clock underneath will overwrite this on the next
612    /// [`Server::refresh_clock`], which is why this is only useful in a test
613    /// that drives commands directly rather than through the event loop.
614    pub fn set_clock_ms(&mut self, ms: u64) {
615        self.clock.set(ms);
616        for db in &mut self.dbs {
617            db.set_clock_ms(ms);
618        }
619    }
620
621    /// Seconds since this server was built.
622    #[must_use]
623    pub fn uptime_secs(&self) -> u64 {
624        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
625    }
626
627    /// Bytes held by every database's index and arena, plus the read and reply
628    /// buffers of every connection.
629    ///
630    /// The buffers are in here because they are real and because Redis counts
631    /// its own, so leaving them out would make the one number people compare
632    /// flattering rather than true. They are not a database, so nothing in the
633    /// keyspace can change them and the engine has to say when they move.
634    #[must_use]
635    pub fn memory_bytes(&self) -> usize {
636        self.keyspaces().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
637    }
638
639    /// What the keyspace itself is holding, live records only.
640    ///
641    /// `used_memory` minus this is what the store costs to run: the index, the
642    /// space dead records are sitting in until compaction gets to them, and the
643    /// connections' buffers.
644    #[must_use]
645    pub fn dataset_bytes(&self) -> usize {
646        self.keyspaces()
647            .map(|db| db.map().arena().live_bytes() as usize)
648            .sum()
649    }
650
651    /// Bytes the arenas are holding, live and dead together.
652    #[must_use]
653    pub fn arena_bytes(&self) -> usize {
654        self.keyspaces()
655            .map(|db| db.map().arena().reserved_bytes() as usize)
656            .sum()
657    }
658
659    /// Bytes the indexes are holding.
660    #[must_use]
661    pub fn index_bytes(&self) -> usize {
662        self.keyspaces()
663            .map(|db| db.map().index().memory_bytes())
664            .sum()
665    }
666
667    /// What arena compaction has cost, across every database.
668    ///
669    /// The write amplification of value separation, which is invisible from the
670    /// outside otherwise: a client that writes a megabyte can leave the store
671    /// copying several more, and the only sign of it without these is that the
672    /// writes got slower.
673    #[must_use]
674    pub fn compaction(&self) -> yo_kv::Compaction {
675        self.keyspaces().map(|db| db.map().compaction()).fold(
676            yo_kv::Compaction::default(),
677            |a, b| yo_kv::Compaction {
678                walked: a.walked + b.walked,
679                moved: a.moved + b.moved,
680                bytes: a.bytes + b.bytes,
681            },
682        )
683    }
684
685    /// Arena segments whose pages are real, across every database.
686    #[must_use]
687    pub fn segment_count(&self) -> usize {
688        self.keyspaces()
689            .map(|db| db.map().arena().resident_segments())
690            .sum()
691    }
692
693    /// What the connections' read and reply buffers are holding.
694    #[must_use]
695    pub const fn conn_bytes(&self) -> usize {
696        self.conn_bytes
697    }
698
699    /// Note that the connections are holding `delta` bytes more than they were,
700    /// or fewer when it is negative.
701    ///
702    /// A delta and not a total because the alternative is a walk over every
703    /// connection, and the walk would have to happen on a turn of the loop
704    /// rather than when `INFO` asks, which puts the cost of a report on the
705    /// command path of a server nobody is asking.
706    pub fn note_conn_bytes(&mut self, delta: isize) {
707        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
708    }
709
710    /// Keys reclaimed by running into them after their deadline.
711    #[must_use]
712    pub fn expired_keys(&self) -> u64 {
713        self.keyspaces().map(Keyspace::expired_keys).sum()
714    }
715
716    /// Keys thrown away to make room, which is the other number entirely.
717    #[must_use]
718    pub fn evicted_keys(&self) -> u64 {
719        self.keyspaces().map(Keyspace::evicted_keys).sum()
720    }
721
722    /// Every command that has been seen, with its counters.
723    ///
724    /// Only the ones that have. A server reports a handful of lines rather than
725    /// one per command in the table, which is what Redis does and is the
726    /// difference between a section a person can read and one they cannot.
727    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
728        self.cmdstats
729            .0
730            .iter()
731            .enumerate()
732            .filter(|(_, row)| row.seen())
733            .map(|(at, row)| (table::name_at(at), *row))
734    }
735
736    /// The `maxmemory` limit in bytes, zero when there is not one.
737    #[must_use]
738    pub const fn maxmemory(&self) -> u64 {
739        self.maxmemory
740    }
741
742    /// Set the limit, and take a reading straight away.
743    ///
744    /// The reading is here rather than left to the next maintenance turn because
745    /// a client that sets the limit and sends a write in the same batch expects
746    /// the write to be judged against the limit it just set, and because the
747    /// cached number is meaningless until the first time there is a limit to
748    /// compare it with.
749    ///
750    /// Turning the limit on also turns on the running total every slab keeps of
751    /// what its collections hold, and turning it off turns that back off, so a
752    /// server with no limit is not paying to count something nobody reads. The
753    /// first reading after switching it on is the walk that the total starts
754    /// from, and it is the only walk.
755    pub fn set_maxmemory(&mut self, bytes: u64) {
756        self.maxmemory = bytes;
757        for db in &mut self.dbs {
758            db.track_memory(bytes != 0);
759        }
760        self.used = self.settled_memory();
761    }
762
763    /// Say where a database should get its store from when it needs one.
764    ///
765    /// This is what turns the eviction inversion on. Until it is called every
766    /// database answers a memory limit by evicting, which is Redis, and after it
767    /// is called a database under memory pressure moves values to whatever the
768    /// closure hands back instead of throwing keys away.
769    ///
770    /// Called at most once per database and only under pressure, so a server
771    /// that is given a file and never fills memory never touches it.
772    pub fn set_store_source(
773        &mut self,
774        source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
775    ) {
776        self.store = Some(Box::new(source));
777    }
778
779    /// Whether this server has been given somewhere to put cold values.
780    #[must_use]
781    pub const fn has_store_source(&self) -> bool {
782        self.store.is_some()
783    }
784
785    /// Open database `at`'s store, if it has not got one and there is one to be
786    /// had.
787    ///
788    /// A store that will not open leaves the database where it was, which is
789    /// evicting, because a memory limit that cannot be answered by moving data
790    /// still has to be answered.
791    fn attach_store(&mut self, at: usize) {
792        if self.slot(at).store_bytes().is_some() {
793            return;
794        }
795        let Some(source) = self.store.as_mut() else {
796            return;
797        };
798        if let Some(blocks) = source(at) {
799            self.slot_mut(at).attach(blocks);
800        }
801    }
802
803    /// The `maxstore` limit in bytes, `None` when there is not one.
804    #[must_use]
805    pub const fn maxstore(&self) -> Option<u64> {
806        self.maxstore
807    }
808
809    /// Set the storage limit, or clear it with `None`.
810    ///
811    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
812    /// total, because this limit is compared against a number the store keeps
813    /// and answers on demand, not against a walk.
814    pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
815        self.maxstore = bytes;
816    }
817
818    /// What every attached store is holding, for `INFO memory`.
819    ///
820    /// Zero on a server with nothing attached, which is not the same as a server
821    /// whose file is empty, and [`Server::regime`] is the field that tells those
822    /// two apart.
823    #[must_use]
824    pub fn store_bytes(&self) -> u64 {
825        self.keyspaces().filter_map(Keyspace::store_bytes).sum()
826    }
827
828    /// What the file has been asked to do, added up over every database.
829    ///
830    /// Counters and not levels, so they only ever go up and a run is the
831    /// difference between two readings. G9 is a ratio over these: the faults a
832    /// run took, divided by the point reads it issued, has to come out at 1.05
833    /// or less with a working set ten times memory. There is no way to work that
834    /// out from outside the server, so it is reported rather than inferred.
835    ///
836    /// A fault is a read that went to the store. Whether it also went to the
837    /// device depends on the store: a log serves a read out of a resident page
838    /// without touching anything. At ten times memory almost every fault is a
839    /// real read, which is why the gate is written against this number, but the
840    /// two are not the same thing and a run tight against the bar should be
841    /// checked against what the operating system says.
842    #[must_use]
843    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
844        let mut total = yo_kv::tier::Stats::default();
845        for db in self.keyspaces() {
846            let Some(tier) = db.tier() else { continue };
847            let s = tier.stats();
848            total.demoted += s.demoted;
849            total.promoted += s.promoted;
850            total.faults += s.faults;
851            total.served += s.served;
852            total.bytes_out += s.bytes_out;
853            total.bytes_in += s.bytes_in;
854        }
855        total
856    }
857
858    /// Which way this server answers a memory limit, in one word for `INFO`.
859    ///
860    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
861    /// inversion: a memory limit moves values to the file and nothing stored is
862    /// lost. A server reports one word rather than leaving an operator to work
863    /// it out from a limit, a setting and whether a file happens to be open.
864    #[must_use]
865    pub fn regime(&self) -> &'static str {
866        if (0..self.slots()).any(|at| self.migrates(at)) {
867            "migrate"
868        } else {
869            "evict"
870        }
871    }
872
873    /// Whether database `at` answers a memory limit by moving values to the
874    /// file rather than by throwing keys away.
875    ///
876    /// Three things have to hold. There has to be somewhere to move them, which
877    /// is a store attached to that database or a source that can open one, and
878    /// on a server that was never given a file this is false everywhere and
879    /// every database behaves exactly as it did.
880    /// The storage budget has to be more than nothing, which is what
881    /// `maxstore 0` says it is not. And the file has to be under that budget,
882    /// because a full file is a storage limit reached and eviction is the right
883    /// answer to a storage limit.
884    fn migrates(&self, at: usize) -> bool {
885        if self.maxstore == Some(0) {
886            return false;
887        }
888        match self.slot(at).store_bytes() {
889            Some(held) => self.maxstore.is_none_or(|cap| held < cap),
890            // Nothing attached, but somewhere to get one from the moment this
891            // database needs it, which is what makes the answer yes rather than
892            // no. Opening it here would mean `INFO` opened files.
893            None => self.store.is_some(),
894        }
895    }
896
897    /// Take a fresh memory reading, which the maintenance turn does once a batch.
898    ///
899    /// Nothing at all when there is no limit, which is the default and is every
900    /// server that has not asked for one.
901    pub fn refresh_memory(&mut self) {
902        if self.maxmemory != 0 {
903            self.used = self.settled_memory();
904        }
905    }
906
907    /// [`Server::memory_bytes`], asked the cheap way.
908    ///
909    /// The same number. The difference is that this asks each database only
910    /// about the collections that could have moved since the last time, which is
911    /// what a batch touched rather than what the server holds, so it can be
912    /// asked once a batch and again on every command that is over the limit.
913    fn settled_memory(&mut self) -> usize {
914        self.keyspaces_mut()
915            .map(Keyspace::settled_memory_bytes)
916            .sum::<usize>()
917            + self.conn_bytes
918    }
919
920    /// Make room under the `maxmemory` limit, throwing keys away if that is what
921    /// it takes. Answers whether there is anything left it could throw away.
922    ///
923    /// Redis runs the same thing from `processCommand` before every command and
924    /// so does this: a client that writes has to be judged at the moment it
925    /// writes, not a batch later, or the limit is a suggestion.
926    ///
927    /// Three things happen in the loop and all three are needed. Eviction picks
928    /// a key and drops it. Compaction gives the pages back, because dropping a
929    /// key marks its record dead and returns nothing on its own, so a loop that
930    /// only evicted would throw the whole keyspace away and watch the number
931    /// stay where it was. The reading is taken again each time round, because
932    /// the two of them together are the only thing that moves it.
933    ///
934    /// # Why running out of budget is not a no
935    ///
936    /// `false` means there was nothing left to evict, which is `noeviction`, or
937    /// a `volatile` policy on a database where nothing has a deadline, or a
938    /// keyspace that is already empty. It does not mean the server is still over
939    /// its limit, and that difference is Redis's: `performEvictions` answers
940    /// `EVICT_FAIL` only when it has run out of things to delete, and
941    /// `processCommand` refuses the client on that and on nothing else. Running
942    /// out of time part way through a job it is doing well comes back as
943    /// `EVICT_RUNNING` and the command goes through, because a server that is
944    /// evicting steadily and refusing every write while it does it is worse for
945    /// the client than a little overshoot.
946    ///
947    /// # What the limit is worth
948    ///
949    /// Space comes back a segment at a time and a segment is two megabytes, so
950    /// this holds a server to its limit give or take a segment. A `maxmemory` of
951    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
952    /// megabytes is asking for a precision this store does not have.
953    pub fn make_room(&mut self) -> bool {
954        if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
955            return true;
956        }
957        // The cached reading is a batch old and the batch may have compacted
958        // since, so take a fresh one before throwing anything away. It is the
959        // settled reading and not the walk, so what this costs is the handful of
960        // collections the last batch touched and not the whole database.
961        self.used = self.settled_memory();
962        let mut budget = EVICT_BUDGET;
963        while self.used as u64 > self.maxmemory {
964            let over = self.used - self.maxmemory as usize;
965            if !self.relieve_step(over) {
966                return false;
967            }
968            self.compact_hard_step();
969            self.used = self.settled_memory();
970            budget -= 1;
971            if budget == 0 {
972                break;
973            }
974        }
975        true
976    }
977
978    /// Give back `over` bytes from whichever database can, by moving values to
979    /// the file where there is one and by throwing keys away where there is not.
980    ///
981    /// The two answers are the eviction inversion and which one a database gets
982    /// is [`Server::migrates`]. Answers whether anything was given back at all,
983    /// and `false` is what refuses the client's write.
984    ///
985    /// A store that will not take the bytes counts as nothing given back, so the
986    /// write is refused rather than turned into a deletion. A disk that is
987    /// misbehaving is a reason to stop accepting writes and it is not a reason
988    /// to start losing data that was accepted already.
989    ///
990    /// Round robin from a cursor rather than always starting at database zero,
991    /// so a server using more than one of them does not empty the first before
992    /// touching the second. Almost every server is on database zero only, where
993    /// this is one call that answers and fifteen that say the map is empty.
994    fn relieve_step(&mut self, over: usize) -> bool {
995        for turn in 0..self.slots() {
996            let i = (self.evict_db + turn) % self.slots();
997            // An empty keyspace has nothing to move and opening a log for one
998            // would cost a resident page window to find that out.
999            let gave = if !self.slot(i).is_empty() && self.migrates(i) {
1000                self.attach_store(i);
1001                // Whether it made room and not whether it moved a key. A round
1002                // that demoted nothing and handed back a segment is a round
1003                // that made room, and reading only the count refuses the write
1004                // that provoked it.
1005                self.slot_mut(i)
1006                    .relieve(over)
1007                    .is_ok_and(yo_kv::tier::Relief::made_room)
1008            } else {
1009                self.slot_mut(i).evict_one()
1010            };
1011            if gave {
1012                self.evict_db = (i + 1) % self.slots();
1013                self.dirty |= 1u64 << self.slot_db(i);
1014                return true;
1015            }
1016        }
1017        false
1018    }
1019
1020    /// The sweep the shard loop calls, at most once a millisecond.
1021    ///
1022    /// The gate is the whole difference between this and [`Server::expire_step`].
1023    /// A maintenance slice runs on every turn of the loop and a turn is a
1024    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
1025    /// thousand times per millisecond and spend a real share of the shard on
1026    /// looking for keys that cannot have died since the last look. Nothing in a
1027    /// database changes fast enough to be worth asking about more often than the
1028    /// clock can tell the difference, and the clock here is milliseconds.
1029    ///
1030    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
1031    /// hertz, so this is not the thing that decides how promptly memory comes
1032    /// back. What it decides is that an idle server sweeps a thousand times a
1033    /// second rather than a million.
1034    pub fn expire_slice(&mut self, budget: usize) -> usize {
1035        let now = self.clock.now_ms();
1036        if now == self.expire_ms {
1037            return 0;
1038        }
1039        self.expire_ms = now;
1040        self.expire_step(budget)
1041    }
1042
1043    /// Sweep dead keys out of the databases, spending at most `budget` looks.
1044    ///
1045    /// Answers what it spent, so the caller can charge its maintenance slice for
1046    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
1047    ///
1048    /// Round robin from its own cursor, and every database gets offered whatever
1049    /// is left of the budget rather than a sixteenth of it each, so a server on
1050    /// database zero only, which is nearly every server, spends the whole slice
1051    /// where the keys are. The fifteen empty ones cost a comparison apiece
1052    /// because a database with no key carrying a deadline says so without
1053    /// drawing anything.
1054    ///
1055    /// The cursor moves to the database after whichever one did the work, so two
1056    /// busy databases take turns instead of the lower numbered one starving the
1057    /// other.
1058    pub fn expire_step(&mut self, budget: usize) -> usize {
1059        let mut spent = 0;
1060        for turn in 0..self.slots() {
1061            if spent >= budget {
1062                break;
1063            }
1064            let i = (self.expire_db + turn) % self.slots();
1065            let c = self.slot_mut(i).expire_cycle(budget - spent);
1066            spent += c.examined;
1067            if c.expired > 0 {
1068                self.expire_db = (i + 1) % self.slots();
1069                self.dirty |= 1u64 << self.slot_db(i);
1070            }
1071        }
1072        spent
1073    }
1074
1075    /// One slice of compaction for a server that is over its limit.
1076    ///
1077    /// Takes the databases in the same order [`Server::compact_step`] does and
1078    /// stops at the first one that had something to move, and it asks with the
1079    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
1080    fn compact_hard_step(&mut self) -> Option<usize> {
1081        for turn in 0..self.slots() {
1082            let i = (self.next_db + turn) % self.slots();
1083            if let Some(moved) = self.slot_mut(i).compact_hard() {
1084                self.next_db = (i + 1) % self.slots();
1085                return Some(moved);
1086            }
1087        }
1088        None
1089    }
1090
1091    /// Give one database's dead space back, if any database has enough of it to
1092    /// be worth the move. `None` when no database had a candidate.
1093    ///
1094    /// Once per batch, next to the clock. Overwriting a key writes a new record
1095    /// and counts the old one dead, so without this a server holds everything
1096    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
1097    /// a key against Redis at 144 for the same load, and the whole difference
1098    /// was dead records nothing ever came back for.
1099    ///
1100    /// At most one segment moves per call and the search starts one database
1101    /// further along each time, so the cost of asking is a comparison per
1102    /// database and the cost of acting is bounded by a segment.
1103    pub fn compact_step(&mut self) -> Option<usize> {
1104        for turn in 0..self.slots() {
1105            let i = (self.next_db + turn) % self.slots();
1106            // Nothing has run against this database since it last said it had
1107            // nothing to collect, so it still has nothing to collect and the
1108            // line it lives on stays where it is.
1109            let at = self.slot_db(i);
1110            if self.dirty & (1 << at) == 0 {
1111                continue;
1112            }
1113            if let Some(moved) = self.slot_mut(i).compact_step() {
1114                self.next_db = (i + 1) % self.slots();
1115                return Some(moved);
1116            }
1117            // Only once every stripe of the database has said it has nothing,
1118            // since the bit is per database and one stripe answering for all of
1119            // them would stop the others being asked at all.
1120            if i % self.width == self.width - 1 {
1121                self.dirty &= !(1u64 << at);
1122            }
1123        }
1124        None
1125    }
1126}
1127
1128impl Default for Server {
1129    fn default() -> Server {
1130        Server::new()
1131    }
1132}
1133
1134/// What one connection has chosen.
1135pub struct Session {
1136    db: usize,
1137    id: u64,
1138    name: Vec<u8>,
1139    /// The `HIMPORT` fieldsets this connection has prepared.
1140    ///
1141    /// Connection state and not keyspace state, which is the reference's design
1142    /// and not a shortcut: a fieldset is invisible to every other connection and
1143    /// the keys built from one outlive it.
1144    sets: himport::Fieldsets,
1145}
1146
1147impl Session {
1148    /// A new connection, on database zero with no name.
1149    #[must_use]
1150    pub fn new(id: u64) -> Session {
1151        Session {
1152            db: 0,
1153            id,
1154            name: Vec::new(),
1155            sets: himport::Fieldsets::default(),
1156        }
1157    }
1158
1159    /// The connection id, which `HELLO` reports and `CLIENT` will.
1160    #[must_use]
1161    pub const fn id(&self) -> u64 {
1162        self.id
1163    }
1164
1165    /// Which database this connection is working in.
1166    #[must_use]
1167    pub const fn db(&self) -> usize {
1168        self.db
1169    }
1170
1171    /// The name the client gave itself, empty if it gave none.
1172    #[must_use]
1173    pub fn name(&self) -> &[u8] {
1174        &self.name
1175    }
1176
1177    /// Put everything back the way it was when the connection was opened.
1178    ///
1179    /// The protocol is not here because it is not here: it lives in the reply
1180    /// buffer, and `RESET` sets it back there.
1181    pub fn reset(&mut self) {
1182        self.db = 0;
1183        self.name.clear();
1184        // `SELECT` leaves these alone and `RESET` does not, both checked
1185        // against 8.10.1, which is the one pair of answers you could not guess
1186        // from what the command is for.
1187        self.sets.clear();
1188    }
1189
1190    /// Record the name from `HELLO ... SETNAME`.
1191    fn set_name(&mut self, name: &[u8]) {
1192        yo_alloc::allow(|| {
1193            self.name.clear();
1194            self.name.extend_from_slice(name);
1195        });
1196    }
1197}
1198
1199/// Run one command and write its reply.
1200///
1201/// The name is looked up and the arity is checked here, once, so that no body
1202/// has to. Everything after that is the command's own.
1203pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1204    // The decoder never produces a command with no name. If one ever arrives,
1205    // it is not something to answer.
1206    if args.is_empty() {
1207        return Flow::Continue;
1208    }
1209    resolved(server, session, lookup(args.name()), args, out)
1210}
1211
1212/// The same, for a caller that has already found the command.
1213///
1214/// The engine frames a command before it runs it, and between those two it also
1215/// asks which key the command touches so the record can be prefetched. That is
1216/// two more chances to look the name up, and looking it up three times to run it
1217/// once is three times the cost of the cheapest thing in the path. So the engine
1218/// resolves the name where it frames the command, carries the answer on the
1219/// framed command, and both the other two take it from there.
1220///
1221/// `spec` is `None` for a name that is not a command, which is the same thing
1222/// [`lookup`] says and lands in the same reply.
1223pub fn resolved(
1224    server: &mut Server,
1225    session: &mut Session,
1226    spec: Option<&'static Spec>,
1227    args: Args<'_>,
1228    out: &mut Out,
1229) -> Flow {
1230    if args.is_empty() {
1231        return Flow::Continue;
1232    }
1233    server.stats.commands += 1;
1234
1235    let Some(spec) = spec else {
1236        write_error(out, &args::unknown_command(args));
1237        return Flow::Continue;
1238    };
1239    if !arity_ok(spec, args.len()) {
1240        server.cmdstats.at(spec).rejected += 1;
1241        write_error(out, &args::wrong_arity(spec.name));
1242        return Flow::Continue;
1243    }
1244
1245    // The limit first, so a server with no `maxmemory`, which is the default and
1246    // is nearly all of them, pays one comparison against a field that is already
1247    // warm. Every command and not only the writes, because that is where Redis
1248    // puts it: making room is the server's job whatever the client asked for,
1249    // and the flag only decides who gets told no when there is no room to make.
1250    //
1251    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1252    // Redis's list, so a command that only frees is let through with nothing
1253    // left, which is what lets a client dig itself out with `DEL`.
1254    if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1255        server.cmdstats.at(spec).rejected += 1;
1256        out.error_line(b"OOM ", OOM);
1257        return Flow::Continue;
1258    }
1259
1260    // Which databases the maintenance turn after this batch has to ask. Marked
1261    // for every command and not only for the writes, because a read can make
1262    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1263    // record it dropped is exactly the kind of thing the collector is for.
1264    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1265    // two groups that hold them mark all of them rather than the session's.
1266    server.dirty |= match spec.group {
1267        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1268        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1269            1u64 << session.db
1270        }
1271        _ => ALL_DATABASES,
1272    };
1273
1274    let mark = out.len();
1275    // Before the group, because the five that block are list commands and would
1276    // otherwise land in `lists`, which is handed one database and nothing that
1277    // could park a client. The flag is the right thing to branch on rather than
1278    // a list of names: it is what `COMMAND INFO` reports about exactly these
1279    // commands, and the sorted set and stream ones that arrive later carry it
1280    // too.
1281    let done = if spec.flags.contains(&"blocking") {
1282        blocking::execute(server, session, spec, args, out)
1283    } else {
1284        match spec.group {
1285            "string" => {
1286                let db = session.db;
1287                strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1288            }
1289            // Its own group and its own file, and the same values underneath:
1290            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1291            // something a `SET` left behind works.
1292            "bitmap" => {
1293                let db = session.db;
1294                bits::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1295            }
1296            // The same again: a sketch is a string with a documented layout, so
1297            // `GET` hands one to a client and `SET` takes it back.
1298            "hyperloglog" => {
1299                let db = session.db;
1300                hll::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1301            }
1302            "set" => {
1303                let db = session.db;
1304                sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1305            }
1306            // The one hash command whose state is not in the keyspace. A
1307            // fieldset belongs to the connection, so this is handed the session
1308            // as well as the database, the same exception `MIGRATE` gets in the
1309            // keyspace group for the socket it keeps.
1310            "hash" if spec.name == "himport" => {
1311                let db = session.db;
1312                himport::execute(&mut server.dbs[db], &mut session.sets, args, out)
1313                    .map(|()| Flow::Continue)
1314            }
1315            "hash" => {
1316                let db = session.db;
1317                hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1318            }
1319            "list" => {
1320                let db = session.db;
1321                lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1322            }
1323            "zset" => {
1324                let db = session.db;
1325                zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1326            }
1327            // A geo key is a sorted set and these are sorted set commands with
1328            // arithmetic on the way in and on the way out, so a client can ZREM
1329            // a place out of one and ZCARD it to count them.
1330            "geo" => {
1331                let db = session.db;
1332                geo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1333            }
1334            "array" => {
1335                let db = session.db;
1336                arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1337            }
1338            "graph" => {
1339                let db = session.db;
1340                graph::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1341            }
1342            // A document under a key, reached by a path. The group is Redis's
1343            // module surface and the storage is ours, the same trade the vector
1344            // set group makes.
1345            "json" => {
1346                let db = session.db;
1347                json::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1348            }
1349            "vector" => {
1350                let db = session.db;
1351                vectors::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1352            }
1353            "bloom" => {
1354                let db = session.db;
1355                bloom::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1356            }
1357            "cuckoo" => {
1358                let db = session.db;
1359                cuckoo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1360            }
1361            "cms" => {
1362                let db = session.db;
1363                cms::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1364            }
1365            "topk" => {
1366                let db = session.db;
1367                topk::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1368            }
1369            "tdigest" => {
1370                let db = session.db;
1371                tdigest::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1372            }
1373            "ts" => {
1374                let db = session.db;
1375                ts::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1376            }
1377            // The clock is read before the database is borrowed, because every
1378            // stream command needs the time and it lives on the server. An
1379            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1380            // `XINFO` reporting it all have to agree about what moment this is.
1381            "stream" => {
1382                let db = session.db;
1383                let now = server.now_ms();
1384                streams::execute(&mut server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1385            }
1386            // The one keyspace command that needs more than the databases,
1387            // because the socket it talks down is held on the server between
1388            // commands and not opened again for each one.
1389            "keyspace" if spec.name == "migrate" => {
1390                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1391            }
1392            // Every database and not the one the session is on, because `COPY` takes
1393            // a `DB n` and writes into a database nobody selected.
1394            "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
1395                .map(|()| Flow::Continue),
1396            // No database at all, because an index is not a key. The registry
1397            // is the whole of what these sixteen commands touch.
1398            "search" => {
1399                search::execute(&mut server.search, spec, args, out).map(|()| Flow::Continue)
1400            }
1401            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1402            _ => server::execute(server, session, spec, args, out),
1403        }
1404    };
1405    let flow = match done {
1406        Ok(flow) => flow,
1407        Err(e) => {
1408            out.truncate(mark);
1409            write_error(out, &e);
1410            Flow::Continue
1411        }
1412    };
1413
1414    // Counted here and not before the call, which is where Redis counts it, so
1415    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1416    // same way theirs does.
1417    //
1418    // Failure is read off the reply rather than off the `Result`, because the
1419    // two are not the same set. A command that ran out of arguments comes back
1420    // as an `Err` and a command that was sent the wrong password writes its own
1421    // error line and comes back `Ok`, and both of those are a call that failed.
1422    // The first byte at the mark is what a client would branch on, and it is `-`
1423    // for an error on either protocol and `!` for RESP3's long form.
1424    let row = server.cmdstats.at(spec);
1425    row.calls += 1;
1426    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1427        row.failed += 1;
1428    }
1429    flow
1430}
1431
1432/// The error line for an error value.
1433///
1434/// The prefix is what a client branches on, and there are three of them:
1435/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1436/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1437/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1438/// than routed through here. `OOM` is not a [`Code`] of its own because
1439/// [`Code::Full`] already covers the string that is too long for
1440/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1441fn write_error(out: &mut Out, e: &Error) {
1442    let prefix: &[u8] = match e.code() {
1443        Code::WrongType => b"WRONGTYPE ",
1444        // Only the HyperLogLog commands answer this one, and the prefix is the
1445        // sentence a client branches on to tell a sketch it cannot read from a
1446        // sketch it sent wrong.
1447        Code::Corrupt => b"INVALIDOBJ ",
1448        _ => b"ERR ",
1449    };
1450    out.error_line(prefix, e.message().as_bytes());
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455    use super::*;
1456    use crate::proto::{Limits, Proto};
1457    use crate::request::Argv;
1458
1459    /// Build the wire bytes for a command.
1460    ///
1461    /// Tests go through the codec rather than around it, so an argument in a
1462    /// test is the same borrowed slice a connection produces.
1463    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1464        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1465        for p in parts {
1466            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1467            wire.extend_from_slice(p);
1468            wire.extend_from_slice(b"\r\n");
1469        }
1470        wire
1471    }
1472
1473    /// A server, a connection and a buffer, driven the way the reactor will.
1474    struct Fixture {
1475        server: Server,
1476        session: Session,
1477        argv: Argv,
1478        out: Out,
1479    }
1480
1481    impl Fixture {
1482        fn new() -> Fixture {
1483            Fixture::on(Server::new())
1484        }
1485
1486        /// The same, on a server whose databases are cut into `width` stripes.
1487        fn striped(width: usize) -> Fixture {
1488            Fixture::on(Server::with_width(width))
1489        }
1490
1491        fn on(server: Server) -> Fixture {
1492            Fixture {
1493                server,
1494                session: Session::new(7),
1495                argv: Argv::new(),
1496                out: Out::new(Proto::Resp2),
1497            }
1498        }
1499
1500        /// Run one command and answer with the bytes it wrote.
1501        fn run(&mut self, parts: &[&[u8]]) -> String {
1502            self.flow(parts).1
1503        }
1504
1505        /// Run one command and answer with the bytes exactly as written.
1506        ///
1507        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
1508        /// every reply that is text and destroys a `DUMP` payload, since a
1509        /// payload is arbitrary bytes and a checksum on the end of them.
1510        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1511            let wire = encode(parts);
1512            self.argv.decode(&wire, &Limits::default()).unwrap();
1513            self.out.clear();
1514            execute(
1515                &mut self.server,
1516                &mut self.session,
1517                Args::new(&self.argv, &wire),
1518                &mut self.out,
1519            );
1520            self.out.as_slice().to_vec()
1521        }
1522
1523        /// Move every clock in the server on by `ms`.
1524        fn advance(&mut self, ms: u64) {
1525            self.server.advance_clock_ms(ms);
1526        }
1527
1528        /// The same, with what the connection should do next.
1529        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1530            let wire = encode(parts);
1531            self.argv.decode(&wire, &Limits::default()).unwrap();
1532            self.out.clear();
1533            let flow = execute(
1534                &mut self.server,
1535                &mut self.session,
1536                Args::new(&self.argv, &wire),
1537                &mut self.out,
1538            );
1539            (
1540                flow,
1541                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1542            )
1543        }
1544    }
1545
1546    /// What a client does all day: write the same keys again and again. Every
1547    /// one of those writes leaves the previous record behind, so a server that
1548    /// never compacts holds every version of every key it has ever been sent.
1549    #[test]
1550    fn rewriting_the_same_keys_does_not_grow_the_server() {
1551        let mut f = Fixture::new();
1552        let val = vec![b'v'; 1024];
1553        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1554
1555        for k in &keys {
1556            f.run(&[b"SET", k, &val]);
1557        }
1558        f.server.compact_step();
1559        let after_first = f.server.memory_bytes();
1560
1561        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
1562        // of it. Thirty two megabytes written to hold sixty four kilobytes,
1563        // which is the shape of a real workload and is enough churn to fill
1564        // sixteen segments if nothing ever comes back.
1565        for _ in 0..500 {
1566            for k in &keys {
1567                f.run(&[b"SET", k, &val]);
1568            }
1569            f.server.compact_step();
1570        }
1571
1572        assert!(
1573            f.server.memory_bytes() <= after_first * 2,
1574            "held {} after five hundred passes against {after_first} after one",
1575            f.server.memory_bytes()
1576        );
1577        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1578        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1579    }
1580
1581    /// The same churn on a database nobody starts on, either side of a quiet
1582    /// spell long enough for the maintenance turn to stop asking about it.
1583    ///
1584    /// The turn after each batch skips a database that has already said it has
1585    /// nothing to collect and has not been touched since, which is what keeps a
1586    /// server whose clients are all on database zero from loading and storing
1587    /// in the other fifteen every batch to be told no. Two things could go
1588    /// wrong with that. A database might never be marked at all, so this uses
1589    /// database nine, which nothing marks by accident. And a database whose
1590    /// mark was cleared might never get it back, so this drains the collector
1591    /// until it says there is nothing left, checks the mark really is gone, and
1592    /// then writes another thirty two megabytes through the same sixty four
1593    /// keys. If either went wrong the server would hold all of it.
1594    #[test]
1595    fn a_database_nobody_started_on_is_still_collected() {
1596        let mut f = Fixture::new();
1597        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1598        let val = vec![b'v'; 1024];
1599        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1600
1601        for k in &keys {
1602            f.run(&[b"SET", k, &val]);
1603        }
1604        while f.server.compact_step().is_some() {}
1605        assert_eq!(
1606            f.server.dirty & (1 << 9),
1607            0,
1608            "database nine was drained and should not be asked again until it is written to"
1609        );
1610        let after_first = f.server.memory_bytes();
1611
1612        for _ in 0..500 {
1613            for k in &keys {
1614                f.run(&[b"SET", k, &val]);
1615            }
1616            f.server.compact_step();
1617        }
1618
1619        assert!(
1620            f.server.memory_bytes() <= after_first * 2,
1621            "held {} after five hundred passes against {after_first} after one",
1622            f.server.memory_bytes()
1623        );
1624        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1625        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1626        // And nothing landed anywhere else on the way.
1627        f.run(&[b"SELECT", b"0"]);
1628        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1629    }
1630
1631    #[test]
1632    fn a_command_goes_from_bytes_to_bytes() {
1633        let mut f = Fixture::new();
1634        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1635        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1636        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1637        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1638        // The name is matched whatever case it came in, and so are the options.
1639        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1640        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1641    }
1642
1643    #[test]
1644    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1645        let mut f = Fixture::new();
1646        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1647        // A key named twice exists twice and can only be deleted once, and both
1648        // of those are Redis's answers rather than tidier ones.
1649        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1650        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1651        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1652        // UNLINK is the same body and reports the same way.
1653        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1654        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1655    }
1656
1657    #[test]
1658    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1659        let mut f = Fixture::new();
1660        f.run(&[b"SET", b"k", b"v"]);
1661        // A simple string on both protocols, which is unusual: most replies
1662        // that carry a word are bulk strings.
1663        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1664        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1665    }
1666
1667    #[test]
1668    fn touch_counts_the_way_exists_counts() {
1669        let mut f = Fixture::new();
1670        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1671        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1672        assert_eq!(
1673            f.run(&[b"TOUCH", b"a", b"a"]),
1674            ":2\r\n",
1675            "twice counts twice"
1676        );
1677        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1678        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1679    }
1680
1681    #[test]
1682    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1683        let mut f = Fixture::new();
1684        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1685        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1686
1687        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1688        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1689        assert_eq!(
1690            f.run(&[b"TTL", b"b"]),
1691            ":100\r\n",
1692            "the source's and not b's"
1693        );
1694        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1695    }
1696
1697    #[test]
1698    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1699        let mut f = Fixture::new();
1700        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1701        // The source is checked before the destination, so this is the error
1702        // and not the zero RENAMENX would otherwise answer for a taken name.
1703        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1704    }
1705
1706    #[test]
1707    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1708        let mut f = Fixture::new();
1709        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1710
1711        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1712        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1713        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
1714        // one call the two disagree about and neither does any work for.
1715        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1716        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1717        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1718        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1719    }
1720
1721    #[test]
1722    fn renaming_a_set_does_not_touch_a_member() {
1723        let mut f = Fixture::new();
1724        for i in 0..300 {
1725            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1726        }
1727        let before = f.server.memory_bytes();
1728
1729        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1730        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1731        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1732        assert!(
1733            f.server.memory_bytes().abs_diff(before) < 256,
1734            "the members were copied: {} against {before}",
1735            f.server.memory_bytes()
1736        );
1737    }
1738
1739    #[test]
1740    fn a_copy_is_a_second_value_and_not_a_second_name() {
1741        let mut f = Fixture::new();
1742        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1743
1744        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1745        f.run(&[b"SADD", b"t", b"m3"]);
1746        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1747        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1748    }
1749
1750    /// Every type a key can hold, copied, because two of them used to panic.
1751    ///
1752    /// `COPY` reads the value out of the source through one match on the type
1753    /// tag, and that match had a catch all at the bottom from back when a set
1754    /// and a hash were the only bodies. The list and the sorted set landed after
1755    /// it and nobody came back, so `COPY mylist other` took the shard down. It
1756    /// is an ordinary command against a type the server supports everywhere
1757    /// else, so this walks all five rather than the two that were broken: the
1758    /// point is that the next type cannot land the same way.
1759    #[test]
1760    fn every_type_can_be_copied() {
1761        let mut f = Fixture::new();
1762        f.run(&[b"SET", b"str", b"v1"]);
1763        f.run(&[b"SADD", b"set", b"m1"]);
1764        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1765        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1766        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1767
1768        for name in [
1769            &b"str"[..],
1770            &b"set"[..],
1771            &b"hash"[..],
1772            &b"list"[..],
1773            &b"zset"[..],
1774        ] {
1775            let dst = [name, b":copy"].concat();
1776            assert_eq!(
1777                f.run(&[b"COPY", name, &dst]),
1778                ":1\r\n",
1779                "copying {}",
1780                String::from_utf8_lossy(name)
1781            );
1782            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1783        }
1784
1785        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1786            let mut want = String::from("*2\r\n");
1787            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1788            want
1789        });
1790        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1791
1792        // And the copy is its own value, not a second name for the source.
1793        f.run(&[b"RPUSH", b"list:copy", b"c"]);
1794        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1795        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1796    }
1797
1798    #[test]
1799    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1800        let mut f = Fixture::new();
1801        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1802        f.run(&[b"SET", b"b", b"v2"]);
1803
1804        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1805        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1806        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1807        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1808        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1809        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1810    }
1811
1812    #[test]
1813    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1814        let mut f = Fixture::new();
1815        f.run(&[b"SET", b"a", b"v1"]);
1816
1817        // Same key, different database, so this is not the same object and is
1818        // an ordinary copy. Same key in the same database is the error below.
1819        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1820        f.run(&[b"SELECT", b"1"]);
1821        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1822        assert_eq!(
1823            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1824            ":0\r\n",
1825            "taken"
1826        );
1827        assert_eq!(
1828            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1829            ":1\r\n"
1830        );
1831    }
1832
1833    #[test]
1834    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1835        let mut f = Fixture::new();
1836        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1837        assert_eq!(
1838            f.run(&[b"SORT", b"l"]),
1839            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1840        );
1841        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
1842        assert_eq!(
1843            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1844            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1845        );
1846        assert_eq!(
1847            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1848            "*1\r\n$1\r\n2\r\n"
1849        );
1850    }
1851
1852    #[test]
1853    fn sort_reads_a_key_per_element_for_by_and_for_get() {
1854        let mut f = Fixture::new();
1855        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1856        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1857        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
1858        // misses, which is a nil in the middle of the array and not a short one.
1859        assert_eq!(
1860            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1861            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1862        );
1863    }
1864
1865    #[test]
1866    fn sort_store_writes_a_list_and_answers_its_length() {
1867        let mut f = Fixture::new();
1868        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1869        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1870        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1871        assert_eq!(
1872            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1873            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1874        );
1875        // An empty result takes the destination with it rather than leaving a
1876        // list that holds nothing.
1877        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1878        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1879    }
1880
1881    #[test]
1882    fn sort_ro_does_not_know_the_word_store() {
1883        let mut f = Fixture::new();
1884        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1885        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1886        assert_eq!(
1887            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1888            "-ERR syntax error\r\n"
1889        );
1890        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1891    }
1892
1893    #[test]
1894    fn sort_refuses_what_it_cannot_sort() {
1895        let mut f = Fixture::new();
1896        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1897        f.run(&[b"SET", b"s", b"x"]);
1898        assert_eq!(
1899            f.run(&[b"SORT", b"s"]),
1900            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1901        );
1902        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1903        assert_eq!(
1904            f.run(&[b"SORT", b"words"]),
1905            "-ERR One or more scores can't be converted into double\r\n"
1906        );
1907        assert_eq!(
1908            f.run(&[b"SORT", b"words", b"ALPHA"]),
1909            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1910        );
1911        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1912    }
1913
1914    #[test]
1915    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1916        let mut f = Fixture::new();
1917        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1918        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1919        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1920        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1921        assert_eq!(
1922            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1923            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1924        );
1925        // And back, which proves the body survived the trip rather than being
1926        // rebuilt from a copy that happened to look the same.
1927        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1928        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1929    }
1930
1931    #[test]
1932    fn move_answers_zero_when_either_end_says_no() {
1933        let mut f = Fixture::new();
1934        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1935        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1936        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1937        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1938        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1939        // The destination is taken, so nothing moves and the source is still
1940        // there with what it had.
1941        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1942        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1943        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1944        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1945    }
1946
1947    #[test]
1948    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1949        let mut f = Fixture::new();
1950        assert_eq!(
1951            f.run(&[b"MOVE", b"a", b"0"]),
1952            "-ERR source and destination objects are the same\r\n"
1953        );
1954        assert_eq!(
1955            f.run(&[b"MOVE", b"a", b"99"]),
1956            "-ERR DB index is out of range\r\n"
1957        );
1958        assert_eq!(
1959            f.run(&[b"MOVE", b"a", b"-1"]),
1960            "-ERR DB index is out of range\r\n"
1961        );
1962        assert_eq!(
1963            f.run(&[b"MOVE", b"a", b"x"]),
1964            "-ERR value is not an integer or out of range\r\n"
1965        );
1966    }
1967
1968    #[test]
1969    fn swapdb_swaps_what_two_connections_would_see() {
1970        let mut f = Fixture::new();
1971        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1972        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1973        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1974        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1975
1976        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1977        // Still on database zero, and database zero is a different database.
1978        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1979        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1980        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1981        // A database swapped with itself is fine and changes nothing.
1982        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1983        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1984    }
1985
1986    #[test]
1987    fn swapdb_says_which_index_it_could_not_read() {
1988        let mut f = Fixture::new();
1989        assert_eq!(
1990            f.run(&[b"SWAPDB", b"x", b"1"]),
1991            "-ERR invalid first DB index\r\n"
1992        );
1993        assert_eq!(
1994            f.run(&[b"SWAPDB", b"0", b"y"]),
1995            "-ERR invalid second DB index\r\n"
1996        );
1997        // A number too big to be an index on a server that keeps one in an int
1998        // is the same complaint, and a plausible one that is not ours is the
1999        // range complaint instead. The split is Redis's.
2000        assert_eq!(
2001            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2002            "-ERR invalid first DB index\r\n"
2003        );
2004        assert_eq!(
2005            f.run(&[b"SWAPDB", b"0", b"99"]),
2006            "-ERR DB index is out of range\r\n"
2007        );
2008        assert_eq!(
2009            f.run(&[b"SWAPDB", b"-1", b"0"]),
2010            "-ERR DB index is out of range\r\n"
2011        );
2012    }
2013
2014    #[test]
2015    fn wait_answers_zero_replicas_without_waiting() {
2016        let mut f = Fixture::new();
2017        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2018        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2019        // A replica that is never going to arrive, and a timeout that would be
2020        // a real wait on a server that had one.
2021        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2022        // Negative replicas is not an error, because zero is already more than
2023        // it asked for.
2024        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2025        assert_eq!(
2026            f.run(&[b"WAIT", b"x", b"0"]),
2027            "-ERR value is not an integer or out of range\r\n"
2028        );
2029        assert_eq!(
2030            f.run(&[b"WAIT", b"0", b"-1"]),
2031            "-ERR timeout is negative\r\n"
2032        );
2033        assert_eq!(
2034            f.run(&[b"WAIT", b"0", b"1.5"]),
2035            "-ERR timeout is not an integer or out of range\r\n"
2036        );
2037    }
2038
2039    #[test]
2040    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2041        let mut f = Fixture::new();
2042        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2043        assert_eq!(
2044            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2045            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2046        );
2047        assert_eq!(
2048            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2049            "-ERR value is out of range, value must between 0 and 1\r\n"
2050        );
2051        assert_eq!(
2052            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2053            "-ERR value is out of range, must be positive\r\n"
2054        );
2055        // The arguments are all read before the server looks at itself, so a
2056        // bad timeout beats the append only complaint even with numlocal set.
2057        assert_eq!(
2058            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2059            "-ERR timeout is negative\r\n"
2060        );
2061    }
2062
2063    /// The bytes inside a bulk reply, with the header and the trailing break
2064    /// taken off. Every `DUMP` test needs this and none of them care how the
2065    /// length was written.
2066    fn payload(reply: &[u8]) -> Vec<u8> {
2067        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2068        reply[head + 2..reply.len() - 2].to_vec()
2069    }
2070
2071    #[test]
2072    fn a_value_survives_a_dump_and_a_restore() {
2073        let mut f = Fixture::new();
2074        f.run(&[b"SET", b"s", b"hello"]);
2075        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2076        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2077        f.run(&[b"SADD", b"u", b"x", b"y"]);
2078        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2079        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2080
2081        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2082            let mut copy = key.to_vec();
2083            copy.push(b'2');
2084            let bytes = payload(&f.raw(&[b"DUMP", key]));
2085            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
2086            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
2087        }
2088
2089        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2090        assert_eq!(
2091            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2092            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2093        );
2094        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2095        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2096        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2097        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2098        // The encoding survives too, since the payload names the plainest legal
2099        // type and the loader puts the value back on the rung it belongs on.
2100        assert_eq!(
2101            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2102            f.run(&[b"OBJECT", b"ENCODING", b"t"])
2103        );
2104    }
2105
2106    #[test]
2107    fn a_dumped_hash_keeps_its_field_deadlines() {
2108        let mut f = Fixture::new();
2109        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2110        assert_eq!(
2111            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2112            "*1\r\n:1\r\n"
2113        );
2114        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2115        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2116        assert_eq!(
2117            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2118            "*2\r\n:-1\r\n:100\r\n"
2119        );
2120    }
2121
2122    #[test]
2123    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2124        let mut f = Fixture::new();
2125        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2126        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2127        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2128        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2129        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2130        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2131        // An absolute deadline that has already gone is not an error. The key is
2132        // not created and the reply is the same OK a live one gets.
2133        assert_eq!(
2134            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2135            "+OK\r\n"
2136        );
2137        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2138    }
2139
2140    #[test]
2141    fn dump_answers_nothing_for_a_key_that_is_not_there() {
2142        let mut f = Fixture::new();
2143        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2144        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2145        f.advance(50);
2146        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2147    }
2148
2149    #[test]
2150    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2151        let mut f = Fixture::new();
2152        f.run(&[b"SET", b"a", b"first"]);
2153        f.run(&[b"SET", b"b", b"second"]);
2154        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2155        assert_eq!(
2156            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2157            "-BUSYKEY Target key name already exists.\r\n"
2158        );
2159        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2160        assert_eq!(
2161            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2162            "+OK\r\n"
2163        );
2164        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2165    }
2166
2167    /// The busy key comes before the payload, which is not the order the
2168    /// arguments read in. Whether a key is taken should not depend on whether
2169    /// the bytes behind it happened to be good.
2170    #[test]
2171    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2172        let mut f = Fixture::new();
2173        f.run(&[b"SET", b"a", b"v"]);
2174        assert_eq!(
2175            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2176            "-BUSYKEY Target key name already exists.\r\n"
2177        );
2178        // And the options come before even that, so a bad FREQ beats the busy
2179        // key the same way a bad DB beats a missing source in COPY.
2180        assert_eq!(
2181            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2182            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2183        );
2184    }
2185
2186    #[test]
2187    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2188        let mut f = Fixture::new();
2189        f.run(&[b"SET", b"a", b"hello"]);
2190        let good = payload(&f.raw(&[b"DUMP", b"a"]));
2191
2192        let mut flipped = good.clone();
2193        flipped[2] ^= 0x40;
2194        assert_eq!(
2195            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2196            "-ERR DUMP payload version or checksum are wrong\r\n"
2197        );
2198        assert_eq!(
2199            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2200            "-ERR DUMP payload version or checksum are wrong\r\n"
2201        );
2202        // A footer that is right over a body that is not. The type byte says
2203        // string and there is nothing behind it, so the checksum agrees and the
2204        // value does not exist.
2205        let mut truncated = good[..1].to_vec();
2206        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2207        let crc = yo_common::crc::crc64(0, &truncated);
2208        truncated.extend_from_slice(&crc.to_le_bytes());
2209        assert_eq!(
2210            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2211            "-ERR Bad data format\r\n"
2212        );
2213        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2214    }
2215
2216    #[test]
2217    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2218        let mut f = Fixture::new();
2219        f.run(&[b"SET", b"a", b"v"]);
2220        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2221        assert_eq!(
2222            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2223            "-ERR Invalid TTL value, must be >= 0\r\n"
2224        );
2225        assert_eq!(
2226            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2227            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2228        );
2229        assert_eq!(
2230            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2231            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2232        );
2233        // Both are accepted and both are then dropped, which is D-26.
2234        assert_eq!(
2235            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2236            "+OK\r\n"
2237        );
2238        assert_eq!(
2239            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2240            "+OK\r\n"
2241        );
2242    }
2243
2244    /// Neither word is refused for being the wrong one. Each is only accepted
2245    /// while the other is unset, so the second of the two falls through to the
2246    /// plain syntax error rather than getting a message of its own.
2247    #[test]
2248    fn restore_takes_idletime_or_freq_and_not_both() {
2249        let mut f = Fixture::new();
2250        f.run(&[b"SET", b"a", b"v"]);
2251        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2252        assert_eq!(
2253            f.run(&[
2254                b"RESTORE",
2255                b"b",
2256                b"0",
2257                &bytes,
2258                b"IDLETIME",
2259                b"1",
2260                b"FREQ",
2261                b"2"
2262            ]),
2263            "-ERR syntax error\r\n"
2264        );
2265        assert_eq!(
2266            f.run(&[
2267                b"RESTORE",
2268                b"b",
2269                b"0",
2270                &bytes,
2271                b"FREQ",
2272                b"2",
2273                b"IDLETIME",
2274                b"1"
2275            ]),
2276            "-ERR syntax error\r\n"
2277        );
2278        assert_eq!(
2279            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2280            "-ERR syntax error\r\n"
2281        );
2282        assert_eq!(
2283            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2284            "-ERR syntax error\r\n"
2285        );
2286    }
2287
2288    #[test]
2289    fn copy_checks_its_options_before_it_looks_for_anything() {
2290        let mut f = Fixture::new();
2291        // No key exists at all, and every one of these is still the option
2292        // complaint rather than a zero, which is the order a real server uses.
2293        assert_eq!(
2294            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2295            "-ERR DB index is out of range\r\n"
2296        );
2297        assert_eq!(
2298            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2299            "-ERR DB index is out of range\r\n"
2300        );
2301        assert_eq!(
2302            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2303            "-ERR value is not an integer or out of range\r\n"
2304        );
2305        assert_eq!(
2306            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2307            "-ERR syntax error\r\n"
2308        );
2309        assert_eq!(
2310            f.run(&[b"COPY", b"a", b"a"]),
2311            "-ERR source and destination objects are the same\r\n"
2312        );
2313        // Repeated, reordered and lowercased, and the last DB wins.
2314        assert_eq!(
2315            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2316            ":0\r\n"
2317        );
2318    }
2319
2320    #[test]
2321    fn time_is_two_bulk_strings_and_moves() {
2322        let mut f = Fixture::new();
2323        let first = f.run(&[b"TIME"]);
2324        assert!(first.starts_with("*2\r\n$"), "got {first}");
2325        let parts: Vec<&str> = first.split("\r\n").collect();
2326        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2327        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2328        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2329        assert!((0..1_000_000).contains(&micros), "got {micros}");
2330        // The coarse clock the keyspace uses is a cached millisecond that a
2331        // background tick refreshes, so a TIME built on it would answer the
2332        // same microsecond twice in a row here.
2333        assert_ne!(first, f.run(&[b"TIME"]));
2334    }
2335
2336    #[test]
2337    fn a_keyspace_scan_walks_every_key_once() {
2338        let mut f = Fixture::new();
2339        for i in 0..500 {
2340            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2341        }
2342
2343        let mut seen: Vec<String> = Vec::new();
2344        let mut cursor = "0".to_owned();
2345        let mut calls = 0;
2346        loop {
2347            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2348            seen.extend(keys);
2349            cursor = next;
2350            calls += 1;
2351            assert!(calls < 10_000, "the cursor is not advancing");
2352            if cursor == "0" {
2353                break;
2354            }
2355        }
2356
2357        seen.sort();
2358        seen.dedup();
2359        assert_eq!(seen.len(), 500, "every key once and only once");
2360        // And more than one call to get them, or the COUNT is being ignored and
2361        // the loop above proved nothing about resuming.
2362        assert!(calls > 1, "500 keys came back in one batch");
2363    }
2364
2365    #[test]
2366    fn a_scan_narrows_by_pattern_and_by_type() {
2367        let mut f = Fixture::new();
2368        f.run(&[b"SET", b"str", b"v"]);
2369        f.run(&[b"SADD", b"members", b"a"]);
2370        f.run(&[b"HSET", b"fields", b"f", b"v"]);
2371
2372        let all = |f: &mut Fixture, args: &[&[u8]]| {
2373            let mut out: Vec<String> = Vec::new();
2374            let mut cursor = "0".to_owned();
2375            loop {
2376                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2377                line.extend_from_slice(args);
2378                let (next, keys) = scan_reply(&f.run(&line));
2379                out.extend(keys);
2380                cursor = next;
2381                if cursor == "0" {
2382                    break;
2383                }
2384            }
2385            out.sort();
2386            out
2387        };
2388
2389        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2390        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2391        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2392        // Case insensitive, the same as Redis's own comparison.
2393        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2394        // A type nothing can hold is not an error, it just matches nothing.
2395        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2396        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2397        // Both filters at once, and they are an and rather than an or.
2398        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2399    }
2400
2401    #[test]
2402    fn a_scan_says_what_is_wrong_with_it() {
2403        let mut f = Fixture::new();
2404        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2405        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2406        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2407        assert_eq!(
2408            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2409            "-ERR syntax error\r\n"
2410        );
2411        assert_eq!(
2412            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2413            "-ERR value is not an integer or out of range\r\n"
2414        );
2415        assert_eq!(
2416            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2417            "-ERR syntax error\r\n"
2418        );
2419        // A cursor the client made up is a cursor. It resumes somewhere
2420        // arbitrary and answers whatever is there, which is what Redis does and
2421        // is the only behaviour that does not need the server to remember every
2422        // cursor it has handed out.
2423        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2424    }
2425
2426    #[test]
2427    fn keys_and_randomkey_look_at_the_whole_database() {
2428        let mut f = Fixture::new();
2429        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2430        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2431
2432        for name in ["one", "two", "three"] {
2433            f.run(&[b"SET", name.as_bytes(), b"v"]);
2434        }
2435        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2436        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2437        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2438
2439        for _ in 0..50 {
2440            let got = f.run(&[b"RANDOMKEY"]);
2441            assert!(
2442                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2443                "got {got}"
2444            );
2445        }
2446    }
2447
2448    #[test]
2449    fn a_walk_does_not_answer_keys_that_have_expired() {
2450        let mut f = Fixture::new();
2451        f.run(&[b"SET", b"alive", b"v"]);
2452        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2453        f.server.advance_clock_ms(2);
2454        assert_eq!(
2455            f.run(&[b"DBSIZE"]),
2456            ":2\r\n",
2457            "nothing has collected it yet"
2458        );
2459
2460        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2461        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2462        assert_eq!(keys, ["alive"]);
2463        for _ in 0..20 {
2464            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2465        }
2466        // The walk collected it on the way past, which is what makes DBSIZE
2467        // here answer what Redis answers once its own cycle has been round.
2468        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2469    }
2470
2471    #[test]
2472    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2473        let mut f = Fixture::new();
2474        f.run(&[b"SET", b"k", b"v"]);
2475        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2476        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2477
2478        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2479        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2480        let ms = int(&f.run(&[b"PTTL", b"k"]));
2481        assert!((99_000..=100_000).contains(&ms), "got {ms}");
2482
2483        // The absolute pair, derived from the same one number the store kept.
2484        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2485        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2486        assert_eq!(at, (at_ms + 500) / 1000);
2487        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2488
2489        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2490        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2491        assert_eq!(
2492            f.run(&[b"PERSIST", b"k"]),
2493            ":0\r\n",
2494            "nothing to take off the second time"
2495        );
2496        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2497        assert_eq!(
2498            f.run(&[b"GET", b"k"]),
2499            "$1\r\nv\r\n",
2500            "and the value went through all of that untouched"
2501        );
2502    }
2503
2504    #[test]
2505    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2506        let mut f = Fixture::new();
2507        f.run(&[b"SET", b"str", b"v"]);
2508        f.run(&[b"SADD", b"set", b"a", b"b"]);
2509        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2510
2511        for key in [b"str".as_slice(), b"set", b"hash"] {
2512            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2513            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2514        }
2515        // The body is not touched by any of that, which is the whole reason the
2516        // deadline lives in the record and the body lives somewhere else.
2517        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2518        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2519        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2520    }
2521
2522    #[test]
2523    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2524        let mut f = Fixture::new();
2525        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2526            f.run(&[b"SET", key, b"v"]);
2527        }
2528        // Four ways of naming a moment that has passed, and all four are a
2529        // delete answering 1 rather than an error. Zero is a moment, minus one
2530        // is a moment, and the hash field commands refuse the negative one.
2531        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2532        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2533        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2534        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2535        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2536        assert_eq!(
2537            f.run(&[b"EXPIRE", b"a", b"100"]),
2538            ":0\r\n",
2539            "and the key really went, so there is nothing to put a deadline on"
2540        );
2541    }
2542
2543    #[test]
2544    fn the_four_conditions_decide_whether_the_deadline_moves() {
2545        let mut f = Fixture::new();
2546        f.run(&[b"SET", b"k", b"v"]);
2547
2548        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2549        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2550        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2551        assert_eq!(
2552            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2553            ":1\r\n",
2554            "no deadline reads as infinitely far away, so LT passes where GT fails"
2555        );
2556
2557        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2558        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2559        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2560        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2561        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2562        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2563
2564        // The condition is answered before the past check, so this is a 0 and
2565        // the key survives. The other order would delete it.
2566        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2567        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2568        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2569        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2570    }
2571
2572    #[test]
2573    fn the_conditions_are_a_set_and_not_a_keyword() {
2574        let mut f = Fixture::new();
2575        f.run(&[b"SET", b"k", b"v"]);
2576
2577        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2578        assert_eq!(
2579            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2580            ":0\r\n",
2581            "the same keyword twice means it once, and NX now has a deadline to fail on"
2582        );
2583
2584        // XX with LT is the one pair that is not either of them on its own: LT
2585        // alone would accept a key with no deadline and this does not.
2586        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2587        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2588        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2589        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2590        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2591        f.run(&[b"PERSIST", b"k"]);
2592        assert_eq!(
2593            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2594            ":0\r\n",
2595            "where LT on its own would have taken it"
2596        );
2597        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2598    }
2599
2600    #[test]
2601    fn a_key_is_gone_once_its_moment_passes() {
2602        let mut f = Fixture::new();
2603        f.run(&[b"SET", b"k", b"v"]);
2604        f.run(&[b"EXPIRE", b"k", b"100"]);
2605
2606        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2607        f.server.set_clock_ms(at as u64 + 1);
2608        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2609        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2610        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2611        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2612    }
2613
2614    #[test]
2615    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2616        let mut f = Fixture::new();
2617        f.run(&[b"SET", b"k", b"v"]);
2618        for (bad, want) in [
2619            (
2620                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2621                "-ERR value is not an integer or out of range\r\n",
2622            ),
2623            (
2624                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2625                "-ERR Unsupported option MAYBE\r\n",
2626            ),
2627            (
2628                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2629                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2630            ),
2631            (
2632                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2633                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2634            ),
2635            (
2636                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2637                "-ERR GT and LT options at the same time are not compatible\r\n",
2638            ),
2639            // Seconds that overflow when multiplied into milliseconds. Every
2640            // message names the command it came from.
2641            (
2642                &[b"EXPIRE", b"k", b"9223372036854775807"],
2643                "-ERR invalid expire time in 'expire' command\r\n",
2644            ),
2645            (
2646                &[b"EXPIREAT", b"k", b"9223372036854775807"],
2647                "-ERR invalid expire time in 'expireat' command\r\n",
2648            ),
2649            (
2650                &[b"PEXPIRE", b"k", b"9223372036854775807"],
2651                "-ERR invalid expire time in 'pexpire' command\r\n",
2652            ),
2653        ] {
2654            assert_eq!(f.run(bad), want, "for {bad:?}");
2655        }
2656        assert_eq!(
2657            f.run(&[b"TTL", b"k"]),
2658            ":-1\r\n",
2659            "and none of those put a deadline on anything"
2660        );
2661
2662        // The one of the four that has no arithmetic to overflow. Redis takes
2663        // it and holds the number as given, and a record here holds forty six
2664        // bits, so it lands in the year 4199 instead. D-17.
2665        assert_eq!(
2666            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2667            ":1\r\n"
2668        );
2669        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2670    }
2671
2672    #[test]
2673    fn flushing_empties_this_database_or_every_one_of_them() {
2674        let mut f = Fixture::new();
2675        f.run(&[b"SELECT", b"0"]);
2676        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2677        f.run(&[b"SELECT", b"1"]);
2678        f.run(&[b"SET", b"c", b"3"]);
2679        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2680        // ASYNC and SYNC are both taken and neither changes anything, since the
2681        // keyspace is empty before the OK goes out either way.
2682        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2683        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2684        // Only database one was emptied.
2685        f.run(&[b"SELECT", b"0"]);
2686        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2687        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2688        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2689        f.run(&[b"SELECT", b"1"]);
2690        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2691        // Anything else after the name is a syntax error, and so is a third
2692        // argument even when the second one is a word we take.
2693        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2694        assert_eq!(
2695            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2696            "-ERR syntax error\r\n"
2697        );
2698    }
2699
2700    #[test]
2701    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2702        let mut f = Fixture::new();
2703        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2704        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2705        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2706        // Nothing is cached, so nothing is there, one answer per hash asked
2707        // about.
2708        assert_eq!(
2709            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2710            "*2\r\n:0\r\n:0\r\n"
2711        );
2712        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2713        assert_eq!(
2714            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2715            "*0\r\n"
2716        );
2717        assert_eq!(
2718            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2719            "-ERR Library not found\r\n"
2720        );
2721
2722        // Redis's two messages here are its own, one per container, and one of
2723        // them reads like a typo.
2724        assert_eq!(
2725            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2726            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2727        );
2728        assert_eq!(
2729            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2730            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2731        );
2732        // A second argument after the mode is the generic one instead, because
2733        // the count is checked before the word is looked at.
2734        assert_eq!(
2735            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2736            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2737        );
2738        assert_eq!(
2739            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2740            "-ERR Unknown argument bogus\r\n"
2741        );
2742        assert_eq!(
2743            f.run(&[b"SCRIPT", b"EXISTS"]),
2744            "-ERR wrong number of arguments for 'script|exists' command\r\n"
2745        );
2746
2747        // The ones that need an interpreter are not here, and say so rather
2748        // than answering OK to a load that loaded nothing.
2749        assert_eq!(
2750            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2751            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2752        );
2753        assert_eq!(
2754            f.run(&[b"FUNCTION", b"STATS"]),
2755            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2756        );
2757    }
2758
2759    #[test]
2760    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2761        let mut f = Fixture::new();
2762        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2763        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2764        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2765        // Read back as a string it is still an integer, written out as digits
2766        // only because somebody asked for them.
2767        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2768        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2769        // A counter that is not a number is the error the store raises and this
2770        // layer only spells, which is the whole point of the split.
2771        f.run(&[b"SET", b"k", b"hello"]);
2772        assert_eq!(
2773            f.run(&[b"INCR", b"k"]),
2774            "-ERR value is not an integer or out of range\r\n"
2775        );
2776        assert_eq!(
2777            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2778            "-ERR increment would produce NaN or Infinity\r\n"
2779        );
2780    }
2781
2782    /// Every one of these was read off a running 8.8. They are the answers a
2783    /// client library's own test suite checks, and the shapes are not
2784    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
2785    /// integer, `INCREX` is a pair.
2786    #[test]
2787    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2788        let mut f = Fixture::new();
2789        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2790        // The same digest a real 8.8 answers for the same five bytes, which is
2791        // what makes `IFDEQ` usable against a mixed deployment.
2792        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2793        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2794        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2795        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2796        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2797        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2798        assert_eq!(
2799            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2800            "*2\r\n:1\r\n:0\r\n",
2801            "a refused increment reports the value it left alone and applied nothing"
2802        );
2803        assert_eq!(
2804            f.run(&[
2805                b"INCREX",
2806                b"n",
2807                b"BYINT",
2808                b"5",
2809                b"UBOUND",
2810                b"3",
2811                b"SATURATE"
2812            ]),
2813            "*2\r\n:3\r\n:2\r\n"
2814        );
2815        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2816        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2817    }
2818
2819    #[test]
2820    fn the_same_answers_come_out_in_resp3_spelling() {
2821        let mut f = Fixture::new();
2822        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2823        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2824        // A float counter is a double on RESP3 and the digits in a bulk string
2825        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
2826        assert_eq!(
2827            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2828            "*2\r\n,1.5\r\n,1.5\r\n"
2829        );
2830        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2831        // `RESET` puts the protocol back, which is the part that is easy to
2832        // miss and leaves a pooled connection speaking the wrong one.
2833        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2834        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2835    }
2836
2837    #[test]
2838    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2839        let mut f = Fixture::new();
2840        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2841        assert_eq!(flow, Flow::Continue);
2842        assert_eq!(
2843            reply,
2844            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2845        );
2846        // A name with a line ending in it cannot write its own frame into the
2847        // stream, which is the reason the error writer maps them to spaces.
2848        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2849        assert_eq!(reply.matches("\r\n").count(), 1);
2850    }
2851
2852    #[test]
2853    fn arity_is_checked_before_the_command_is() {
2854        let mut f = Fixture::new();
2855        assert_eq!(
2856            f.run(&[b"GET"]),
2857            "-ERR wrong number of arguments for 'get' command\r\n"
2858        );
2859        assert_eq!(
2860            f.run(&[b"MSET", b"k"]),
2861            "-ERR wrong number of arguments for 'mset' command\r\n"
2862        );
2863        // The table says `PING` takes one or more and a real server then
2864        // refuses three, which is the sort of thing that only shows up against
2865        // the real thing.
2866        assert_eq!(
2867            f.run(&[b"PING", b"a", b"b"]),
2868            "-ERR wrong number of arguments for 'ping' command\r\n"
2869        );
2870        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2871        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2872        // `DELEX` takes two or four and nothing between.
2873        assert_eq!(
2874            f.run(&[b"DELEX", b"k", b"IFEQ"]),
2875            "-ERR wrong number of arguments for 'delex' command\r\n"
2876        );
2877    }
2878
2879    /// The option rules, all of them measured against 8.8 rather than read off
2880    /// the documentation. The surprising one is that `SET` accepts the same
2881    /// keyword twice and `INCREX` does not.
2882    #[test]
2883    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2884        let mut f = Fixture::new();
2885        let syntax = "-ERR syntax error\r\n";
2886        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2887        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2888        assert_eq!(
2889            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2890            syntax
2891        );
2892        assert_eq!(
2893            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2894            syntax
2895        );
2896        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2897        // Twice is fine, and the last one wins.
2898        assert_eq!(
2899            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2900            "+OK\r\n"
2901        );
2902        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2903        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2904        // `INCREX` refuses what `SET` allows.
2905        assert_eq!(
2906            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2907            syntax
2908        );
2909        assert_eq!(
2910            f.run(&[b"INCREX", b"n", b"ENX"]),
2911            "-ERR ENX flag requires an expiration\r\n"
2912        );
2913        assert_eq!(
2914            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2915            "-ERR UBOUND is not an integer or out of range\r\n"
2916        );
2917        assert_eq!(
2918            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2919            "-ERR LBOUND can't be greater than UBOUND\r\n"
2920        );
2921        assert_eq!(
2922            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2923            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2924        );
2925    }
2926
2927    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
2928    /// key that is not there, which answers null without ever looking at the
2929    /// expiration it was given.
2930    #[test]
2931    fn the_expiry_rules_are_redis_own() {
2932        let mut f = Fixture::new();
2933        let bad = "-ERR invalid expire time in 'set' command\r\n";
2934        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2935        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2936        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2937        assert_eq!(
2938            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2939            bad
2940        );
2941        assert_eq!(
2942            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2943            "-ERR value is not an integer or out of range\r\n"
2944        );
2945        assert_eq!(
2946            f.run(&[b"SETEX", b"k", b"0", b"v"]),
2947            "-ERR invalid expire time in 'setex' command\r\n"
2948        );
2949        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2950        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2951        assert_eq!(
2952            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2953            "-ERR syntax error\r\n",
2954            "the option list is still checked before the key is looked up"
2955        );
2956        // A deadline in the past is accepted and the key goes with it.
2957        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2958        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2959        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2960    }
2961
2962    #[test]
2963    fn mset_takes_its_pairs_from_the_read_buffer() {
2964        let mut f = Fixture::new();
2965        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2966        assert_eq!(
2967            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2968            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2969        );
2970        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2971        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2972        assert_eq!(
2973            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2974            "-ERR wrong number of key-value pairs\r\n"
2975        );
2976        assert_eq!(
2977            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2978            "-ERR invalid numkeys value\r\n"
2979        );
2980        assert_eq!(
2981            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2982            "-ERR invalid numkeys value\r\n"
2983        );
2984    }
2985
2986    #[test]
2987    fn lcs_answers_the_length_the_string_and_the_runs() {
2988        let mut f = Fixture::new();
2989        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2990        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2991        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2992        assert_eq!(
2993            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2994            "*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"
2995        );
2996        // Without `IDX` the two options that only mean something with it are
2997        // accepted and ignored, which is what a real server does.
2998        assert_eq!(
2999            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3000            "$6\r\nmytext\r\n"
3001        );
3002    }
3003
3004    #[test]
3005    fn select_moves_the_connection_and_the_databases_stay_apart() {
3006        let mut f = Fixture::new();
3007        f.run(&[b"SET", b"k", b"zero"]);
3008        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3009        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3010        f.run(&[b"SET", b"k", b"four"]);
3011        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3012        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3013        assert_eq!(
3014            f.run(&[b"SELECT", b"99"]),
3015            "-ERR DB index is out of range\r\n"
3016        );
3017        assert_eq!(
3018            f.run(&[b"SELECT", b"-1"]),
3019            "-ERR DB index is out of range\r\n"
3020        );
3021        assert_eq!(
3022            f.run(&[b"SELECT", b"abc"]),
3023            "-ERR value is not an integer or out of range\r\n"
3024        );
3025        // `RESET` brings it back to zero.
3026        f.run(&[b"SELECT", b"4"]);
3027        f.run(&[b"RESET"]);
3028        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3029    }
3030
3031    #[test]
3032    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3033        let mut f = Fixture::new();
3034        let reply = f.run(&[b"HELLO"]);
3035        assert!(reply.starts_with("*14\r\n"), "{reply}");
3036        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3037        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3038        assert!(
3039            reply.contains(":7\r\n"),
3040            "the connection id is in there: {reply}"
3041        );
3042        assert_eq!(
3043            f.run(&[b"HELLO", b"4"]),
3044            "-NOPROTO unsupported protocol version\r\n"
3045        );
3046        assert_eq!(
3047            f.run(&[b"HELLO", b"abc"]),
3048            "-ERR Protocol version is not an integer or out of range\r\n"
3049        );
3050        assert_eq!(
3051            f.run(&[b"HELLO", b"3", b"SETNAME"]),
3052            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3053        );
3054        assert!(
3055            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3056                .starts_with("%7\r\n")
3057        );
3058        assert_eq!(f.session.name(), b"bob");
3059        f.run(&[b"RESET"]);
3060        assert_eq!(f.session.name(), b"");
3061    }
3062
3063    #[test]
3064    fn command_describes_this_server_in_the_shape_a_driver_reads() {
3065        let mut f = Fixture::new();
3066        let count = format!(":{}\r\n", COMMANDS.len());
3067        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3068        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3069        assert_eq!(
3070            info,
3071            "*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\
3072             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3073        );
3074        // A null in the list, and the plain one: `$-1` and not `*-1`.
3075        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3076        assert_eq!(
3077            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3078            "*1\r\n$8\r\ngetrange\r\n"
3079        );
3080        assert_eq!(
3081            f.run(&[b"COMMAND", b"NOPE"]),
3082            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3083        );
3084    }
3085
3086    /// A cluster aware client asks this question and then routes on the
3087    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
3088    /// that matters.
3089    #[test]
3090    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3091        let mut f = Fixture::new();
3092        assert_eq!(
3093            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3094            "*1\r\n$1\r\nk\r\n"
3095        );
3096        assert_eq!(
3097            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3098            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3099        );
3100        assert_eq!(
3101            f.run(&[
3102                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3103            ]),
3104            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3105        );
3106        assert_eq!(
3107            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3108            "-ERR The command has no key arguments\r\n"
3109        );
3110        assert_eq!(
3111            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3112            "-ERR Invalid number of arguments specified for command\r\n"
3113        );
3114    }
3115
3116    #[test]
3117    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3118        let mut f = Fixture::new();
3119        assert_eq!(
3120            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3121            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3122        );
3123        // A pattern matches more than one, and a setting two patterns both ask
3124        // for is still sent once.
3125        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3126        assert!(both.starts_with("*6\r\n"), "{both}");
3127        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3128        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3129        assert_eq!(
3130            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3131            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3132        );
3133        assert_eq!(
3134            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3135            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3136        );
3137        assert_eq!(
3138            f.run(&[b"CONFIG", b"GET"]),
3139            "-ERR wrong number of arguments for 'config|get' command\r\n"
3140        );
3141        // Too few arguments and an odd number of them are different
3142        // complaints, which is the sort of thing only the real server tells
3143        // you.
3144        assert_eq!(
3145            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3146            "-ERR wrong number of arguments for 'config|set' command\r\n"
3147        );
3148        assert_eq!(
3149            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3150            "-ERR syntax error\r\n"
3151        );
3152        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3153        assert_eq!(
3154            f.run(&[b"CONFIG", b"REWRITE"]),
3155            "-ERR The server is running without a config file\r\n"
3156        );
3157    }
3158
3159    #[test]
3160    fn the_eviction_policy_reads_back_what_was_written_to_it() {
3161        let mut f = Fixture::new();
3162        assert_eq!(
3163            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3164            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3165        );
3166        assert_eq!(
3167            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3168            "+OK\r\n",
3169            "the name is matched without regard to case, like every other one"
3170        );
3171        assert_eq!(
3172            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3173            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3174        );
3175        // And INFO agrees with CONFIG, which it did not when it was a literal.
3176        assert!(
3177            f.run(&[b"INFO", b"memory"])
3178                .contains("maxmemory_policy:allkeys-lfu"),
3179            "INFO and CONFIG disagree about the policy"
3180        );
3181        // The refusal names every legal value in the order the real server's
3182        // enum table lists them, because a client comparing the message compares
3183        // the whole string.
3184        assert_eq!(
3185            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3186            "-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"
3187        );
3188        // A bad pair leaves the good one in the same command alone, and the
3189        // policy is checked by the same pass that checks the numbers.
3190        assert_eq!(
3191            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3192            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3193        );
3194        f.run(&[
3195            b"CONFIG",
3196            b"SET",
3197            b"hash-max-listpack-entries",
3198            b"7",
3199            b"maxmemory-policy",
3200            b"nonsense",
3201        ]);
3202        assert_eq!(
3203            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3204            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3205        );
3206    }
3207
3208    #[test]
3209    fn the_three_eviction_numbers_read_back_too() {
3210        let mut f = Fixture::new();
3211        for (name, default, set) in [
3212            ("maxmemory-samples", "5", "12"),
3213            ("lfu-log-factor", "10", "3"),
3214            ("lfu-decay-time", "1", "60"),
3215        ] {
3216            let get = || {
3217                format!(
3218                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3219                    name.len(),
3220                    default.len()
3221                )
3222            };
3223            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3224            assert_eq!(
3225                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3226                "+OK\r\n"
3227            );
3228            assert_eq!(
3229                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3230                format!(
3231                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3232                    name.len(),
3233                    set.len()
3234                )
3235            );
3236            // A number that is not a number is refused with the same sentence
3237            // every other number gets, which names the setting the client typed.
3238            assert_eq!(
3239                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3240                format!(
3241                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3242                )
3243            );
3244        }
3245    }
3246
3247    #[test]
3248    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3249        let mut f = Fixture::new();
3250        assert_eq!(
3251            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3252            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3253            "no limit is the default"
3254        );
3255        // The pairing is Redis's and it is a trap: the bare letter is a power of
3256        // ten and the one with the b is a power of two.
3257        for (typed, bytes) in [
3258            (&b"1024"[..], "1024"),
3259            (b"1k", "1000"),
3260            (b"1kb", "1024"),
3261            (b"1M", "1000000"),
3262            (b"1Mb", "1048576"),
3263            (b"1gb", "1073741824"),
3264            (b"100mb", "104857600"),
3265        ] {
3266            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3267            assert_eq!(
3268                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3269                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3270                "set {}",
3271                String::from_utf8_lossy(typed)
3272            );
3273        }
3274        assert!(
3275            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3276            "the report agrees with the setting"
3277        );
3278
3279        // A unit nobody has heard of, and a negative number, which is not a very
3280        // large one however it is spelled.
3281        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3282            assert_eq!(
3283                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3284                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3285                "refused {}",
3286                String::from_utf8_lossy(bad)
3287            );
3288        }
3289        assert!(
3290            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3291            "and the refusal left the old one alone"
3292        );
3293    }
3294
3295    #[test]
3296    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3297        let mut f = Fixture::new();
3298        f.run(&[b"SET", b"here", b"already"]);
3299        // A byte, which is under what an empty server holds, so nothing this
3300        // command could do would get it under. The default policy is
3301        // `noeviction`, so nothing is what it does.
3302        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3303        assert_eq!(
3304            f.run(&[b"SET", b"k", b"v"]),
3305            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3306        );
3307        assert_eq!(
3308            f.run(&[b"LPUSH", b"l", b"v"]),
3309            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3310        );
3311        // Reading is allowed, and so is the one thing that would help.
3312        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3313        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3314        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3315
3316        // Taking the limit away lets the write through again.
3317        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3318        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3319    }
3320
3321    #[test]
3322    fn an_allkeys_policy_makes_room_instead_of_refusing() {
3323        let mut f = Fixture::new();
3324        let val = vec![b'v'; 256];
3325        for i in 0..24000u32 {
3326            let k = format!("key:{i:08}");
3327            f.run(&[b"SET", k.as_bytes(), &val]);
3328        }
3329        let full = f.server.memory_bytes();
3330        assert!(
3331            full > 3 * 1024 * 1024,
3332            "the arena is several segments: {full}"
3333        );
3334
3335        // Two megabytes under what it is holding, which is one segment's worth,
3336        // so getting there means giving a whole segment back and not just
3337        // dropping a few records.
3338        let limit = full - 2 * 1024 * 1024;
3339        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3340        f.run(&[
3341            b"CONFIG",
3342            b"SET",
3343            b"maxmemory",
3344            limit.to_string().as_bytes(),
3345        ]);
3346
3347        // Writes keep working the whole way down. The budget means one command
3348        // does not do it all, so this runs until the server has settled and
3349        // checks that nothing was refused on the way.
3350        for i in 0..2000u32 {
3351            let k = format!("new:{i:08}");
3352            assert_eq!(
3353                f.run(&[b"SET", k.as_bytes(), &val]),
3354                "+OK\r\n",
3355                "write {i} was refused"
3356            );
3357            f.server.refresh_memory();
3358            if f.server.memory_bytes() <= limit {
3359                break;
3360            }
3361        }
3362        assert!(
3363            f.server.memory_bytes() <= limit,
3364            "it never got under: {} against {limit}",
3365            f.server.memory_bytes()
3366        );
3367        let info = f.run(&[b"INFO", b"stats"]);
3368        assert!(!info.contains("evicted_keys:0"), "{info}");
3369        assert!(
3370            f.run(&[b"DBSIZE"]) != ":0\r\n",
3371            "and it did not empty the database to get there"
3372        );
3373    }
3374
3375    #[test]
3376    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3377        // The limit is judged against a number kept as the collections move,
3378        // rather than found by asking all of them, and the two have to be the
3379        // same number or the limit is enforced against a fiction. This does the
3380        // things that move it, which is growing a collection, shrinking one,
3381        // changing its representation, deleting it and reusing its slot, across
3382        // all five types, and checks the two against each other as it goes.
3383        let mut f = Fixture::new();
3384        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3385        let big = vec![b'v'; 200];
3386
3387        for i in 0..400u32 {
3388            let n = i.to_string();
3389            let n = n.as_bytes();
3390            f.run(&[b"SADD", b"s", n]);
3391            f.run(&[b"SADD", b"s2", &big]);
3392            f.run(&[b"HSET", b"h", n, &big]);
3393            f.run(&[b"RPUSH", b"l", &big]);
3394            f.run(&[b"ZADD", b"z", n, n]);
3395            f.run(&[b"ARSET", b"a", n, &big]);
3396            if i % 7 == 0 {
3397                f.run(&[b"SREM", b"s", n]);
3398                f.run(&[b"HDEL", b"h", n]);
3399                f.run(&[b"LPOP", b"l"]);
3400                f.run(&[b"ZREM", b"z", n]);
3401                f.run(&[b"ARDEL", b"a", n]);
3402            }
3403            if i % 53 == 0 {
3404                // Every type deleted and made again, so a slot goes on the free
3405                // list and comes back holding something else.
3406                f.run(&[b"DEL", b"s2"]);
3407            }
3408            assert_eq!(
3409                f.server.settled_memory(),
3410                f.server.memory_bytes(),
3411                "after round {i}"
3412            );
3413        }
3414
3415        // The run has to have built something, or the two numbers agreeing is
3416        // two zeroes agreeing.
3417        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3418        assert!(
3419            f.server.memory_bytes() > 512 * 1024,
3420            "{}",
3421            f.server.memory_bytes()
3422        );
3423
3424        // And it survives the collections going away entirely.
3425        f.run(&[b"FLUSHALL"]);
3426        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3427    }
3428
3429    #[test]
3430    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3431        // A server with no limit does not keep the running total, so setting a
3432        // limit on a database that is already full has to start it from a walk.
3433        // If it did not, the first reading would be zero and the server would
3434        // think it had all the room in the world.
3435        let mut f = Fixture::new();
3436        for i in 0..200u32 {
3437            let n = i.to_string();
3438            f.run(&[b"SADD", b"s", n.as_bytes()]);
3439            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3440        }
3441        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3442        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3443
3444        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3445        for i in 200..400u32 {
3446            let n = i.to_string();
3447            f.run(&[b"SADD", b"s", n.as_bytes()]);
3448        }
3449        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3450        assert_eq!(
3451            f.server.settled_memory(),
3452            f.server.memory_bytes(),
3453            "the writes it was not watching are in the number it started from"
3454        );
3455    }
3456
3457    #[test]
3458    fn evicted_keys_and_expired_keys_are_different_numbers() {
3459        let mut f = Fixture::new();
3460        // Nothing has been evicted and nothing can be under the default policy,
3461        // so this stays at zero while the other one moves.
3462        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3463        f.server.advance_clock_ms(20);
3464        f.run(&[b"GET", b"gone"]);
3465        let info = f.run(&[b"INFO", b"stats"]);
3466        assert!(info.contains("expired_keys:1"), "{info}");
3467        assert!(info.contains("evicted_keys:0"), "{info}");
3468    }
3469
3470    #[test]
3471    fn the_object_subcommands_follow_the_policy() {
3472        let mut f = Fixture::new();
3473        f.run(&[b"SET", b"s", b"v"]);
3474        // Under the default the clock is kept and the counter is not, and under
3475        // an LFU policy it is the other way round. Each subcommand refuses on
3476        // the side where its reading of the three bytes means nothing.
3477        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3478        assert!(
3479            f.run(&[b"OBJECT", b"FREQ", b"s"])
3480                .starts_with("-ERR An LFU maxmemory policy is not selected"),
3481        );
3482
3483        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3484        assert!(
3485            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3486                .starts_with("-ERR An LFU maxmemory policy is selected"),
3487        );
3488        // The key was written under a clock policy, so what comes back is that
3489        // clock read as a counter. It is a number and not an error, which is the
3490        // point: switching at runtime does not invalidate anything, it only makes
3491        // the old field mean something else until the key is used again.
3492        assert!(
3493            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3494            "FREQ should answer under an LFU policy"
3495        );
3496    }
3497
3498    #[test]
3499    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3500        let mut f = Fixture::new();
3501        f.run(&[b"SET", b"s", b"hello"]);
3502        f.run(&[b"SET", b"n", b"123"]);
3503        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3504        f.run(&[b"SADD", b"ss", b"a", b"b"]);
3505        f.run(&[b"HSET", b"h", b"f", b"v"]);
3506        for (key, want) in [
3507            (b"s".as_slice(), "embstr"),
3508            (b"n", "int"),
3509            (b"si", "intset"),
3510            (b"ss", "listpack"),
3511            (b"h", "listpack"),
3512        ] {
3513            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3514            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3515        }
3516
3517        // A field deadline widens the blob rather than promoting it, and this
3518        // is the only place a client can see that happen.
3519        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3520        assert_eq!(
3521            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3522            "$10\r\nlistpackex\r\n"
3523        );
3524
3525        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3526        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3527        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3528    }
3529
3530    #[test]
3531    fn object_answers_nil_for_a_key_that_is_not_there() {
3532        let mut f = Fixture::new();
3533        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3534            assert_eq!(
3535                f.run(&[b"OBJECT", sub, b"nokey"]),
3536                "$-1\r\n",
3537                "a nil and not an error, which is what 8.10.1 does"
3538            );
3539        }
3540        // And the key is looked up before FREQ has its complaint, so the
3541        // complaint only reaches a key that exists.
3542        f.run(&[b"SET", b"s", b"v"]);
3543        assert!(
3544            f.run(&[b"OBJECT", b"FREQ", b"s"])
3545                .starts_with("-ERR An LFU maxmemory policy is not"),
3546        );
3547        assert_eq!(
3548            f.run(&[b"OBJECT", b"NOPE", b"s"]),
3549            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3550        );
3551        assert_eq!(
3552            f.run(&[b"OBJECT", b"ENCODING"]),
3553            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3554        );
3555        assert_eq!(
3556            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3557            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3558        );
3559        assert_eq!(
3560            f.run(&[b"OBJECT"]),
3561            "-ERR wrong number of arguments for 'object' command\r\n"
3562        );
3563    }
3564
3565    #[test]
3566    fn config_moves_the_ladder_and_object_encoding_agrees() {
3567        let mut f = Fixture::new();
3568        assert_eq!(
3569            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3570            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3571            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3572        );
3573        // The old spelling is the same number under a different name, and a
3574        // glob that catches both sends both.
3575        assert_eq!(
3576            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3577            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3578        );
3579        assert!(
3580            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3581                .starts_with("*8\r\n")
3582        );
3583        assert!(
3584            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3585                .starts_with("*6\r\n")
3586        );
3587
3588        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3589        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3590
3591        assert_eq!(
3592            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3593            "+OK\r\n",
3594            "written under the old name and read back under the new one"
3595        );
3596        assert_eq!(
3597            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3598            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3599        );
3600        assert_eq!(
3601            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3602            "$8\r\nlistpack\r\n",
3603            "the hash that already exists is left exactly where it was"
3604        );
3605        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3606        assert_eq!(
3607            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3608            "$9\r\nhashtable\r\n",
3609            "and the next one built goes straight to a table"
3610        );
3611
3612        // The set has three of these and all three move.
3613        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3614        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3615        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3616        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3617        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3618        assert_eq!(
3619            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3620            "$9\r\nhashtable\r\n"
3621        );
3622    }
3623
3624    #[test]
3625    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3626        let mut f = Fixture::new();
3627        assert_eq!(
3628            f.run(&[
3629                b"CONFIG",
3630                b"SET",
3631                b"hash-max-listpack-entries",
3632                b"7",
3633                b"set-max-listpack-entries",
3634                b"abc"
3635            ]),
3636            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3637        );
3638        assert_eq!(
3639            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3640            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3641            "the pair in front of the bad one did not go in"
3642        );
3643        // The name in the complaint is the one that was typed, so the old
3644        // spelling comes back as the old spelling.
3645        assert_eq!(
3646            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3647            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3648        );
3649        assert_eq!(
3650            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3651            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3652        );
3653        // A number past what an i64 holds is the parse complaint and not the
3654        // range one, which is upstream reading it before it checks it.
3655        assert_eq!(
3656            f.run(&[
3657                b"CONFIG",
3658                b"SET",
3659                b"set-max-intset-entries",
3660                b"99999999999999999999"
3661            ]),
3662            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3663        );
3664        assert_eq!(
3665            f.run(&[
3666                b"CONFIG",
3667                b"SET",
3668                b"set-max-intset-entries",
3669                b"9223372036854775807"
3670            ]),
3671            "+OK\r\n"
3672        );
3673    }
3674
3675    #[test]
3676    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3677        let mut f = Fixture::new();
3678        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3679        f.run(&[b"SELECT", b"3"]);
3680        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3681        assert_eq!(
3682            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3683            "$9\r\nhashtable\r\n",
3684            "these are one server wide number in Redis, whatever a Keyspace carries"
3685        );
3686    }
3687
3688    #[test]
3689    fn info_reports_the_numbers_it_can_stand_behind() {
3690        let mut f = Fixture::new();
3691        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3692        let all = f.run(&[b"INFO"]);
3693        assert!(all.contains("redis_version:8.8.0"), "{all}");
3694        assert!(
3695            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3696            "{all}"
3697        );
3698        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3699        assert!(all.contains("role:master"), "{all}");
3700        // One section is one section.
3701        let clients = f.run(&[b"INFO", b"clients"]);
3702        assert!(clients.contains("connected_clients:0"), "{clients}");
3703        assert!(!clients.contains("redis_version"), "{clients}");
3704        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3705    }
3706
3707    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
3708    ///
3709    /// This is Redis's `unit/info-command` written against the fixture. Every
3710    /// assertion in it is one of theirs, in their order, and the two fields it
3711    /// turns on are the two that suite was failing on: `master_repl_offset`,
3712    /// which is in the default set, and `rejected_calls`, which is not.
3713    #[test]
3714    fn commandstats_is_asked_for_and_replication_is_not() {
3715        let mut f = Fixture::new();
3716        for arg in ["", "all", "default", "everything"] {
3717            let info = if arg.is_empty() {
3718                f.run(&[b"INFO"])
3719            } else {
3720                f.run(&[b"INFO", arg.as_bytes()])
3721            };
3722            assert!(info.contains("redis_version"), "{arg}: {info}");
3723            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3724            assert!(info.contains("used_memory"), "{arg}: {info}");
3725            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3726            let asked = arg == "all" || arg == "everything";
3727            assert_eq!(
3728                info.contains("rejected_calls"),
3729                asked,
3730                "{arg} should{} carry the command counters: {info}",
3731                if asked { "" } else { " not" }
3732            );
3733        }
3734
3735        let cpu = f.run(&[b"INFO", b"cpu"]);
3736        assert!(cpu.contains("used_cpu_user"), "{cpu}");
3737        assert!(!cpu.contains("used_memory"), "{cpu}");
3738
3739        // Their case, to make the point that a section name is not case
3740        // sensitive any more than a command name is.
3741        let stats = f.run(&[b"INFO", b"commandSTATS"]);
3742        assert!(!stats.contains("used_memory"), "{stats}");
3743        assert!(stats.contains("rejected_calls"), "{stats}");
3744
3745        // Two sections named, and neither of them pulls in a third.
3746        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3747        assert!(pair.contains("used_cpu_user"), "{pair}");
3748        assert!(!pair.contains("master_repl_offset"), "{pair}");
3749
3750        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3751        assert!(with_all.contains("used_memory"), "{with_all}");
3752        assert!(with_all.contains("master_repl_offset"), "{with_all}");
3753        assert!(with_all.contains("rejected_calls"), "{with_all}");
3754        // A section named twice is still written once.
3755        assert_eq!(
3756            with_all.matches("used_cpu_user_children").count(),
3757            1,
3758            "{with_all}"
3759        );
3760
3761        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3762        assert!(with_default.contains("used_memory"), "{with_default}");
3763        assert!(
3764            with_default.contains("master_repl_offset"),
3765            "{with_default}"
3766        );
3767        assert!(!with_default.contains("rejected_calls"), "{with_default}");
3768        assert_eq!(
3769            with_default.matches("used_cpu_user_children").count(),
3770            1,
3771            "{with_default}"
3772        );
3773    }
3774
3775    /// The memory section says what this process may use, not what the machine
3776    /// has.
3777    ///
3778    /// The distinction is the whole point of it. A server inside a container
3779    /// that reports the host's memory is a server whose operator sizes it for
3780    /// memory it will be killed for touching, so all three numbers are there:
3781    /// what the machine has, what the cgroup allows, and the quarter of the
3782    /// tighter one that pools are sized from.
3783    #[test]
3784    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3785        let mut f = Fixture::new();
3786        let info = f.run(&[b"INFO", b"memory"]);
3787        for field in [
3788            "total_system_memory:",
3789            "mem_cgroup_limit:",
3790            "mem_limit:",
3791            "mem_budget:",
3792        ] {
3793            assert!(info.contains(field), "no {field} in {info}");
3794        }
3795
3796        let field = |name: &str| -> u64 {
3797            info.lines()
3798                .find_map(|l| l.strip_prefix(name))
3799                .unwrap_or_else(|| panic!("no {name} in {info}"))
3800                .trim()
3801                .parse()
3802                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3803        };
3804        let limit = field("mem_limit:");
3805        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3806        // Zero means there is no limit to report, which is a real answer on a
3807        // machine with no cgroups and no way to ask how big it is.
3808        if limit != 0 {
3809            let host = field("total_system_memory:");
3810            let cgroup = field("mem_cgroup_limit:");
3811            assert!(
3812                limit == host || limit == cgroup,
3813                "the limit came from neither number: {info}"
3814            );
3815        }
3816    }
3817
3818    /// The three counters, each on the path that raises it.
3819    ///
3820    /// `calls` on a command that worked, `failed_calls` on one that ran and
3821    /// answered with an error, and `rejected_calls` on one that never ran at
3822    /// all. The last two are the pair that is easy to collapse into one number
3823    /// and that Redis keeps apart, because a client sending the wrong number of
3824    /// arguments and a client asking for a list element that is not there are
3825    /// not the same problem.
3826    #[test]
3827    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3828        let mut f = Fixture::new();
3829        f.run(&[b"SET", b"k", b"v"]);
3830        f.run(&[b"SET", b"k", b"w"]);
3831        // Ran, and answered with an error, because `k` is not a list.
3832        f.run(&[b"LPUSH", b"k", b"x"]);
3833        // Never ran: `LPUSH` takes at least three arguments.
3834        f.run(&[b"LPUSH", b"k"]);
3835
3836        let stats = f.run(&[b"INFO", b"commandstats"]);
3837        assert!(
3838            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3839            "{stats}"
3840        );
3841        assert!(
3842            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3843            "{stats}"
3844        );
3845        assert!(
3846            !stats.contains("cmdstat_zadd"),
3847            "a command nobody has sent has no row: {stats}"
3848        );
3849    }
3850
3851    /// A cache that writes with a deadline and never reads back used to hold
3852    /// every key it had ever written, because lazy expiry needs somebody to walk
3853    /// past a key before it can reclaim it and nobody ever did.
3854    #[test]
3855    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3856        let mut f = Fixture::new();
3857        for i in 0..3_000u32 {
3858            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3859        }
3860        for i in 0..1_000u32 {
3861            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3862        }
3863        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3864        f.advance(100);
3865        assert_eq!(
3866            f.run(&[b"DBSIZE"]),
3867            ":4000\r\n",
3868            "DBSIZE counts records and nothing has read past the dead ones yet"
3869        );
3870
3871        // What the shard loop does, one slice at a time.
3872        let mut spent = 0;
3873        for _ in 0..2_000 {
3874            spent += f.server.expire_step(4096);
3875            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3876                break;
3877            }
3878        }
3879        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3880        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3881        for i in 0..1_000u32 {
3882            assert_eq!(
3883                f.run(&[b"GET", format!("k{i}").as_bytes()]),
3884                "$1\r\nv\r\n",
3885                "it took a key that had no deadline"
3886            );
3887        }
3888    }
3889
3890    #[test]
3891    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3892        let mut f = Fixture::new();
3893        for i in 0..2_000u32 {
3894            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3895        }
3896        assert_eq!(f.server.expire_step(4096), 0);
3897        // And one database having them does not make the other fifteen pay.
3898        f.run(&[b"SELECT", b"3"]);
3899        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3900        f.advance(100);
3901        for _ in 0..64 {
3902            f.server.expire_step(4096);
3903        }
3904        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3905        f.run(&[b"SELECT", b"0"]);
3906        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3907        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3908    }
3909
3910    /// The gate, which is what stops a maintenance slice that runs every hundred
3911    /// nanoseconds from drawing a sample every hundred nanoseconds.
3912    #[test]
3913    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3914        let mut f = Fixture::new();
3915        for i in 0..500u32 {
3916            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3917        }
3918        f.advance(100);
3919        let at = f.server.striped(0).now_ms();
3920        f.server.set_clock_ms(at);
3921        // A small budget, so that one slice cannot finish the job and a second
3922        // one having nothing to do would mean the gate and not an empty
3923        // database.
3924        assert!(f.server.expire_slice(8) > 0, "the first one works");
3925        for _ in 0..1_000 {
3926            assert_eq!(
3927                f.server.expire_slice(8),
3928                0,
3929                "the millisecond has not moved and neither should this"
3930            );
3931        }
3932        assert!(
3933            f.server.striped(0).expires() > 400,
3934            "there is plenty left to take"
3935        );
3936        f.server.set_clock_ms(at + 1);
3937        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3938    }
3939
3940    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
3941    /// how much of a cache is volatile was reading a constant.
3942    #[test]
3943    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3944        let mut f = Fixture::new();
3945        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3946        assert!(
3947            f.run(&[b"INFO", b"keyspace"])
3948                .contains("db0:keys=3,expires=0"),
3949            "none of them has one yet"
3950        );
3951        f.run(&[b"EXPIRE", b"a", b"1000"]);
3952        f.run(&[b"EXPIRE", b"b", b"1000"]);
3953        let two = f.run(&[b"INFO", b"keyspace"]);
3954        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3955        f.run(&[b"PERSIST", b"a"]);
3956        f.run(&[b"DEL", b"b"]);
3957        let none = f.run(&[b"INFO", b"keyspace"]);
3958        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3959
3960        // Each database answers for itself, the way Redis reports it.
3961        f.run(&[b"SELECT", b"1"]);
3962        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3963        let both = f.run(&[b"INFO", b"keyspace"]);
3964        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3965        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3966    }
3967
3968    #[cfg(unix)]
3969    #[test]
3970    fn info_cpu_reports_processor_time_that_was_really_measured() {
3971        let mut f = Fixture::new();
3972        let cpu = f.run(&[b"INFO", b"cpu"]);
3973        assert!(cpu.contains("# CPU"), "{cpu}");
3974        // Redis's unit/info-command asks for this one by name in three tests.
3975        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3976        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3977        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3978        assert!(!cpu.contains("redis_version"), "{cpu}");
3979
3980        // It is a measurement and not a constant, so it goes up when work
3981        // happens. A tight loop rather than a sleep, because sleeping is the
3982        // one thing that does not move this number.
3983        let before = used_cpu_user(&cpu);
3984        let mut n = 0u64;
3985        let mut rounds = 0;
3986        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3987            for i in 0..1_000_000u64 {
3988                n = n.wrapping_add(i.wrapping_mul(i));
3989            }
3990            rounds += 1;
3991            // A bound rather than a spin, so a platform where this number does
3992            // not move fails here instead of hanging. Even a clock with whole
3993            // millisecond granularity gets there in the first round or two.
3994            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3995        }
3996    }
3997
3998    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
3999    #[cfg(unix)]
4000    fn used_cpu_user(info: &str) -> f64 {
4001        info.lines()
4002            .find_map(|l| l.strip_prefix("used_cpu_user:"))
4003            .expect("no used_cpu_user in the reply")
4004            .trim()
4005            .parse()
4006            .expect("used_cpu_user is not a number")
4007    }
4008
4009    /// The safety net under the rule that a body checks its arguments before
4010    /// it writes anything. `MGET` writes its array header first and then reads
4011    /// each key, so if a later argument could fail the header would already be
4012    /// out. Nothing in the string group does that today and this is what would
4013    /// catch the first one that did.
4014    #[test]
4015    fn a_command_that_fails_leaves_nothing_half_written() {
4016        let mut f = Fixture::new();
4017        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4018        assert_eq!(reply, "-ERR offset is out of range\r\n");
4019        assert!(!reply.contains(':'), "no integer went out in front of it");
4020    }
4021
4022    #[test]
4023    fn quit_answers_first_and_closes_after() {
4024        let mut f = Fixture::new();
4025        let (flow, reply) = f.flow(&[b"QUIT"]);
4026        assert_eq!(reply, "+OK\r\n");
4027        assert_eq!(flow, Flow::Close);
4028    }
4029
4030    /// A server that has not been asked to stop is not stopping, and one that
4031    /// has says so without writing anything back.
4032    ///
4033    /// The empty reply is the point. Redis answers nothing at all here and the
4034    /// client sees the socket close, and an `OK` would be a promise from a
4035    /// process that is about to not exist.
4036    #[test]
4037    fn shutdown_writes_nothing_and_sets_the_flag() {
4038        let mut f = Fixture::new();
4039        assert!(!f.server.stopping(), "nobody has asked yet");
4040
4041        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4042        assert_eq!(reply, "");
4043        assert_eq!(flow, Flow::Close);
4044        assert!(f.server.stopping());
4045    }
4046
4047    /// Every flag combination 8.10.1 takes, and every one it refuses.
4048    ///
4049    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
4050    /// contradict each other, `ABORT` says to do nothing so it cannot be
4051    /// combined with a word about how to do it, and repeating any one of them
4052    /// is fine. All of it was read off a running 8.10.1 rather than worked out
4053    /// from the documentation, which does not say.
4054    #[test]
4055    fn shutdown_takes_the_flags_redis_takes() {
4056        for flags in [
4057            &[b"NOSAVE".as_slice()][..],
4058            &[b"SAVE"],
4059            &[b"NOW"],
4060            &[b"FORCE"],
4061            &[b"nosave"],
4062            &[b"NOW", b"NOW"],
4063            &[b"SAVE", b"SAVE"],
4064            &[b"NOSAVE", b"NOW", b"FORCE"],
4065        ] {
4066            let mut f = Fixture::new();
4067            let mut parts = vec![b"SHUTDOWN".as_slice()];
4068            parts.extend_from_slice(flags);
4069            let (flow, reply) = f.flow(&parts);
4070            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4071            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4072            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4073        }
4074
4075        for flags in [
4076            &[b"BOGUS".as_slice()][..],
4077            &[b"SAVE", b"NOSAVE"],
4078            &[b"NOSAVE", b"SAVE"],
4079            &[b"ABORT", b"NOW"],
4080            &[b"NOSAVE", b"ABORT"],
4081            &[b"NOW", b"FORCE", b"ABORT"],
4082        ] {
4083            let mut f = Fixture::new();
4084            let mut parts = vec![b"SHUTDOWN".as_slice()];
4085            parts.extend_from_slice(flags);
4086            assert_eq!(
4087                f.run(&parts),
4088                "-ERR syntax error\r\n",
4089                "SHUTDOWN {flags:?} was accepted"
4090            );
4091            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4092        }
4093    }
4094
4095    /// `ABORT` has nothing to call off, ever.
4096    ///
4097    /// A shutdown here is decided and done inside one turn of the loop, so
4098    /// there is no window in which one is in progress. That makes Redis's
4099    /// message for a cancel with nothing to cancel the right answer every time
4100    /// rather than only when nothing happens to be pending. Two `ABORT`s is
4101    /// still one `ABORT`, which is what 8.10.1 does.
4102    #[test]
4103    fn shutdown_abort_never_has_anything_to_abort() {
4104        let mut f = Fixture::new();
4105        for parts in [
4106            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4107            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4108        ] {
4109            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4110            assert!(!f.server.stopping(), "an abort stopped the server");
4111        }
4112    }
4113
4114    /// A fixture whose server writes into a directory of its own.
4115    ///
4116    /// Every test here really writes files, because the whole point of the
4117    /// command is the files and a backup that is only a state machine would
4118    /// pass a test suite and fail the first person who tried to restore one.
4119    /// The directory carries the test's name so that the suite can run its
4120    /// tests in parallel the way it always does.
4121    struct Backups {
4122        f: Fixture,
4123        dir: PathBuf,
4124    }
4125
4126    impl Backups {
4127        fn new(name: &str) -> Backups {
4128            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4129            let _ = std::fs::remove_dir_all(&dir);
4130            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4131            let mut f = Fixture::new();
4132            f.server.set_dir(dir.clone());
4133            Backups { f, dir }
4134        }
4135
4136        fn run(&mut self, parts: &[&[u8]]) -> String {
4137            self.f.run(parts)
4138        }
4139
4140        /// The names in `backupdir`, sorted, so a test can say what is on disk.
4141        fn files(&self) -> Vec<String> {
4142            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4143                Ok(entries) => entries
4144                    .filter_map(|e| e.ok())
4145                    .map(|e| e.file_name().to_string_lossy().into_owned())
4146                    .collect(),
4147                Err(_) => Vec::new(),
4148            };
4149            names.sort();
4150            names
4151        }
4152
4153        fn read(&self, name: &str) -> Vec<u8> {
4154            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4155        }
4156    }
4157
4158    impl Drop for Backups {
4159        fn drop(&mut self) {
4160            let _ = std::fs::remove_dir_all(&self.dir);
4161        }
4162    }
4163
4164    /// The four states and the moves between them, in the order a client walks
4165    /// them, with the files checked at every step.
4166    #[test]
4167    fn backup_walks_the_states_the_reference_walks() {
4168        let mut b = Backups::new("states");
4169        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4170
4171        assert!(status(&mut b).contains("idle"));
4172        assert!(b.files().is_empty(), "an idle server has written a backup");
4173
4174        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4175        assert!(status(&mut b).contains("incrementing"));
4176        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4177
4178        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4179        assert!(status(&mut b).contains("sealed"));
4180        assert_eq!(
4181            b.files(),
4182            [
4183                "appendonly.aof.1.base.rdb",
4184                "appendonly.aof.1.incr.aof",
4185                "appendonly.aof.manifest",
4186            ]
4187        );
4188
4189        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4190        assert!(status(&mut b).contains("idle"));
4191        assert!(b.files().is_empty(), "cleanup left something behind");
4192    }
4193
4194    /// Every move that is refused, in the reference's words.
4195    #[test]
4196    fn backup_refuses_the_moves_the_reference_refuses() {
4197        let mut b = Backups::new("refusals");
4198
4199        assert_eq!(
4200            b.run(&[b"BACKUP", b"SEAL"]),
4201            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4202        );
4203        assert_eq!(
4204            b.run(&[b"BACKUP", b"ABORT"]),
4205            "-ERR No backup in progress\r\n"
4206        );
4207        // Cleanup from idle is not an error, it is a way of saying there was
4208        // nothing to clean up.
4209        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4210
4211        b.run(&[b"BACKUP", b"START"]);
4212        assert_eq!(
4213            b.run(&[b"BACKUP", b"START"]),
4214            "-ERR A backup is already in progress, ABORT it first\r\n"
4215        );
4216        assert_eq!(
4217            b.run(&[b"BACKUP", b"CLEANUP"]),
4218            "-ERR Backup is in progress\r\n"
4219        );
4220
4221        b.run(&[b"BACKUP", b"SEAL"]);
4222        assert_eq!(
4223            b.run(&[b"BACKUP", b"START"]),
4224            "-ERR A sealed backup exists, CLEANUP it first\r\n"
4225        );
4226        assert_eq!(
4227            b.run(&[b"BACKUP", b"SEAL"]),
4228            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4229        );
4230        assert_eq!(
4231            b.run(&[b"BACKUP", b"ABORT"]),
4232            "-ERR No backup in progress\r\n"
4233        );
4234    }
4235
4236    /// An abort takes the base file away and leaves a state saying who did it.
4237    ///
4238    /// The next backup takes the next sequence number rather than reusing the
4239    /// one whose files were just thrown away, so a directory somebody copied a
4240    /// half finished backup out of cannot end up with two different files under
4241    /// one name.
4242    #[test]
4243    fn backup_abort_removes_the_file_and_says_who_did_it() {
4244        let mut b = Backups::new("abort");
4245        b.run(&[b"BACKUP", b"START"]);
4246        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4247
4248        let status = b.run(&[b"BACKUP", b"STATUS"]);
4249        assert!(status.contains("failed"), "{status}");
4250        assert!(status.contains("aborted by user"), "{status}");
4251        assert!(b.files().is_empty(), "abort left the base file behind");
4252        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4253
4254        // A start from failed works, and is the second backup.
4255        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4256        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4257        let status = b.run(&[b"BACKUP", b"STATUS"]);
4258        assert!(status.contains("incrementing"), "{status}");
4259        assert!(!status.contains("aborted"), "the old error was kept");
4260    }
4261
4262    /// `LIST` names nothing, then one file, then three, and they are absolute.
4263    #[test]
4264    fn backup_list_names_the_files_that_are_pinned_so_far() {
4265        let mut b = Backups::new("list");
4266        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4267
4268        b.run(&[b"BACKUP", b"START"]);
4269        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4270        let base = base.to_string_lossy().into_owned();
4271        assert_eq!(
4272            b.run(&[b"BACKUP", b"LIST"]),
4273            format!("*1\r\n${}\r\n{base}\r\n", base.len())
4274        );
4275
4276        b.run(&[b"BACKUP", b"SEAL"]);
4277        let listed = b.run(&[b"BACKUP", b"LIST"]);
4278        assert!(listed.starts_with("*3\r\n"), "{listed}");
4279        // The order is the manifest's order, base then incremental then the
4280        // manifest itself, which is the order a restore needs them in.
4281        let names: Vec<&str> = listed
4282            .lines()
4283            .filter(|l| l.starts_with('/') || l.contains(":\\"))
4284            .collect();
4285        assert_eq!(names.len(), 3, "{listed}");
4286        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4287        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4288        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4289    }
4290
4291    /// The base file is the dataset as it was at `START` and not at `SEAL`.
4292    ///
4293    /// That is D-46 and it is the one thing about this a client can notice, so
4294    /// it is pinned here rather than left to be discovered by whoever restores
4295    /// one. The incremental file is empty for the same reason: there is no
4296    /// append only log underneath this server to copy the writes in between out
4297    /// of.
4298    #[test]
4299    fn a_backup_holds_the_dataset_as_it_was_at_start() {
4300        let mut b = Backups::new("contents");
4301        b.run(&[b"SET", b"bk", b"v1"]);
4302        b.run(&[b"BACKUP", b"START"]);
4303        b.run(&[b"SET", b"bk", b"v2"]);
4304        b.run(&[b"BACKUP", b"SEAL"]);
4305
4306        let base = b.read("appendonly.aof.1.base.rdb");
4307        assert!(base.starts_with(b"REDIS"), "not an RDB file");
4308        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4309        assert!(
4310            !base.windows(2).any(|w| w == b"v2"),
4311            "the base file moved on after START"
4312        );
4313        // The aux field a loader acts on, and the one that says this file is
4314        // the base of an append only file rather than a standalone dump. Its
4315        // value is the one byte string 1, which the encoder writes as an
4316        // integer the way a real server writes it.
4317        let at = base
4318            .windows(8)
4319            .position(|w| w == b"aof-base")
4320            .expect("no aof-base aux field");
4321        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4322
4323        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4324        assert_eq!(
4325            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4326            "file appendonly.aof.1.base.rdb seq 1 type b\n\
4327             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4328        );
4329    }
4330
4331    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
4332    /// RESP2, which is what every other map shaped reply in this server does.
4333    #[test]
4334    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4335        let mut b = Backups::new("status");
4336        b.f.server.set_clock_ms(1_700_000_000_000);
4337
4338        assert_eq!(
4339            b.run(&[b"BACKUP", b"STATUS"]),
4340            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4341             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4342        );
4343
4344        b.f.out = Out::new(Proto::Resp3);
4345        b.run(&[b"BACKUP", b"START"]);
4346        assert_eq!(
4347            b.run(&[b"BACKUP", b"STATUS"]),
4348            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4349             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4350        );
4351
4352        b.run(&[b"BACKUP", b"SEAL"]);
4353        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4354        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4355    }
4356
4357    /// A sealed backup that nobody cleans up goes away on its own once
4358    /// `backup-sealed-ttl` seconds have passed since the seal.
4359    #[test]
4360    fn a_sealed_backup_is_swept_away_after_the_timeout() {
4361        let mut b = Backups::new("ttl");
4362        b.f.server.set_clock_ms(1_000_000);
4363        assert_eq!(
4364            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4365            "+OK\r\n"
4366        );
4367        b.run(&[b"BACKUP", b"START"]);
4368        b.run(&[b"BACKUP", b"SEAL"]);
4369
4370        // A minute short of the deadline, nothing happens.
4371        b.f.server.set_clock_ms(1_000_000 + 59_000);
4372        b.f.server.backup_expire();
4373        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4374        assert_eq!(b.files().len(), 3);
4375
4376        b.f.server.set_clock_ms(1_000_000 + 60_000);
4377        b.f.server.backup_expire();
4378        let status = b.run(&[b"BACKUP", b"STATUS"]);
4379        assert!(status.contains("idle"), "{status}");
4380        assert!(b.files().is_empty(), "the timeout left the files behind");
4381
4382        // Zero is the default and means a sealed backup is kept for ever.
4383        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4384        b.run(&[b"BACKUP", b"START"]);
4385        b.run(&[b"BACKUP", b"SEAL"]);
4386        b.f.server.set_clock_ms(9_000_000_000);
4387        b.f.server.backup_expire();
4388        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4389    }
4390
4391    /// The three settings around the command, read and written the way 8.10.1
4392    /// reads and writes them.
4393    #[test]
4394    fn the_backup_settings_behave_the_way_the_reference_does() {
4395        let mut b = Backups::new("config");
4396        let dir = b.dir.to_string_lossy().into_owned();
4397
4398        assert_eq!(
4399            b.run(&[b"CONFIG", b"GET", b"dir"]),
4400            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4401        );
4402        assert_eq!(
4403            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4404            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4405        );
4406        assert_eq!(
4407            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4408            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4409        );
4410
4411        // `dir` is a protected config, so it is refused even for the value it
4412        // already holds, and `backupdirname` is immutable.
4413        assert_eq!(
4414            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4415            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4416        );
4417        assert_eq!(
4418            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4419            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4420        );
4421        assert!(
4422            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4423                .contains("argument couldn't be parsed into an integer")
4424        );
4425        assert!(
4426            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4427                .contains("argument must be between 0 and 9223372036854775807 inclusive")
4428        );
4429    }
4430
4431    /// The help text, which has `HELP` in it twice because the reference's does.
4432    #[test]
4433    fn backup_help_is_the_text_the_reference_sends() {
4434        let mut f = Fixture::new();
4435        let help = f.run(&[b"BACKUP", b"HELP"]);
4436        assert!(help.starts_with("*17\r\n"), "{help}");
4437        assert!(
4438            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4439        );
4440        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
4441        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
4442        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
4443    }
4444
4445    /// What a mistyped `BACKUP` gets told.
4446    ///
4447    /// The arity error names `backup` where the reference names `backup|start`,
4448    /// which is D-46: the table reports one arity for the container the way the
4449    /// reference does, and the per subcommand table that would carry the better
4450    /// name is not built yet. Every subcommand is exactly two words, so nothing
4451    /// legal is refused by it.
4452    #[test]
4453    fn backup_refuses_what_it_cannot_read() {
4454        let mut f = Fixture::new();
4455        assert_eq!(
4456            f.run(&[b"BACKUP"]),
4457            "-ERR wrong number of arguments for 'backup' command\r\n"
4458        );
4459        assert_eq!(
4460            f.run(&[b"BACKUP", b"START", b"x"]),
4461            "-ERR wrong number of arguments for 'backup' command\r\n"
4462        );
4463        assert_eq!(
4464            f.run(&[b"BACKUP", b"NOPE"]),
4465            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4466        );
4467    }
4468
4469    #[test]
4470    fn the_command_counter_counts_every_command_including_the_bad_ones() {
4471        let mut f = Fixture::new();
4472        f.run(&[b"PING"]);
4473        f.run(&[b"NOPE"]);
4474        f.run(&[b"GET"]);
4475        assert_eq!(f.server.stats.commands, 3);
4476    }
4477
4478    #[test]
4479    fn a_set_goes_from_bytes_to_bytes() {
4480        let mut f = Fixture::new();
4481        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4482        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4483        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4484        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4485        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4486        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4487        assert_eq!(
4488            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4489            "*3\r\n:1\r\n:0\r\n:1\r\n"
4490        );
4491        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4492        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4493    }
4494
4495    #[test]
4496    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4497        let mut f = Fixture::new();
4498        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4499        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4500        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4501        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4502        assert_eq!(
4503            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4504            "*2\r\n:0\r\n:0\r\n"
4505        );
4506        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4507    }
4508
4509    #[test]
4510    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4511        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
4512        // and one that gets a `*` hands it a list, without either of them being
4513        // told which command was sent.
4514        let mut f = Fixture::new();
4515        f.run(&[b"SADD", b"s", b"one"]);
4516        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4517
4518        f.run(&[b"HELLO", b"3"]);
4519        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4520    }
4521
4522    #[test]
4523    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4524        // An intset holds the number, so these digits exist for the first time
4525        // in the reply buffer.
4526        let mut f = Fixture::new();
4527        f.run(&[b"SADD", b"s", b"42"]);
4528        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4529        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4530        assert_eq!(
4531            f.run(&[b"SISMEMBER", b"s", b"042"]),
4532            ":0\r\n",
4533            "the member is the bytes and not the number they parse to"
4534        );
4535    }
4536
4537    #[test]
4538    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4539        let mut f = Fixture::new();
4540        f.run(&[b"SET", b"str", b"v"]);
4541        f.run(&[b"SADD", b"set", b"a"]);
4542
4543        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4544        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4545        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4546        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4547        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4548        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4549        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4550        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4551        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4552
4553        // MGET is the one that does not, because Redis gives nil for the odd
4554        // key out rather than failing the good keys next to it.
4555        assert_eq!(
4556            f.run(&[b"MGET", b"str", b"set", b"nope"]),
4557            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4558        );
4559        // And plain SET overwrites any type, which takes the body with it.
4560        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4561        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4562    }
4563
4564    #[test]
4565    fn a_wrongtype_leaves_nothing_half_written() {
4566        // SMISMEMBER writes an array header and then one reply per member, so
4567        // it is the first command in the server that could get a header out in
4568        // front of an error if it checked its key in the wrong order.
4569        let mut f = Fixture::new();
4570        f.run(&[b"SET", b"k", b"v"]);
4571        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4572        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4573        assert!(!reply.contains('*'), "an array header went out in front");
4574    }
4575
4576    #[test]
4577    fn emptying_a_set_takes_the_key_with_it() {
4578        let mut f = Fixture::new();
4579        f.run(&[b"SADD", b"s", b"a", b"b"]);
4580        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4581        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4582        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4583        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4584        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4585    }
4586
4587    /// Pull the cursor and the members out of one `SSCAN` reply.
4588    ///
4589    /// Crude on purpose. A test that walked a set through a real client would
4590    /// be testing the client, and what these tests are about is the shape of
4591    /// the bytes and the fact that a walk sees every member once.
4592    fn split_scan(reply: &str) -> (String, Vec<String>) {
4593        let mut lines = reply.split("\r\n");
4594        assert_eq!(lines.next(), Some("*2"), "got {reply}");
4595        lines.next().expect("the cursor header");
4596        let cursor = lines.next().expect("the cursor").to_owned();
4597        let header = lines.next().expect("the member header");
4598        let n: usize = header[1..].parse().expect("a member count");
4599        let mut members = Vec::with_capacity(n);
4600        for _ in 0..n {
4601            lines.next().expect("a member header");
4602            members.push(lines.next().expect("a member").to_owned());
4603        }
4604        (cursor, members)
4605    }
4606
4607    #[test]
4608    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
4609        let mut f = Fixture::new();
4610        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
4611
4612        let one = f.run(&[b"SPOP", b"s"]);
4613        assert!(
4614            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
4615            "got {one}"
4616        );
4617        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4618
4619        // A count takes that many, and the last one takes the key with it.
4620        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
4621        assert!(rest.starts_with("*3\r\n"), "got {rest}");
4622        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4623        // And a pop at a key that is not there is a nil, not an empty bulk.
4624        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
4625        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
4626    }
4627
4628    #[test]
4629    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
4630        // The one place in the server where the reply type carries something
4631        // the command name does not. SPOP's members are distinct so a RESP3
4632        // client can build a set out of them. SRANDMEMBER with a negative count
4633        // can hand back the same member three times, and a set would lose two.
4634        let mut f = Fixture::new();
4635        f.run(&[b"HELLO", b"3"]);
4636        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
4637
4638        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
4639        // And a positive count is an array too, since Redis makes it one.
4640        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4641
4642        // A negative count against a set of one is where the difference bites:
4643        // the same member three times, which is a three element reply and would
4644        // have been a one element reply if it had gone out as a set.
4645        f.run(&[b"SADD", b"one", b"z"]);
4646        assert_eq!(
4647            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
4648            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
4649        );
4650    }
4651
4652    #[test]
4653    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
4654        let mut f = Fixture::new();
4655        f.run(&[b"SADD", b"s", b"only"]);
4656        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4657        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4658        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
4659
4660        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
4661        // The count form answers an empty array rather than a nil, which is the
4662        // pair of answers Redis gives and is not the pair it looks like.
4663        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
4664        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
4665        // Asking for more than is there answers all of it once and not padding.
4666        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
4667    }
4668
4669    #[test]
4670    fn a_pop_count_that_is_not_a_positive_number_says_so() {
4671        let mut f = Fixture::new();
4672        f.run(&[b"SADD", b"s", b"a"]);
4673        let bad = "-ERR value is out of range, must be positive\r\n";
4674        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
4675        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
4676        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
4677        // Zero is allowed and is a real answer rather than an error.
4678        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
4679        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
4680    }
4681
4682    #[test]
4683    fn a_scan_walks_a_set_of_any_size_exactly_once() {
4684        let mut f = Fixture::new();
4685        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
4686        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
4687            .into_iter()
4688            .chain(members.iter().map(Vec::as_slice))
4689            .collect();
4690        f.run(&args);
4691
4692        let mut seen = Vec::new();
4693        let mut cursor = "0".to_owned();
4694        loop {
4695            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
4696            let (next, got) = split_scan(&reply);
4697            seen.extend(got);
4698            cursor = next;
4699            if cursor == "0" {
4700                break;
4701            }
4702        }
4703        seen.sort();
4704        seen.dedup();
4705        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
4706
4707        // A set small enough to be a listpack answers in one call whatever
4708        // cursor it was handed, which is what Redis does for that encoding.
4709        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
4710        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
4711        assert_eq!(cursor, "0");
4712        assert_eq!(got.len(), 3);
4713        // And a key that is not there is a finished scan of nothing.
4714        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
4715    }
4716
4717    #[test]
4718    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
4719        let mut f = Fixture::new();
4720        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
4721
4722        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
4723        let mut got = got;
4724        got.sort();
4725        assert_eq!(got, ["aa", "ab"]);
4726
4727        // An integer member has no digits stored anywhere, so MATCH is the one
4728        // place a scan pays to write some.
4729        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4730        let mut got = got;
4731        got.sort();
4732        assert_eq!(got, ["12", "13"]);
4733
4734        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4735        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4736        assert_eq!(
4737            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4738            "-ERR syntax error\r\n"
4739        );
4740        // A count under one is a syntax error and not a range error, which is
4741        // the odder of Redis's two answers and the reason it is copied exactly.
4742        assert_eq!(
4743            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4744            "-ERR syntax error\r\n"
4745        );
4746    }
4747
4748    #[test]
4749    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4750        let mut f = Fixture::new();
4751        f.run(&[b"SADD", b"src", b"a", b"b"]);
4752        f.run(&[b"SADD", b"dst", b"c"]);
4753
4754        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4755        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4756        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4757        // A member that is not in the source is a zero and moves nothing.
4758        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4759        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4760
4761        // A destination that does not exist gets made, and a source that runs
4762        // out goes away.
4763        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4764        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4765        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4766    }
4767
4768    #[test]
4769    fn moving_checks_the_types_in_the_order_redis_checks_them() {
4770        // Not the order it looks like it should be. A source that is not there
4771        // answers zero without ever looking at the destination, so this is a
4772        // zero and not a WRONGTYPE even though the destination is a string.
4773        let mut f = Fixture::new();
4774        f.run(&[b"SET", b"str", b"v"]);
4775        f.run(&[b"SADD", b"set", b"a"]);
4776
4777        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4778        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4779        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4780        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4781        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4782        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4783        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4784        assert_eq!(
4785            f.run(&[b"SISMEMBER", b"set", b"a"]),
4786            ":1\r\n",
4787            "and none of that moved anything"
4788        );
4789    }
4790
4791    #[test]
4792    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4793        // SSCAN writes an outer array header before it walks, so it is the
4794        // command most likely to get bytes out in front of an error.
4795        let mut f = Fixture::new();
4796        f.run(&[b"SADD", b"s", b"a"]);
4797        for bad in [
4798            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4799            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4800            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4801        ] {
4802            let reply = f.run(bad);
4803            assert!(reply.starts_with("-ERR"), "got {reply}");
4804            assert!(!reply.contains('*'), "an array header went out in front");
4805        }
4806    }
4807
4808    #[test]
4809    fn a_hash_writes_reads_and_deletes_its_fields() {
4810        let mut f = Fixture::new();
4811        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4812        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4813        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4814        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4815        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4816        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4817        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4818        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4819        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4820        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4821
4822        // The value the client sent is `9`, so HGET h b must not find the `2`
4823        // that is a value. A search with a step of one would have.
4824        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4825
4826        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4827        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4828        assert_eq!(
4829            f.run(&[b"EXISTS", b"h"]),
4830            ":0\r\n",
4831            "and losing the last field lost the key"
4832        );
4833    }
4834
4835    #[test]
4836    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4837        let mut f = Fixture::new();
4838        f.run(&[b"HSET", b"h", b"a", b"1"]);
4839        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4840        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4841        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4842        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4843        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4844
4845        f.run(&[b"HELLO", b"3"]);
4846        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4847        assert_eq!(
4848            f.run(&[b"HGETALL", b"nokey"]),
4849            "%0\r\n",
4850            "a missing key is the empty hash and never a nil"
4851        );
4852        assert_eq!(
4853            f.run(&[b"HKEYS", b"h"]),
4854            "*1\r\n$1\r\na\r\n",
4855            "and the two that answer one side stay arrays"
4856        );
4857    }
4858
4859    #[test]
4860    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4861        let mut f = Fixture::new();
4862        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4863        assert_eq!(
4864            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4865            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4866            "the reply is positional, so b is a nil and not a gap"
4867        );
4868        assert_eq!(
4869            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4870            "*2\r\n$-1\r\n$-1\r\n",
4871            "and a missing key is all nils rather than an empty array"
4872        );
4873
4874        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4875        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4876        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4877    }
4878
4879    #[test]
4880    fn a_hash_counts_up_and_says_so_when_it_cannot() {
4881        let mut f = Fixture::new();
4882        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4883        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4884        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4885        assert_eq!(
4886            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4887            "$4\r\n10.5\r\n",
4888            "a bulk string and not a double, on both protocols"
4889        );
4890
4891        f.run(&[b"HSET", b"h", b"s", b"words"]);
4892        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4893        assert!(
4894            bad.starts_with("-ERR hash value is not an integer"),
4895            "{bad}"
4896        );
4897        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4898        assert!(
4899            bad.starts_with("-ERR value is not an integer"),
4900            "a bad argument is not yet a hash value, {bad}"
4901        );
4902        assert_eq!(
4903            f.run(&[b"HGET", b"h", b"s"]),
4904            "$5\r\nwords\r\n",
4905            "and neither of them wrote anything"
4906        );
4907    }
4908
4909    #[test]
4910    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4911        let mut f = Fixture::new();
4912        for i in 0..500 {
4913            let field = format!("field-{i}");
4914            let value = format!("value-{i}");
4915            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4916        }
4917
4918        let mut seen: Vec<String> = Vec::new();
4919        let mut cursor = "0".to_owned();
4920        loop {
4921            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4922            let (next, items) = scan_reply(&reply);
4923            assert_eq!(items.len() % 2, 0, "a pair went out half written");
4924            for pair in items.chunks(2) {
4925                assert_eq!(
4926                    pair[0].strip_prefix("field-"),
4927                    pair[1].strip_prefix("value-"),
4928                    "a field came back with someone else's value"
4929                );
4930                seen.push(pair[0].clone());
4931            }
4932            cursor = next;
4933            if cursor == "0" {
4934                break;
4935            }
4936        }
4937        seen.sort();
4938        seen.dedup();
4939        assert_eq!(seen.len(), 500, "every field once and only once");
4940
4941        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4942        assert!(
4943            items.iter().all(|s| s.starts_with("field-")),
4944            "NOVALUES still sent the values"
4945        );
4946
4947        let (_, one) = scan_reply(&f.run(&[
4948            b"HSCAN",
4949            b"h",
4950            b"0",
4951            b"MATCH",
4952            b"field-499",
4953            b"COUNT",
4954            b"1000",
4955        ]));
4956        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4957    }
4958
4959    #[test]
4960    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4961        let mut f = Fixture::new();
4962        f.run(&[b"HSET", b"h", b"a", b"1"]);
4963        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4964        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4965        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4966        assert_eq!(
4967            f.run(&[b"HRANDFIELD", b"h", b"3"]),
4968            "*1\r\n$1\r\na\r\n",
4969            "a positive count is capped at the size of the hash"
4970        );
4971        assert_eq!(
4972            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4973            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4974            "and a negative one repeats itself"
4975        );
4976        assert_eq!(
4977            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4978            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4979            "flat on RESP2"
4980        );
4981
4982        f.run(&[b"HELLO", b"3"]);
4983        assert_eq!(
4984            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4985            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4986            "and nested on RESP3, but still an array and never a map"
4987        );
4988    }
4989
4990    #[test]
4991    fn every_hash_command_says_wrongtype_and_writes_nothing() {
4992        let mut f = Fixture::new();
4993        f.run(&[b"SET", b"str", b"v"]);
4994        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4995
4996        for cmd in [
4997            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
4998            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
4999            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5000            &[b"HGET".as_slice(), b"str", b"f"][..],
5001            &[b"HMGET".as_slice(), b"str", b"f"][..],
5002            &[b"HDEL".as_slice(), b"str", b"f"][..],
5003            &[b"HLEN".as_slice(), b"str"][..],
5004            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5005            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5006            &[b"HGETALL".as_slice(), b"str"][..],
5007            &[b"HKEYS".as_slice(), b"str"][..],
5008            &[b"HVALS".as_slice(), b"str"][..],
5009            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5010            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5011            &[b"HRANDFIELD".as_slice(), b"str"][..],
5012            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5013            &[b"HSCAN".as_slice(), b"str", b"0"][..],
5014        ] {
5015            let reply = f.run(cmd);
5016            assert_eq!(reply, wrong, "{:?}", cmd[0]);
5017        }
5018        assert_eq!(
5019            f.run(&[b"GET", b"str"]),
5020            "$1\r\nv\r\n",
5021            "and none of them touched the value"
5022        );
5023    }
5024
5025    #[test]
5026    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5027        let mut f = Fixture::new();
5028        f.run(&[b"HSET", b"h", b"f", b"v"]);
5029        for bad in [
5030            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5031            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5032            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5033            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5034        ] {
5035            let reply = f.run(bad);
5036            assert!(reply.starts_with("-ERR"), "got {reply}");
5037            assert!(!reply.contains('*'), "an array header went out in front");
5038        }
5039    }
5040
5041    #[test]
5042    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5043        let mut f = Fixture::new();
5044        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5045        assert_eq!(
5046            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5047            "*1\r\n:1\r\n"
5048        );
5049        assert_eq!(
5050            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5051            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5052            "one answer per field, and the two sentinels are TTL's own"
5053        );
5054
5055        // The same deadline in the other three units, all of them derived from
5056        // the one number the store kept.
5057        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5058        assert!((99_000..=100_000).contains(&ms), "got {ms}");
5059        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5060        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5061        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5062        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5063
5064        assert_eq!(
5065            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5066            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5067            "one for the deadline taken off, and it does not say what it was"
5068        );
5069        assert_eq!(
5070            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5071            "*1\r\n:-1\r\n"
5072        );
5073        assert_eq!(
5074            f.run(&[b"HGET", b"h", b"a"]),
5075            "$1\r\n1\r\n",
5076            "and the field is still there with the value it had"
5077        );
5078    }
5079
5080    #[test]
5081    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5082        let mut f = Fixture::new();
5083        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5084        assert_eq!(
5085            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5086            "*1\r\n:2\r\n",
5087            "two, and not one, because nothing was stored"
5088        );
5089        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5090        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5091
5092        assert_eq!(
5093            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5094            "*1\r\n:2\r\n"
5095        );
5096        assert_eq!(
5097            f.run(&[b"EXISTS", b"h"]),
5098            ":0\r\n",
5099            "and the last field going took the key with it"
5100        );
5101
5102        // Zero is a delete and not an error, where minus one is an error. That
5103        // is Redis's split and it is easy to get backwards.
5104        f.run(&[b"HSET", b"h", b"a", b"1"]);
5105        assert_eq!(
5106            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5107            "*1\r\n:2\r\n"
5108        );
5109    }
5110
5111    #[test]
5112    fn a_field_is_gone_once_its_moment_passes() {
5113        let mut f = Fixture::new();
5114        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5115        assert_eq!(
5116            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5117            "*1\r\n:1\r\n"
5118        );
5119        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5120
5121        // Time moves once per turn of the event loop and nowhere else, so a
5122        // test moves it by hand rather than by sleeping. There is nothing to
5123        // sleep for: the deadline is a number and so is the clock.
5124        f.server.advance_clock_ms(60);
5125        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5126        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5127        assert_eq!(
5128            f.run(&[b"HGETALL", b"h"]),
5129            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5130            "and the walks do not hand back a field that has expired"
5131        );
5132    }
5133
5134    #[test]
5135    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5136        let mut f = Fixture::new();
5137        for cmd in [
5138            &[
5139                b"HEXPIRE".as_slice(),
5140                b"nokey",
5141                b"100",
5142                b"FIELDS",
5143                b"2",
5144                b"a",
5145                b"b",
5146            ][..],
5147            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5148            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5149            &[
5150                b"HEXPIRETIME".as_slice(),
5151                b"nokey",
5152                b"FIELDS",
5153                b"2",
5154                b"a",
5155                b"b",
5156            ][..],
5157            &[
5158                b"HPERSIST".as_slice(),
5159                b"nokey",
5160                b"FIELDS",
5161                b"2",
5162                b"a",
5163                b"b",
5164            ][..],
5165        ] {
5166            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5167        }
5168    }
5169
5170    #[test]
5171    fn writing_a_field_clears_the_deadline_that_was_on_it() {
5172        let mut f = Fixture::new();
5173        f.run(&[b"HSET", b"h", b"a", b"1"]);
5174        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5175        f.run(&[b"HSET", b"h", b"a", b"2"]);
5176        assert_eq!(
5177            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5178            "*1\r\n:-1\r\n",
5179            "Redis has done this since 7.4, and it is why HGETEX exists"
5180        );
5181    }
5182
5183    #[test]
5184    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5185        let mut f = Fixture::new();
5186        f.run(&[b"HSET", b"h", b"a", b"1"]);
5187        assert_eq!(
5188            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5189            "*1\r\n:0\r\n",
5190            "XX on a field with no deadline changes nothing"
5191        );
5192        assert_eq!(
5193            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5194            "*1\r\n:1\r\n"
5195        );
5196        assert_eq!(
5197            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5198            "*1\r\n:0\r\n",
5199            "and NX will not move one that is already there"
5200        );
5201        assert_eq!(
5202            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5203            "*1\r\n:0\r\n"
5204        );
5205        assert_eq!(
5206            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5207            "*1\r\n:1\r\n"
5208        );
5209        assert_eq!(
5210            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5211            "*1\r\n:1\r\n"
5212        );
5213        assert_eq!(
5214            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5215            "*1\r\n:50\r\n"
5216        );
5217    }
5218
5219    #[test]
5220    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5221        let mut f = Fixture::new();
5222        f.run(&[b"HSET", b"h", b"a", b"1"]);
5223        for (bad, want) in [
5224            (
5225                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5226                "-ERR invalid expire time, must be >= 0",
5227            ),
5228            (
5229                &[
5230                    b"HEXPIRE".as_slice(),
5231                    b"h",
5232                    b"9999999999999999",
5233                    b"FIELDS",
5234                    b"1",
5235                    b"a",
5236                ][..],
5237                "-ERR invalid expire time in 'hexpire' command",
5238            ),
5239            (
5240                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5241                "-ERR wrong number of arguments for 'hexpire' command",
5242            ),
5243            (
5244                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5245                "-ERR Parameter `numFields` should be greater than 0",
5246            ),
5247            (
5248                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5249                "-ERR wrong number of arguments",
5250            ),
5251            (
5252                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5253                "-ERR wrong number of arguments",
5254            ),
5255        ] {
5256            let reply = f.run(bad);
5257            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5258            assert!(!reply.contains('*'), "an array header went out in front");
5259        }
5260        assert_eq!(
5261            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5262            "*1\r\n:-1\r\n",
5263            "and not one of them put a deadline on anything"
5264        );
5265    }
5266
5267    #[test]
5268    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5269        let mut f = Fixture::new();
5270        f.run(&[b"SET", b"str", b"v"]);
5271        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5272
5273        for cmd in [
5274            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5275            &[
5276                b"HPEXPIRE".as_slice(),
5277                b"str",
5278                b"100",
5279                b"FIELDS",
5280                b"1",
5281                b"f",
5282            ][..],
5283            &[
5284                b"HEXPIREAT".as_slice(),
5285                b"str",
5286                b"9999999999",
5287                b"FIELDS",
5288                b"1",
5289                b"f",
5290            ][..],
5291            &[
5292                b"HPEXPIREAT".as_slice(),
5293                b"str",
5294                b"9999999999999",
5295                b"FIELDS",
5296                b"1",
5297                b"f",
5298            ][..],
5299            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5300            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5301            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5302            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5303            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5304        ] {
5305            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5306        }
5307        assert_eq!(
5308            f.run(&[b"GET", b"str"]),
5309            "$1\r\nv\r\n",
5310            "and none of them touched the value"
5311        );
5312    }
5313
5314    #[test]
5315    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5316        let mut f = Fixture::new();
5317        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5318        assert_eq!(
5319            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5320            "*2\r\n$1\r\n1\r\n$-1\r\n",
5321            "positional, so the field that was not there is a nil in its place"
5322        );
5323        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5324        assert_eq!(
5325            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5326            "*1\r\n$-1\r\n"
5327        );
5328        assert_eq!(
5329            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5330            "*1\r\n$1\r\n2\r\n"
5331        );
5332        assert_eq!(
5333            f.run(&[b"EXISTS", b"h"]),
5334            ":0\r\n",
5335            "and the last field took the key"
5336        );
5337    }
5338
5339    #[test]
5340    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5341        let mut f = Fixture::new();
5342        f.run(&[b"HSET", b"h", b"a", b"1"]);
5343        assert_eq!(
5344            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5345            "*1\r\n$1\r\n1\r\n"
5346        );
5347        assert_eq!(
5348            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5349            "*1\r\n:-1\r\n",
5350            "no option means leave it alone, which is the one place this is not GETEX"
5351        );
5352
5353        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5354        assert_eq!(
5355            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5356            "*1\r\n:100\r\n"
5357        );
5358        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5359        assert_eq!(
5360            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5361            "*1\r\n:100\r\n",
5362            "and a plain read really does leave it alone"
5363        );
5364        assert_eq!(
5365            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5366            "*1\r\n$1\r\n1\r\n"
5367        );
5368        assert_eq!(
5369            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5370            "*1\r\n:-1\r\n"
5371        );
5372
5373        assert_eq!(
5374            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5375            "*1\r\n$1\r\n1\r\n",
5376            "the value goes out before the deadline that has already gone is applied"
5377        );
5378        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5379        assert_eq!(
5380            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5381            "*1\r\n$-1\r\n"
5382        );
5383    }
5384
5385    #[test]
5386    fn hsetex_writes_all_of_it_or_none_of_it() {
5387        let mut f = Fixture::new();
5388        assert_eq!(
5389            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5390            ":1\r\n"
5391        );
5392        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5393        assert_eq!(
5394            f.run(&[
5395                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5396            ]),
5397            ":0\r\n",
5398            "FNX wants every field named to be missing"
5399        );
5400        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5401        assert_eq!(
5402            f.run(&[b"HEXISTS", b"h", b"new"]),
5403            ":0\r\n",
5404            "and none of the list was written"
5405        );
5406        assert_eq!(
5407            f.run(&[
5408                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5409            ]),
5410            ":0\r\n",
5411            "and FXX wants every one of them to be there"
5412        );
5413        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5414        assert_eq!(
5415            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5416            ":1\r\n"
5417        );
5418        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5419
5420        assert_eq!(
5421            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5422            ":0\r\n"
5423        );
5424        assert_eq!(
5425            f.run(&[b"EXISTS", b"gone"]),
5426            ":0\r\n",
5427            "a key with no fields cannot meet FXX and is not created trying"
5428        );
5429    }
5430
5431    #[test]
5432    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5433        let mut f = Fixture::new();
5434        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5435        assert_eq!(
5436            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5437            "*1\r\n:100\r\n"
5438        );
5439
5440        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5441        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5442        assert_eq!(
5443            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5444            "*1\r\n:100\r\n",
5445            "KEEPTTL put back what the write cleared"
5446        );
5447
5448        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5449        assert_eq!(
5450            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5451            "*1\r\n:-1\r\n",
5452            "and without it a write clears the deadline the way HSET does"
5453        );
5454
5455        // Any order, because Redis reads these in a loop and not in a fixed
5456        // sequence.
5457        assert_eq!(
5458            f.run(&[
5459                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5460            ]),
5461            ":1\r\n"
5462        );
5463        assert_eq!(
5464            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5465            "*1\r\n:100\r\n"
5466        );
5467
5468        assert_eq!(
5469            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5470            ":1\r\n",
5471            "written, and not the separate code the HEXPIRE family has for this"
5472        );
5473        assert_eq!(
5474            f.run(&[b"EXISTS", b"h"]),
5475            ":0\r\n",
5476            "and storing it and then removing it emptied the hash"
5477        );
5478    }
5479
5480    #[test]
5481    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5482        let mut f = Fixture::new();
5483        f.run(&[b"HSET", b"h", b"a", b"1"]);
5484        for (bad, want) in [
5485            // HGETDEL has three sentences of its own for these three mistakes.
5486            (
5487                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5488                "-ERR Number of fields must be a positive integer",
5489            ),
5490            (
5491                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5492                "-ERR The `numfields` parameter must match the number of arguments",
5493            ),
5494            (
5495                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5496                "-ERR Mandatory argument FIELDS is missing or not at the right position",
5497            ),
5498            // And HGETEX and HSETEX have three different ones between them.
5499            (
5500                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5501                "-ERR invalid number of fields",
5502            ),
5503            (
5504                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5505                "-ERR wrong number of arguments",
5506            ),
5507            (
5508                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5509                "-ERR unknown argument: FIELD",
5510            ),
5511            (
5512                &[
5513                    b"HGETEX".as_slice(),
5514                    b"h",
5515                    b"KEEPTTL",
5516                    b"FIELDS",
5517                    b"1",
5518                    b"a",
5519                ][..],
5520                "-ERR unknown argument: KEEPTTL",
5521            ),
5522            (
5523                &[
5524                    b"HGETEX".as_slice(),
5525                    b"h",
5526                    b"EX",
5527                    b"100",
5528                    b"PERSIST",
5529                    b"FIELDS",
5530                    b"1",
5531                    b"a",
5532                ][..],
5533                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5534            ),
5535            (
5536                &[
5537                    b"HSETEX".as_slice(),
5538                    b"h",
5539                    b"EX",
5540                    b"1",
5541                    b"KEEPTTL",
5542                    b"FIELDS",
5543                    b"1",
5544                    b"a",
5545                    b"1",
5546                ][..],
5547                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5548            ),
5549            (
5550                &[
5551                    b"HSETEX".as_slice(),
5552                    b"h",
5553                    b"FNX",
5554                    b"FXX",
5555                    b"FIELDS",
5556                    b"1",
5557                    b"a",
5558                    b"1",
5559                ][..],
5560                "-ERR Only one of FXX or FNX arguments can be specified",
5561            ),
5562            (
5563                &[
5564                    b"HSETEX".as_slice(),
5565                    b"h",
5566                    b"FIELDS",
5567                    b"2",
5568                    b"a",
5569                    b"1",
5570                    b"b",
5571                ][..],
5572                "-ERR wrong number of arguments",
5573            ),
5574            (
5575                &[
5576                    b"HGETEX".as_slice(),
5577                    b"h",
5578                    b"EX",
5579                    b"-1",
5580                    b"FIELDS",
5581                    b"1",
5582                    b"a",
5583                ][..],
5584                "-ERR invalid expire time, must be >= 0",
5585            ),
5586            (
5587                &[
5588                    b"HGETEX".as_slice(),
5589                    b"h",
5590                    b"PXAT",
5591                    b"99999999999999",
5592                    b"FIELDS",
5593                    b"1",
5594                    b"a",
5595                ][..],
5596                "-ERR invalid expire time in 'hgetex' command",
5597            ),
5598            (
5599                &[
5600                    b"HSETEX".as_slice(),
5601                    b"h",
5602                    b"EX",
5603                    b"abc",
5604                    b"FIELDS",
5605                    b"1",
5606                    b"a",
5607                    b"1",
5608                ][..],
5609                "-ERR value is not an integer or out of range",
5610            ),
5611        ] {
5612            let reply = f.run(bad);
5613            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5614            assert!(!reply.contains('*'), "an array header went out in front");
5615        }
5616        assert_eq!(
5617            f.run(&[b"HGET", b"h", b"a"]),
5618            "$1\r\n1\r\n",
5619            "and not one of them wrote anything"
5620        );
5621        assert_eq!(
5622            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5623            "*1\r\n:-1\r\n"
5624        );
5625    }
5626
5627    #[test]
5628    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
5629        let mut f = Fixture::new();
5630        f.run(&[b"SET", b"str", b"v"]);
5631        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5632        for cmd in [
5633            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5634            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5635            &[
5636                b"HGETEX".as_slice(),
5637                b"str",
5638                b"EX",
5639                b"100",
5640                b"FIELDS",
5641                b"1",
5642                b"f",
5643            ][..],
5644            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
5645        ] {
5646            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5647        }
5648        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5649    }
5650
5651    /// The two orders `HIMPORT` juggles, which are not the same order.
5652    ///
5653    /// Values arrive in the order the fields were declared in and the hash is
5654    /// built in sorted order, so the first value is not generally the first
5655    /// field. And the sort is by length before bytes, which nothing else here
5656    /// sorts names with: `b` comes before `aa` where a plain byte comparison
5657    /// would put `aa` first. Both read off 8.10.1.
5658    #[test]
5659    fn himport_writes_declared_values_into_sorted_fields() {
5660        let mut f = Fixture::new();
5661        assert_eq!(
5662            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
5663            "+OK\r\n"
5664        );
5665        assert_eq!(
5666            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
5667            "+OK\r\n"
5668        );
5669        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
5670        assert_eq!(
5671            f.run(&[b"HGETALL", b"k"]),
5672            bulks(&["a", "3", "b", "1", "aa", "2"])
5673        );
5674    }
5675
5676    /// It replaces the key rather than writing over it, so a field the fieldset
5677    /// does not name is gone afterwards and so is the deadline.
5678    #[test]
5679    fn himport_set_replaces_the_whole_key() {
5680        let mut f = Fixture::new();
5681        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
5682        f.run(&[b"EXPIRE", b"k", b"100"]);
5683        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5684        assert_eq!(
5685            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5686            "+OK\r\n"
5687        );
5688        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5689        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5690    }
5691
5692    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
5693    /// throws them away, and a key built from one outlives it.
5694    #[test]
5695    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
5696        let mut f = Fixture::new();
5697        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
5698        f.run(&[b"SELECT", b"1"]);
5699        assert_eq!(
5700            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5701            "+OK\r\n"
5702        );
5703        f.run(&[b"SELECT", b"0"]);
5704        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
5705        assert_eq!(
5706            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
5707            "-ERR no such fieldset\r\n"
5708        );
5709    }
5710
5711    /// Which complaint wins when a line is wrong in more than one place.
5712    ///
5713    /// The type of the key beats both of the others, so a `HIMPORT SET` against
5714    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
5715    /// the ordering a real server has and not the one the argument order
5716    /// suggests.
5717    #[test]
5718    fn himport_complains_in_the_order_a_real_server_does() {
5719        let mut f = Fixture::new();
5720        f.run(&[b"SET", b"str", b"v"]);
5721        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5722        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5723        assert_eq!(
5724            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
5725            wrong,
5726            "the type beats a missing fieldset"
5727        );
5728        assert_eq!(
5729            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
5730            wrong,
5731            "and it beats a value count that does not fit"
5732        );
5733        assert_eq!(
5734            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
5735            "-ERR no such fieldset\r\n"
5736        );
5737        // One sentence for too few and for too many alike.
5738        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
5739            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
5740            line.extend_from_slice(values);
5741            assert_eq!(
5742                f.run(&line),
5743                "-ERR value count does not match fieldset field count\r\n",
5744                "{} values into two fields",
5745                values.len()
5746            );
5747        }
5748        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5749    }
5750
5751    /// The arity of each subcommand, and the unknown one.
5752    #[test]
5753    fn himport_checks_each_subcommand_count_under_its_own_name() {
5754        let mut f = Fixture::new();
5755        assert_eq!(
5756            f.run(&[b"HIMPORT"]),
5757            "-ERR wrong number of arguments for 'himport' command\r\n"
5758        );
5759        for (rest, name) in [
5760            (&["PREPARE"][..], "prepare"),
5761            (&["PREPARE", "fs"][..], "prepare"),
5762            (&["SET"][..], "set"),
5763            (&["SET", "k"][..], "set"),
5764            (&["SET", "k", "fs"][..], "set"),
5765            (&["DISCARD"][..], "discard"),
5766            (&["DISCARD", "a", "b"][..], "discard"),
5767            (&["DISCARDALL", "x"][..], "discardall"),
5768        ] {
5769            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
5770            line.extend(rest.iter().map(|a| a.as_bytes()));
5771            assert_eq!(
5772                f.run(&line),
5773                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
5774                "HIMPORT {}",
5775                rest.join(" ")
5776            );
5777        }
5778        assert_eq!(
5779            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
5780            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
5781        );
5782    }
5783
5784    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
5785    /// is the answer of the two that could not be guessed from outside.
5786    #[test]
5787    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
5788        let mut f = Fixture::new();
5789        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5790        assert_eq!(
5791            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
5792            "-ERR duplicate field name in fieldset\r\n"
5793        );
5794        assert_eq!(
5795            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5796            "+OK\r\n"
5797        );
5798        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5799    }
5800
5801    /// Preparing the same name twice replaces it, and the two discards count
5802    /// what they took rather than answering OK.
5803    #[test]
5804    fn himport_prepare_replaces_and_the_discards_count() {
5805        let mut f = Fixture::new();
5806        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5807        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
5808        assert_eq!(
5809            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5810            "+OK\r\n"
5811        );
5812        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
5813
5814        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
5815        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
5816        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
5817        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
5818        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
5819        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
5820    }
5821
5822    /// The one integer of a single element array reply.
5823    /// The number out of a plain integer reply.
5824    ///
5825    /// [`int_reply`] is the same thing wrapped in a one element array, which is
5826    /// the shape every hash field command answers in.
5827    fn int(reply: &str) -> i64 {
5828        let body = reply
5829            .strip_prefix(':')
5830            .and_then(|s| s.strip_suffix("\r\n"))
5831            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
5832        body.parse().expect("an integer")
5833    }
5834
5835    fn int_reply(reply: &str) -> i64 {
5836        let body = reply
5837            .strip_prefix("*1\r\n:")
5838            .and_then(|s| s.strip_suffix("\r\n"))
5839            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
5840        body.parse().expect("an integer")
5841    }
5842
5843    /// The cursor and the flat items of a scan reply.
5844    fn scan_reply(reply: &str) -> (String, Vec<String>) {
5845        let mut lines = reply.split("\r\n");
5846        assert_eq!(lines.next(), Some("*2"), "got {reply}");
5847        lines.next().expect("the cursor header");
5848        let cursor = lines.next().expect("a cursor").to_owned();
5849        let header = lines.next().expect("an item count");
5850        let n: usize = header[1..].parse().expect("a count");
5851        let mut items = Vec::with_capacity(n);
5852        for _ in 0..n {
5853            lines.next().expect("an item header");
5854            items.push(lines.next().expect("an item").to_owned());
5855        }
5856        (cursor, items)
5857    }
5858
5859    /// The members of a set reply, sorted, since none of these promise an
5860    /// order and a test that asserted one would be asserting an accident.
5861    fn sorted(reply: &str) -> Vec<String> {
5862        let mut lines = reply.split("\r\n");
5863        let header = lines.next().expect("a header");
5864        assert!(
5865            header.starts_with('*') || header.starts_with('~'),
5866            "got {reply}"
5867        );
5868        let n: usize = header[1..].parse().expect("a member count");
5869        let mut got = Vec::with_capacity(n);
5870        for _ in 0..n {
5871            lines.next().expect("a member header");
5872            got.push(lines.next().expect("a member").to_owned());
5873        }
5874        got.sort();
5875        got
5876    }
5877
5878    #[test]
5879    fn the_algebra_answers_what_the_sets_share_and_do_not() {
5880        let mut f = Fixture::new();
5881        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5882        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5883        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
5884
5885        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
5886        assert_eq!(
5887            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
5888            ["1", "2", "3", "4", "5"]
5889        );
5890        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
5891        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
5892
5893        // A key that is not there is an empty set, which empties an
5894        // intersection and does nothing at all to a union.
5895        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
5896        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5897        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5898        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5899    }
5900
5901    #[test]
5902    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5903        let mut f = Fixture::new();
5904        f.run(&[b"SADD", b"a", b"x"]);
5905        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5906        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5907        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5908
5909        f.run(&[b"HELLO", b"3"]);
5910        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5911        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5912        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5913        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5914    }
5915
5916    #[test]
5917    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5918        let mut f = Fixture::new();
5919        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5920        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5921
5922        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5923        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5924        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5925        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5926        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5927        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5928
5929        // An empty answer deletes the destination rather than leaving an empty
5930        // set behind, and the destination may be one of the sources.
5931        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5932        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5933        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5934        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5935
5936        // And a destination holding something else is overwritten, the same way
5937        // SET overwrites, rather than refused.
5938        f.run(&[b"SET", b"str", b"v"]);
5939        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5940        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5941    }
5942
5943    #[test]
5944    fn sintercard_counts_without_building_and_stops_at_a_limit() {
5945        let mut f = Fixture::new();
5946        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5947        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5948
5949        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5950        assert_eq!(
5951            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5952            ":2\r\n"
5953        );
5954        assert_eq!(
5955            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5956            ":3\r\n",
5957            "a limit of zero is no limit"
5958        );
5959        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5960        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5961
5962        // The counted keys are what make its three error messages its own.
5963        assert_eq!(
5964            f.run(&[b"SINTERCARD", b"0", b"a"]),
5965            "-ERR numkeys should be greater than 0\r\n"
5966        );
5967        assert_eq!(
5968            f.run(&[b"SINTERCARD", b"abc", b"a"]),
5969            "-ERR numkeys should be greater than 0\r\n"
5970        );
5971        assert_eq!(
5972            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5973            "-ERR Number of keys can't be greater than number of args\r\n"
5974        );
5975        assert_eq!(
5976            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5977            "-ERR LIMIT can't be negative\r\n"
5978        );
5979        assert_eq!(
5980            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5981            "-ERR syntax error\r\n"
5982        );
5983        // A key really can be called LIMIT, which is why the count exists.
5984        f.run(&[b"SADD", b"LIMIT", b"2"]);
5985        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5986    }
5987
5988    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
5989    /// over a difference. Every number here was read off 8.10.1 first.
5990    #[test]
5991    fn sunioncard_and_sdiffcard_count_without_building() {
5992        let mut f = Fixture::new();
5993        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5994        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
5995
5996        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
5997        assert_eq!(
5998            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5999            ":2\r\n"
6000        );
6001        assert_eq!(
6002            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6003            ":6\r\n",
6004            "a limit of zero is no limit"
6005        );
6006        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6007        assert_eq!(
6008            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6009            ":4\r\n",
6010            "a missing key adds nothing to a union"
6011        );
6012
6013        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6014        assert_eq!(
6015            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6016            ":1\r\n"
6017        );
6018        assert_eq!(
6019            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6020            ":2\r\n",
6021            "a difference is not symmetric"
6022        );
6023        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6024        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6025        assert_eq!(
6026            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6027            ":0\r\n",
6028            "nothing taken away from nothing"
6029        );
6030
6031        // The same three messages SINTERCARD has, because the line is the same
6032        // line and is parsed once for all three.
6033        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6034            assert_eq!(
6035                f.run(&[name, b"0", b"a"]),
6036                "-ERR numkeys should be greater than 0\r\n"
6037            );
6038            assert_eq!(
6039                f.run(&[name, b"abc", b"a"]),
6040                "-ERR numkeys should be greater than 0\r\n"
6041            );
6042            assert_eq!(
6043                f.run(&[name, b"-1", b"a"]),
6044                "-ERR numkeys should be greater than 0\r\n"
6045            );
6046            assert_eq!(
6047                f.run(&[name, b"3", b"a", b"b"]),
6048                "-ERR Number of keys can't be greater than number of args\r\n"
6049            );
6050            assert_eq!(
6051                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6052                "-ERR LIMIT can't be negative\r\n"
6053            );
6054            assert_eq!(
6055                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6056                "-ERR LIMIT can't be negative\r\n",
6057                "a LIMIT that is not a number gets the negative message too"
6058            );
6059            assert_eq!(
6060                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6061                "-ERR syntax error\r\n"
6062            );
6063            assert_eq!(
6064                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6065                "-ERR syntax error\r\n"
6066            );
6067            assert_eq!(
6068                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6069                "-ERR syntax error\r\n"
6070            );
6071        }
6072
6073        // And a key called LIMIT is a key, here as much as on SINTERCARD.
6074        f.run(&[b"SADD", b"LIMIT", b"2"]);
6075        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6076        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6077    }
6078
6079    #[test]
6080    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6081        let mut f = Fixture::new();
6082        f.run(&[b"SADD", b"a", b"1"]);
6083        f.run(&[b"SADD", b"d", b"old"]);
6084        f.run(&[b"SET", b"str", b"v"]);
6085
6086        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6087        for bad in [
6088            &[b"SINTER".as_slice(), b"a", b"str"][..],
6089            &[b"SUNION".as_slice(), b"str"][..],
6090            &[b"SDIFF".as_slice(), b"a", b"str"][..],
6091            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6092            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6093            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6094            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6095        ] {
6096            let reply = f.run(bad);
6097            assert_eq!(reply, wrong, "for {:?}", bad[0]);
6098        }
6099        assert_eq!(
6100            f.run(&[b"SMEMBERS", b"d"]),
6101            "*1\r\n$3\r\nold\r\n",
6102            "and the destination was left alone every time"
6103        );
6104    }
6105
6106    /// The leak a set can spring that nothing on the wire would ever show: the
6107    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
6108    #[test]
6109    fn churning_sets_does_not_grow_the_server() {
6110        let mut f = Fixture::new();
6111        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6112        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6113            .chain(std::iter::once(&b"s"[..]))
6114            .chain(members.iter().map(Vec::as_slice))
6115            .collect();
6116
6117        f.run(&args);
6118        f.run(&[b"DEL", b"s"]);
6119        f.server.compact_step();
6120        let after_first = f.server.memory_bytes();
6121
6122        for _ in 0..200 {
6123            f.run(&args);
6124            f.run(&[b"DEL", b"s"]);
6125            f.server.compact_step();
6126        }
6127        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6128        assert!(
6129            f.server.memory_bytes() <= after_first * 2,
6130            "held {} after two hundred passes against {after_first} after one",
6131            f.server.memory_bytes()
6132        );
6133    }
6134
6135    // --------------------------------------------------------------- bitmaps
6136
6137    /// The two single bit commands, and the encoding rule underneath them.
6138    ///
6139    /// A write always leaves the value `raw` and a read never re-encodes, which
6140    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
6141    /// with its first digit changed after a `SETBIT`.
6142    #[test]
6143    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6144        let mut f = Fixture::new();
6145        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6146        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6147        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6148        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6149        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6150        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6151
6152        // Writing a nought past the end still creates the key and still pads.
6153        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6154        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6155        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6156
6157        f.run(&[b"SET", b"num", b"12345"]);
6158        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6159        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6160        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6161        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6162        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6163    }
6164
6165    /// Counting, in bytes and in bits.
6166    ///
6167    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
6168    /// says 22 for it. The server is the thing being copied here.
6169    #[test]
6170    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6171        let mut f = Fixture::new();
6172        f.run(&[b"SET", b"mykey", b"foobar"]);
6173        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6174        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6175        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6176        assert_eq!(
6177            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6178            ":6\r\n"
6179        );
6180        assert_eq!(
6181            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6182            ":25\r\n"
6183        );
6184        assert_eq!(
6185            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6186            ":17\r\n"
6187        );
6188        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6189
6190        // A start past the end is left where it is and the end is pulled back,
6191        // so the range comes out backwards and counts nothing.
6192        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6193
6194        // A lone start is a syntax error here, where BITPOS allows it.
6195        assert_eq!(
6196            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6197            "-ERR syntax error\r\n"
6198        );
6199        assert_eq!(
6200            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6201            "-ERR syntax error\r\n"
6202        );
6203    }
6204
6205    /// Searching, and the one place a miss is not minus one.
6206    ///
6207    /// A search for a nought that runs to the end of the string answers the
6208    /// length in bits, because the string is treated as if it had noughts after
6209    /// it forever. Give it an explicit end and it answers minus one instead.
6210    #[test]
6211    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6212        let mut f = Fixture::new();
6213        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6214        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6215        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6216        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6217        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6218        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6219
6220        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6221        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6222        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6223        assert_eq!(
6224            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6225            ":8\r\n"
6226        );
6227
6228        // A missing key is all noughts, so a one is never found and a nought is
6229        // at position zero.
6230        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6231        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6232    }
6233
6234    /// The eight operations, with the answers a real server gives for them.
6235    #[test]
6236    fn the_eight_combinations_write_what_a_real_server_writes() {
6237        let mut f = Fixture::new();
6238        f.run(&[b"SET", b"a", b"abc"]);
6239        f.run(&[b"SET", b"b", b"abd"]);
6240        let cases: &[(&[u8], &str)] = &[
6241            (b"AND", "ab`"),
6242            (b"OR", "abg"),
6243            (b"XOR", "\u{0}\u{0}\u{7}"),
6244            (b"DIFF", "\u{0}\u{0}\u{3}"),
6245            (b"DIFF1", "\u{0}\u{0}\u{4}"),
6246            (b"ANDOR", "ab`"),
6247            (b"ONE", "\u{0}\u{0}\u{7}"),
6248        ];
6249        for (op, want) in cases {
6250            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6251            assert_eq!(
6252                f.run(&[b"GET", b"d"]),
6253                format!("$3\r\n{want}\r\n"),
6254                "{op:?}"
6255            );
6256        }
6257        // The one whose answer is not text, so it is compared as bytes.
6258        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6259        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6260
6261        // A missing source is a string of noughts as long as it needs to be, so
6262        // an AND against one writes three zero bytes rather than nothing.
6263        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6264        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6265
6266        // Every source missing is an empty result, and an empty result takes
6267        // the destination with it.
6268        f.run(&[b"SET", b"dest", b"x"]);
6269        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6270        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6271    }
6272
6273    /// What `BITOP` says when it is asked for something it cannot do.
6274    #[test]
6275    fn bitop_names_the_operation_in_its_own_complaints() {
6276        let mut f = Fixture::new();
6277        f.run(&[b"SET", b"a", b"abc"]);
6278        assert_eq!(
6279            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6280            "-ERR syntax error\r\n"
6281        );
6282        assert_eq!(
6283            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6284            "-ERR BITOP NOT must be called with a single source key.\r\n"
6285        );
6286        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6287            assert_eq!(
6288                f.run(&[b"BITOP", op, b"d", b"a"]),
6289                format!(
6290                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
6291                    String::from_utf8_lossy(op)
6292                )
6293            );
6294        }
6295        f.run(&[b"LPUSH", b"l", b"x"]);
6296        assert_eq!(
6297            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6298            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6299        );
6300    }
6301
6302    /// Packed fields, the three overflow policies and the `#` offset.
6303    #[test]
6304    fn bitfield_reads_and_writes_packed_fields() {
6305        let mut f = Fixture::new();
6306        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6307        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6308
6309        assert_eq!(
6310            f.run(&[
6311                b"BITFIELD",
6312                b"bf",
6313                b"INCRBY",
6314                b"u2",
6315                b"100",
6316                b"1",
6317                b"GET",
6318                b"u4",
6319                b"0"
6320            ]),
6321            "*2\r\n:1\r\n:0\r\n"
6322        );
6323        // The field at bit 100 is two bits wide, so it ends in the thirteenth
6324        // byte and the value grew to thirteen bytes to hold it.
6325        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6326
6327        // A `#` offset counts in fields rather than in bits.
6328        assert_eq!(
6329            f.run(&[
6330                b"BITFIELD",
6331                b"bf",
6332                b"SET",
6333                b"u8",
6334                b"#0",
6335                b"255",
6336                b"GET",
6337                b"u8",
6338                b"#0"
6339            ]),
6340            "*2\r\n:0\r\n:255\r\n"
6341        );
6342
6343        assert_eq!(
6344            f.run(&[
6345                b"BITFIELD",
6346                b"bf",
6347                b"OVERFLOW",
6348                b"SAT",
6349                b"INCRBY",
6350                b"i8",
6351                b"0",
6352                b"120",
6353                b"INCRBY",
6354                b"i8",
6355                b"0",
6356                b"120"
6357            ]),
6358            "*2\r\n:119\r\n:127\r\n"
6359        );
6360        assert_eq!(
6361            f.run(&[
6362                b"BITFIELD",
6363                b"bf2",
6364                b"OVERFLOW",
6365                b"FAIL",
6366                b"INCRBY",
6367                b"u2",
6368                b"0",
6369                b"5"
6370            ]),
6371            "*1\r\n$-1\r\n"
6372        );
6373        assert_eq!(
6374            f.run(&[
6375                b"BITFIELD",
6376                b"bf3",
6377                b"OVERFLOW",
6378                b"WRAP",
6379                b"INCRBY",
6380                b"u2",
6381                b"0",
6382                b"5"
6383            ]),
6384            "*1\r\n:1\r\n"
6385        );
6386        assert_eq!(
6387            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6388            "*1\r\n:4611686018427387904\r\n"
6389        );
6390    }
6391
6392    /// A bad subcommand anywhere in the line stops all of it.
6393    ///
6394    /// Redis checks the whole argument list before it runs any of it, so the
6395    /// `SET` in front of the bad type here never happens and the key it would
6396    /// have created is not there afterwards.
6397    #[test]
6398    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6399        let mut f = Fixture::new();
6400        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6401        assert_eq!(
6402            f.run(&[
6403                b"BITFIELD",
6404                b"bad",
6405                b"SET",
6406                b"u8",
6407                b"0",
6408                b"1",
6409                b"GET",
6410                b"u99",
6411                b"0"
6412            ]),
6413            bad_type
6414        );
6415        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6416        assert_eq!(
6417            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6418            bad_type
6419        );
6420        assert_eq!(
6421            f.run(&[b"BITFIELD", b"bad", b"GET"]),
6422            "-ERR syntax error\r\n"
6423        );
6424        assert_eq!(
6425            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6426            "-ERR syntax error\r\n"
6427        );
6428        assert_eq!(
6429            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6430            "-ERR syntax error\r\n"
6431        );
6432        assert_eq!(
6433            f.run(&[
6434                b"BITFIELD",
6435                b"bad",
6436                b"OVERFLOW",
6437                b"NOPE",
6438                b"GET",
6439                b"u8",
6440                b"0"
6441            ]),
6442            "-ERR Invalid OVERFLOW type specified\r\n"
6443        );
6444        assert_eq!(
6445            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6446            "-ERR value is not an integer or out of range\r\n"
6447        );
6448        for at in [&b"#-1"[..], b"abc"] {
6449            assert_eq!(
6450                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6451                "-ERR bit offset is not an integer or out of range\r\n"
6452            );
6453        }
6454    }
6455
6456    /// The read only twin reads, refuses to write, and creates nothing.
6457    #[test]
6458    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6459        let mut f = Fixture::new();
6460        f.run(&[b"SET", b"n", b"123"]);
6461        assert_eq!(
6462            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6463            "*1\r\n:49\r\n"
6464        );
6465        // A read does not unpack an int the way a write does.
6466        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6467
6468        // An OVERFLOW word is allowed even though nothing here can overflow.
6469        assert_eq!(
6470            f.run(&[
6471                b"BITFIELD_RO",
6472                b"n",
6473                b"OVERFLOW",
6474                b"SAT",
6475                b"GET",
6476                b"u8",
6477                b"0"
6478            ]),
6479            "*1\r\n:49\r\n"
6480        );
6481        for sub in [&b"SET"[..], b"INCRBY"] {
6482            assert_eq!(
6483                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6484                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6485            );
6486        }
6487
6488        assert_eq!(
6489            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6490            "*1\r\n:0\r\n"
6491        );
6492        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6493    }
6494
6495    /// The offsets a bitmap command will not take.
6496    #[test]
6497    fn an_offset_off_the_end_of_the_world_is_refused() {
6498        let mut f = Fixture::new();
6499        let bad = "-ERR bit offset is not an integer or out of range\r\n";
6500        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6501            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6502            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6503        }
6504        for arg in [&b"2"[..], b"-1"] {
6505            assert_eq!(
6506                f.run(&[b"BITPOS", b"k", arg]),
6507                "-ERR The bit argument must be 1 or 0.\r\n"
6508            );
6509        }
6510        assert_eq!(
6511            f.run(&[b"BITPOS", b"k", b"abc"]),
6512            "-ERR value is not an integer or out of range\r\n"
6513        );
6514        assert_eq!(
6515            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6516            "-ERR value is not an integer or out of range\r\n"
6517        );
6518        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6519        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6520        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6521    }
6522
6523    /// Every one of the seven refuses a key that is not a string.
6524    #[test]
6525    fn every_bitmap_command_says_wrongtype() {
6526        let mut f = Fixture::new();
6527        f.run(&[b"LPUSH", b"l", b"x"]);
6528        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6529        let cases: &[&[&[u8]]] = &[
6530            &[b"SETBIT", b"l", b"0", b"1"],
6531            &[b"GETBIT", b"l", b"0"],
6532            &[b"BITCOUNT", b"l"],
6533            &[b"BITPOS", b"l", b"1"],
6534            &[b"BITOP", b"AND", b"d", b"l"],
6535            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6536            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6537        ];
6538        for case in cases {
6539            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6540        }
6541    }
6542
6543    // --------------------------------------------------------- hyperloglogs
6544
6545    #[test]
6546    fn a_sketch_is_added_to_and_counted() {
6547        let mut f = Fixture::new();
6548        // Creating the key counts as a change, even with nothing to add.
6549        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6550        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6551        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6552        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6553        // And it is a string, which is not an implementation detail: a client
6554        // can `GET` a sketch out of one server and `SET` it into another.
6555        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6556        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6557
6558        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6559        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6560        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6561    }
6562
6563    #[test]
6564    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6565        let mut f = Fixture::new();
6566        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6567        // Not text, so it is compared as bytes.
6568        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";
6569        let mut reply = b"$27\r\n".to_vec();
6570        reply.extend_from_slice(want);
6571        reply.extend_from_slice(b"\r\n");
6572        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6573    }
6574
6575    #[test]
6576    fn counting_several_keys_counts_their_union() {
6577        let mut f = Fixture::new();
6578        f.run(&[b"PFADD", b"a", b"x", b"y"]);
6579        f.run(&[b"PFADD", b"b", b"y", b"z"]);
6580        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6581        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6582        // A key that is not there is an empty sketch, not an error and not
6583        // something that gets created by being counted.
6584        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6585        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6586        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6587    }
6588
6589    #[test]
6590    fn a_merge_keeps_what_the_destination_had() {
6591        let mut f = Fixture::new();
6592        f.run(&[b"PFADD", b"a", b"x", b"y"]);
6593        f.run(&[b"PFADD", b"b", b"z"]);
6594        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
6595        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
6596        // The destination is one of the sources, so a second merge adds to it.
6597        f.run(&[b"PFADD", b"c", b"w"]);
6598        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
6599        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
6600        // And with no sources it is a no-op that still answers OK and still
6601        // creates a destination that was not there.
6602        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
6603        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
6604    }
6605
6606    #[test]
6607    fn the_debug_forms_answer_four_different_shapes() {
6608        let mut f = Fixture::new();
6609        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6610        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
6611        assert_eq!(
6612            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6613            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
6614        );
6615        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
6616        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
6617        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
6618        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
6619        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6620        // A dense sketch has no opcodes left to print.
6621        assert_eq!(
6622            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6623            "-ERR HLL encoding is not sparse\r\n"
6624        );
6625
6626        // All 16384 registers, of which three are not nought.
6627        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
6628        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
6629        assert_eq!(reply.matches(":0\r\n").count(), 16381);
6630        assert_eq!(reply.matches(":1\r\n").count(), 2);
6631        assert_eq!(reply.matches(":2\r\n").count(), 1);
6632
6633        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
6634    }
6635
6636    #[test]
6637    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
6638        let mut f = Fixture::new();
6639        f.run(&[b"SET", b"plain", b"not a sketch"]);
6640        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
6641        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
6642        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
6643        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
6644        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
6645
6646        // A key that is not a string at all gets the ordinary sentence, and a
6647        // destination that would have been written is not created.
6648        f.run(&[b"RPUSH", b"l", b"x"]);
6649        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6650        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
6651        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
6652        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
6653        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6654        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
6655    }
6656
6657    #[test]
6658    fn pfdebug_has_its_own_complaints() {
6659        let mut f = Fixture::new();
6660        f.run(&[b"PFADD", b"h", b"a"]);
6661        // The word is quoted exactly as the client spelled it, and this is not
6662        // the "Try X HELP." sentence every other container command uses.
6663        assert_eq!(
6664            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6665            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6666        );
6667        // Where all three of the real commands take a missing key as empty.
6668        let gone = "-ERR The specified key does not exist\r\n";
6669        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
6670        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
6671        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
6672        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
6673        assert_eq!(
6674            f.run(&[b"PFDEBUG"]),
6675            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
6676        );
6677        assert_eq!(
6678            f.run(&[b"PFSELFTEST", b"x"]),
6679            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
6680        );
6681    }
6682
6683    #[test]
6684    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
6685        let mut f = Fixture::new();
6686        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6687        // The sketch with its last byte cut off, which is still a header and a
6688        // magic and is a run length encoding that stops short of register 16384.
6689        let reply = f.raw(&[b"GET", b"h"]);
6690        let short = reply[5..reply.len() - 3].to_vec();
6691        f.run(&[b"SET", b"h", &short]);
6692        assert_eq!(
6693            f.run(&[b"PFCOUNT", b"h"]),
6694            "-INVALIDOBJ Corrupted HLL object detected\r\n"
6695        );
6696    }
6697
6698    #[test]
6699    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
6700        let mut f = Fixture::new();
6701        // One that stays sparse and one that has gone dense, since the payload
6702        // carries the bytes and the two encodings are different lengths.
6703        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
6704        for i in 0..10_000u32 {
6705            let ele = format!("e{i}");
6706            f.run(&[b"PFADD", b"big", ele.as_bytes()]);
6707        }
6708        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
6709        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
6710
6711        for key in [&b"small"[..], b"big"] {
6712            let mut copy = key.to_vec();
6713            copy.push(b'2');
6714            let bytes = payload(&f.raw(&[b"DUMP", key]));
6715            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
6716            // The bytes, the encoding and the estimate all come back, which is
6717            // the whole of what byte compatibility across a round trip means.
6718            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
6719            assert_eq!(
6720                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
6721                f.run(&[b"PFDEBUG", b"ENCODING", key])
6722            );
6723            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
6724        }
6725        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
6726        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
6727    }
6728
6729    /// One RESP2 bulk string. The JSON replies are almost all one of these and
6730    /// the text inside them has quotes in it, so writing the frame out by hand
6731    /// buries the part of the assertion that matters.
6732    fn bulk(s: &str) -> String {
6733        format!("${}\r\n{s}\r\n", s.len())
6734    }
6735
6736    /// A RESP2 array of bulk strings, which is what most of the list replies
6737    /// are and what writing them out by hand in every assertion looks like.
6738    fn bulks(parts: &[&str]) -> String {
6739        let mut s = format!("*{}\r\n", parts.len());
6740        for p in parts {
6741            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
6742        }
6743        s
6744    }
6745
6746    #[test]
6747    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
6748        let mut f = Fixture::new();
6749        // Each element in turn goes at the head, so the last one sent is at the
6750        // front when it is over. That reads like a bug in the client and it is
6751        // what every Redis has always done.
6752        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
6753        assert_eq!(
6754            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6755            bulks(&["c", "b", "a"])
6756        );
6757        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
6758        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
6759        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
6760        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
6761        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
6762        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
6763    }
6764
6765    #[test]
6766    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
6767        let mut f = Fixture::new();
6768        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
6769        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
6770        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6771        f.run(&[b"RPUSH", b"k", b"a"]);
6772        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
6773        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
6774        assert_eq!(
6775            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6776            bulks(&["z", "a", "y"])
6777        );
6778    }
6779
6780    /// The four ways a pop can come back with nothing, which are three
6781    /// different replies and a RESP2 client can tell all of them apart.
6782    #[test]
6783    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
6784        let mut f = Fixture::new();
6785        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
6786        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
6787        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
6788        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
6789        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6790        // A count of zero against a list that is there is an empty array and
6791        // not a null array, which is the fourth answer.
6792        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
6793        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
6794        // More than there is takes what there is and the key goes with it.
6795        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
6796        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6797    }
6798
6799    #[test]
6800    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
6801        let mut f = Fixture::new();
6802        f.run(&[b"RPUSH", b"k", b"a"]);
6803        let range = "-ERR value is out of range, must be positive\r\n";
6804        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
6805        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
6806        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
6807        // Redis calls this an arity error and not a syntax error, which is a
6808        // distinction it does not always make.
6809        assert_eq!(
6810            f.run(&[b"LPOP", b"k", b"1", b"2"]),
6811            "-ERR wrong number of arguments for 'lpop' command\r\n"
6812        );
6813        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6814    }
6815
6816    #[test]
6817    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
6818        let mut f = Fixture::new();
6819        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6820        assert_eq!(
6821            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6822            bulks(&["a", "b", "c"])
6823        );
6824        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
6825        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
6826        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
6827        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
6828        assert_eq!(
6829            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
6830            bulks(&["a", "b", "c"])
6831        );
6832        // A key that is not there is an empty range and not a nil, which is the
6833        // one place a list disagrees with a set.
6834        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
6835        assert_eq!(
6836            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
6837            "-ERR value is not an integer or out of range\r\n"
6838        );
6839    }
6840
6841    #[test]
6842    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
6843        let mut f = Fixture::new();
6844        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6845        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
6846        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
6847        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
6848        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
6849        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
6850        assert_eq!(
6851            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6852            bulks(&["a", "b", "z"])
6853        );
6854        // Both ways of missing are errors here rather than a nil, because a
6855        // list is never empty and there is nothing else the reply could be.
6856        assert_eq!(
6857            f.run(&[b"LSET", b"k", b"99", b"z"]),
6858            "-ERR index out of range\r\n"
6859        );
6860        assert_eq!(
6861            f.run(&[b"LSET", b"nope", b"0", b"z"]),
6862            "-ERR no such key\r\n"
6863        );
6864    }
6865
6866    #[test]
6867    fn linsert_says_three_things_with_one_signed_number() {
6868        let mut f = Fixture::new();
6869        // Zero for a key that is not there, which is not the same as minus one
6870        // for a pivot that is not in a list that is.
6871        assert_eq!(
6872            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
6873            ":0\r\n"
6874        );
6875        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6876        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
6877        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
6878        assert_eq!(
6879            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6880            bulks(&["X", "a", "b", "Y"])
6881        );
6882        assert_eq!(
6883            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
6884            ":-1\r\n"
6885        );
6886        assert_eq!(
6887            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
6888            "-ERR syntax error\r\n"
6889        );
6890    }
6891
6892    #[test]
6893    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
6894        let mut f = Fixture::new();
6895        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
6896        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
6897        assert_eq!(
6898            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6899            bulks(&["b", "c", "a"])
6900        );
6901        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
6902        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6903        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
6904        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
6905        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6906        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
6907    }
6908
6909    #[test]
6910    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
6911        let mut f = Fixture::new();
6912        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
6913        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
6914        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6915        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
6916        // leave `EXISTS` answering zero rather than leaving an empty one.
6917        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
6918        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6919        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
6920    }
6921
6922    #[test]
6923    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
6924        let mut f = Fixture::new();
6925        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
6926        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
6927        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
6928        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
6929        assert_eq!(
6930            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
6931            "*2\r\n:0\r\n:3\r\n"
6932        );
6933        assert_eq!(
6934            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
6935            "*3\r\n:6\r\n:3\r\n:0\r\n"
6936        );
6937        // MAXLEN counts elements looked at and not matches found, so three
6938        // stops after `a b c` and finds the one match in it.
6939        assert_eq!(
6940            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
6941            "*1\r\n:0\r\n"
6942        );
6943        // Nothing found is three different replies depending on how it was
6944        // asked and whether the key is there at all.
6945        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
6946        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
6947        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
6948        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
6949    }
6950
6951    #[test]
6952    fn lpos_words_its_three_mistakes_the_way_redis_does() {
6953        let mut f = Fixture::new();
6954        f.run(&[b"RPUSH", b"p", b"a"]);
6955        // The whole sentence and not a prefix, because the older wording of it
6956        // is still all over the internet and clients match on the text.
6957        assert_eq!(
6958            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
6959            "-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"
6960        );
6961        assert_eq!(
6962            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
6963            "-ERR COUNT can't be negative\r\n"
6964        );
6965        assert_eq!(
6966            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
6967            "-ERR MAXLEN can't be negative\r\n"
6968        );
6969        assert_eq!(
6970            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
6971            "-ERR syntax error\r\n"
6972        );
6973        assert_eq!(
6974            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
6975            "-ERR syntax error\r\n"
6976        );
6977    }
6978
6979    #[test]
6980    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
6981        let mut f = Fixture::new();
6982        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6983        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
6984        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6985        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
6986        assert_eq!(
6987            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
6988            "$1\r\na\r\n"
6989        );
6990        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
6991        // The same key twice is the documented way to rotate a list and falls
6992        // out of taking the element before deciding where to put it.
6993        f.run(&[b"DEL", b"r"]);
6994        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
6995        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
6996        assert_eq!(
6997            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
6998            bulks(&["3", "1", "2"])
6999        );
7000        assert_eq!(
7001            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7002            "$-1\r\n"
7003        );
7004        assert_eq!(
7005            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7006            "-ERR syntax error\r\n"
7007        );
7008    }
7009
7010    #[test]
7011    fn a_move_checks_the_destination_before_it_takes_anything() {
7012        let mut f = Fixture::new();
7013        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7014        f.run(&[b"SET", b"str", b"v"]);
7015        assert_eq!(
7016            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7017            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7018        );
7019        // The element is still where it was, rather than having gone nowhere.
7020        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7021    }
7022
7023    #[test]
7024    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7025        // OBO is what you get from sending LMOVE that many times, BULK keeps
7026        // the source order. The two only differ when both ends are the same,
7027        // which is the whole reason the word exists.
7028        for (from, to, order, want) in [
7029            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7030            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7031            ("LEFT", "LEFT", "OBO", ["b", "a"]),
7032            ("LEFT", "LEFT", "BULK", ["a", "b"]),
7033            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7034            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7035            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7036            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7037        ] {
7038            let mut f = Fixture::new();
7039            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7040            let how = format!("{from} {to} {order}");
7041            let reply = f.run(&[
7042                b"LMOVEM",
7043                b"s",
7044                b"d",
7045                from.as_bytes(),
7046                to.as_bytes(),
7047                b"COUNT",
7048                b"2",
7049                order.as_bytes(),
7050            ]);
7051            assert_eq!(reply, bulks(&want), "the reply for {how}");
7052            assert_eq!(
7053                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7054                bulks(&want),
7055                "the destination for {how}"
7056            );
7057        }
7058    }
7059
7060    #[test]
7061    fn a_block_move_of_one_needs_no_count_at_all() {
7062        let mut f = Fixture::new();
7063        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7064        assert_eq!(
7065            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7066            bulks(&["a"])
7067        );
7068        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7069        // Six and seven arguments are neither of the two forms, so the
7070        // reference calls both of them a syntax error rather than guessing.
7071        assert_eq!(
7072            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7073            "-ERR syntax error\r\n"
7074        );
7075        assert_eq!(
7076            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7077            "-ERR syntax error\r\n"
7078        );
7079    }
7080
7081    #[test]
7082    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7083        let mut f = Fixture::new();
7084        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7085        // A null array and not a null bulk string, which `redis-cli` prints as
7086        // `(nil)` either way and only the raw wire tells apart. What it would
7087        // have sent is an array, so its nothing is an array's nothing.
7088        assert_eq!(
7089            f.run(&[
7090                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7091            ]),
7092            "*-1\r\n"
7093        );
7094        assert_eq!(
7095            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7096            bulks(&["a", "b", "c"])
7097        );
7098        // COUNT takes what there is, and an emptied source goes away.
7099        assert_eq!(
7100            f.run(&[
7101                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7102            ]),
7103            bulks(&["a", "b", "c"])
7104        );
7105        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7106        assert_eq!(
7107            f.run(&[
7108                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7109            ]),
7110            "*-1\r\n"
7111        );
7112    }
7113
7114    #[test]
7115    fn a_block_move_onto_itself_rotates_by_the_count() {
7116        let mut f = Fixture::new();
7117        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7118        assert_eq!(
7119            f.run(&[
7120                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7121            ]),
7122            bulks(&["a", "b"])
7123        );
7124        assert_eq!(
7125            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7126            bulks(&["c", "a", "b"])
7127        );
7128    }
7129
7130    #[test]
7131    fn a_block_move_reads_the_count_before_the_ordering_word() {
7132        let mut f = Fixture::new();
7133        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7134        f.run(&[b"SET", b"str", b"v"]);
7135        let count = "-ERR count should be greater than 0\r\n";
7136        assert_eq!(
7137            f.run(&[
7138                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7139            ]),
7140            count
7141        );
7142        assert_eq!(
7143            f.run(&[
7144                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7145            ]),
7146            count
7147        );
7148        assert_eq!(
7149            f.run(&[
7150                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7151            ]),
7152            "-ERR syntax error\r\n"
7153        );
7154        assert_eq!(
7155            f.run(&[
7156                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7157            ]),
7158            "-ERR syntax error\r\n"
7159        );
7160        // Every argument is read before the keys are looked at, so a bad count
7161        // beats a wrong type even when the type is wrong on the source.
7162        assert_eq!(
7163            f.run(&[
7164                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7165            ]),
7166            count
7167        );
7168        assert_eq!(
7169            f.run(&[
7170                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7171            ]),
7172            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7173        );
7174        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7175    }
7176
7177    #[test]
7178    fn lmpop_answers_from_the_first_key_that_has_anything() {
7179        let mut f = Fixture::new();
7180        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7181        // The name of the key that answered comes back with the elements,
7182        // because the client cannot work out which one it was.
7183        assert_eq!(
7184            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7185            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7186        );
7187        assert_eq!(
7188            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7189            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7190        );
7191        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7192        // A null array and not a null, even though what it stands in for is an
7193        // array holding a key name and then another array.
7194        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7195    }
7196
7197    #[test]
7198    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7199        let mut f = Fixture::new();
7200        f.run(&[b"RPUSH", b"k", b"a"]);
7201        assert_eq!(
7202            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7203            "-ERR numkeys should be greater than 0\r\n"
7204        );
7205        assert_eq!(
7206            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7207            "-ERR numkeys should be greater than 0\r\n"
7208        );
7209        assert_eq!(
7210            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7211            "-ERR count should be greater than 0\r\n"
7212        );
7213        // A key count that eats the direction is a syntax error and not a
7214        // sentence about key counts, because the direction is simply not there.
7215        assert_eq!(
7216            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7217            "-ERR syntax error\r\n"
7218        );
7219        assert_eq!(
7220            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7221            "-ERR syntax error\r\n"
7222        );
7223        assert_eq!(
7224            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7225            "-ERR syntax error\r\n"
7226        );
7227        assert_eq!(
7228            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7229            "-ERR syntax error\r\n"
7230        );
7231        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7232    }
7233
7234    #[test]
7235    fn every_list_command_says_wrongtype_and_writes_nothing() {
7236        let mut f = Fixture::new();
7237        f.run(&[b"SET", b"str", b"v"]);
7238        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7239        for cmd in [
7240            &[b"LPUSH".as_slice(), b"str", b"a"][..],
7241            &[b"RPUSH", b"str", b"a"],
7242            &[b"LPUSHX", b"str", b"a"],
7243            &[b"RPUSHX", b"str", b"a"],
7244            &[b"LPOP", b"str"],
7245            &[b"LPOP", b"str", b"2"],
7246            &[b"RPOP", b"str"],
7247            &[b"LLEN", b"str"],
7248            &[b"LRANGE", b"str", b"0", b"-1"],
7249            &[b"LINDEX", b"str", b"0"],
7250            &[b"LSET", b"str", b"0", b"a"],
7251            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7252            &[b"LREM", b"str", b"0", b"a"],
7253            &[b"LTRIM", b"str", b"0", b"-1"],
7254            &[b"LPOS", b"str", b"a"],
7255            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7256            &[b"RPOPLPUSH", b"str", b"d"],
7257            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7258            &[b"LMPOP", b"1", b"str", b"LEFT"],
7259        ] {
7260            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7261        }
7262        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7263        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7264    }
7265
7266    /// A timeout is not an integer and it is not an ordinary float either: the
7267    /// three sentences it can answer with are its own, and which one a given
7268    /// argument gets is not what reading the code would suggest.
7269    #[test]
7270    fn a_timeout_has_three_ways_of_being_wrong() {
7271        let mut f = Fixture::new();
7272        let not_float = "-ERR timeout is not a float or out of range\r\n";
7273        let range = "-ERR timeout is out of range\r\n";
7274        for (bad, want) in [
7275            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7276            (&[b"BLPOP", b"k", b"nan"], not_float),
7277            (&[b"BLPOP", b"k", b""], not_float),
7278            // Whitespace on either side, which `strtold` would take and Redis
7279            // does not.
7280            (&[b"BLPOP", b"k", b" 1"], not_float),
7281            (&[b"BLPOP", b"k", b"1 "], not_float),
7282            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7283            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7284            // These three parse, so they are not the not-a-float error, and all
7285            // three are further off than an i64 of milliseconds reaches.
7286            (&[b"BLPOP", b"k", b"1e400"], range),
7287            (&[b"BLPOP", b"k", b"inf"], range),
7288            (&[b"BLPOP", b"k", b"9999999999999999"], range),
7289            (&[b"BRPOP", b"k", b"abc"], not_float),
7290            (
7291                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7292                not_float,
7293            ),
7294            (
7295                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7296                "-ERR timeout is negative\r\n",
7297            ),
7298            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7299        ] {
7300            assert_eq!(f.run(bad), want, "for {bad:?}");
7301        }
7302    }
7303
7304    /// A timeout of exactly zero means no timeout, and there are two ways of
7305    /// writing exactly zero.
7306    #[test]
7307    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7308        let mut f = Fixture::new();
7309        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7310            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7311            assert_eq!(flow, Flow::Block, "for {timeout:?}");
7312            assert!(out.is_empty(), "for {timeout:?}");
7313        }
7314        // Positive, so it is a real deadline, and the deadline is this
7315        // millisecond. Nothing is written here either: the reply comes from the
7316        // sweep, which is the engine's and not this layer's.
7317        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7318        assert_eq!(flow, Flow::Block);
7319        assert!(out.is_empty());
7320    }
7321
7322    #[test]
7323    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7324        let mut f = Fixture::new();
7325        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7326
7327        // The one difference from LPOP: the reply names the key that answered,
7328        // which is what makes BLPOP over several keys usable.
7329        assert_eq!(
7330            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7331            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7332        );
7333        assert_eq!(
7334            f.run(&[b"BRPOP", b"L", b"0"]),
7335            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7336        );
7337        assert_eq!(
7338            f.run(&[
7339                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7340            ]),
7341            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7342        );
7343        assert_eq!(
7344            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7345            "$1\r\nd\r\n"
7346        );
7347        assert_eq!(
7348            f.run(&[b"EXISTS", b"L"]),
7349            ":0\r\n",
7350            "and the key went with it"
7351        );
7352        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7353        // Onto itself, which is how a list is rotated and is a real thing to ask
7354        // a blocking move for.
7355        f.run(&[b"RPUSH", b"D", b"x"]);
7356        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7357        assert_eq!(
7358            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7359            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7360        );
7361    }
7362
7363    #[test]
7364    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7365        let mut f = Fixture::new();
7366        f.run(&[b"RPUSH", b"k", b"a"]);
7367        for (bad, want) in [
7368            (
7369                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7370                "-ERR numkeys should be greater than 0\r\n",
7371            ),
7372            (
7373                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7374                "-ERR numkeys should be greater than 0\r\n",
7375            ),
7376            // Two keys named and one given, so the word that should have been
7377            // the direction is a key and there is no direction left.
7378            (
7379                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7380                "-ERR syntax error\r\n",
7381            ),
7382            (
7383                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7384                "-ERR syntax error\r\n",
7385            ),
7386            (
7387                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7388                "-ERR syntax error\r\n",
7389            ),
7390            (
7391                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7392                "-ERR syntax error\r\n",
7393            ),
7394            // A count that is not a number at all gets the same sentence a zero
7395            // or a negative one gets, rather than the usual one about integers.
7396            (
7397                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7398                "-ERR count should be greater than 0\r\n",
7399            ),
7400            (
7401                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7402                "-ERR count should be greater than 0\r\n",
7403            ),
7404        ] {
7405            assert_eq!(f.run(bad), want, "for {bad:?}");
7406        }
7407        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7408    }
7409
7410    #[test]
7411    fn a_blocking_move_reads_its_directions_before_its_timeout() {
7412        let mut f = Fixture::new();
7413        // Both are wrong. Redis checks the directions first, so this is the
7414        // syntax error and not a complaint about the timeout.
7415        assert_eq!(
7416            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7417            "-ERR syntax error\r\n"
7418        );
7419        assert_eq!(
7420            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7421            "-ERR syntax error\r\n"
7422        );
7423    }
7424
7425    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
7426    /// wait, which is the same relationship every other command in this file has
7427    /// with the one it wraps.
7428    #[test]
7429    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7430        let mut f = Fixture::new();
7431        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7432        assert_eq!(
7433            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7434            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7435        );
7436        assert_eq!(
7437            f.run(&[
7438                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7439            ]),
7440            bulks(&["e", "d"])
7441        );
7442        assert_eq!(
7443            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7444            bulks(&["a", "e", "d"])
7445        );
7446        // `EXACTLY` with enough there does not wait either.
7447        assert_eq!(
7448            f.run(&[
7449                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7450            ]),
7451            bulks(&["b", "c"])
7452        );
7453        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7454    }
7455
7456    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
7457    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
7458    /// whole block has arrived.
7459    #[test]
7460    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7461        let mut f = Fixture::new();
7462        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7463        // Two there and three asked for. `COUNT` takes the two.
7464        assert_eq!(
7465            f.flow(&[
7466                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7467            ]),
7468            (Flow::Continue, bulks(&["a", "b"]))
7469        );
7470
7471        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7472        // The same line with `EXACTLY` parks instead, and takes nothing on the
7473        // way past.
7474        assert_eq!(
7475            f.flow(&[
7476                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7477            ])
7478            .0,
7479            Flow::Block
7480        );
7481        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7482    }
7483
7484    #[test]
7485    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7486        let mut f = Fixture::new();
7487        let syntax = "-ERR syntax error\r\n";
7488        // All three are wrong and the directions are read first.
7489        assert_eq!(
7490            f.run(&[
7491                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7492            ]),
7493            syntax
7494        );
7495        // Directions fine, timeout and count both wrong, so the timeout wins.
7496        assert_eq!(
7497            f.run(&[
7498                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7499            ]),
7500            "-ERR timeout is not a float or out of range\r\n"
7501        );
7502        assert_eq!(
7503            f.run(&[
7504                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7505            ]),
7506            "-ERR timeout is negative\r\n"
7507        );
7508        // And with the timeout fine, the count before the ordering word.
7509        assert_eq!(
7510            f.run(&[
7511                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7512            ]),
7513            "-ERR count should be greater than 0\r\n"
7514        );
7515        assert_eq!(
7516            f.run(&[
7517                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7518            ]),
7519            syntax
7520        );
7521        // Seven and eight arguments are neither of the two forms, the same way
7522        // six and seven are for `LMOVEM`.
7523        assert_eq!(
7524            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7525            syntax
7526        );
7527        assert_eq!(
7528            f.run(&[
7529                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7530            ]),
7531            syntax
7532        );
7533    }
7534
7535    /// The four ways a blocking command sees a key of another type, and the one
7536    /// way it does not.
7537    #[test]
7538    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7539        let mut f = Fixture::new();
7540        f.run(&[b"SET", b"S", b"v"]);
7541        f.run(&[b"RPUSH", b"D", b"x"]);
7542        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7543
7544        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7545        // Every key is checked even when an earlier one would have blocked, so
7546        // an empty key in front of a string does not hide it.
7547        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7548        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7549        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7550        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7551        // The destination, which is only reached because the source has
7552        // something in it.
7553        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7554        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7555        assert_eq!(
7556            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7557            wrong
7558        );
7559        assert_eq!(
7560            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7561            wrong
7562        );
7563
7564        // And the one that does not: an empty source means the destination is
7565        // never looked at, so this waits rather than erroring, and on a real
7566        // server it times out.
7567        assert_eq!(
7568            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7569                .0,
7570            Flow::Block
7571        );
7572        // `BLMOVEM` has a second way of not being ready, and it hides the
7573        // destination just as well: the source is a list with two elements in it
7574        // and `EXACTLY` wants three, so the string never gets looked at.
7575        assert_eq!(
7576            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7577                .0,
7578            Flow::Block
7579        );
7580        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7581        assert_eq!(
7582            f.flow(&[
7583                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7584            ])
7585            .0,
7586            Flow::Block
7587        );
7588    }
7589
7590    /// The same churn the set and the string get, because a list that leaks a
7591    /// chunk per push looks exactly like one that does not until it has run for
7592    /// an afternoon.
7593    #[test]
7594    fn churning_lists_does_not_grow_the_server() {
7595        let mut f = Fixture::new();
7596        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
7597        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
7598            .into_iter()
7599            .chain(vals.iter().map(Vec::as_slice))
7600            .collect();
7601
7602        f.run(&args);
7603        f.run(&[b"DEL", b"k"]);
7604        f.server.compact_step();
7605        let after_first = f.server.memory_bytes();
7606
7607        for _ in 0..200 {
7608            f.run(&args);
7609            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
7610            f.server.compact_step();
7611        }
7612        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7613        assert!(
7614            f.server.memory_bytes() <= after_first * 2,
7615            "held {} after two hundred passes against {after_first} after one",
7616            f.server.memory_bytes()
7617        );
7618    }
7619
7620    // ------------------------------------------------------------ sorted set
7621
7622    #[test]
7623    fn a_sorted_set_takes_scores_and_gives_them_back() {
7624        let mut f = Fixture::new();
7625        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
7626        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
7627        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
7628        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
7629        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
7630        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
7631        assert_eq!(
7632            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
7633            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
7634        );
7635        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
7636        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
7637        // The key goes when the last member does.
7638        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
7639        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7640    }
7641
7642    #[test]
7643    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
7644        let mut f = Fixture::new();
7645        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
7646        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
7647        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
7648        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
7649
7650        f.out = Out::new(Proto::Resp3);
7651        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
7652        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
7653        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
7654        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
7655    }
7656
7657    #[test]
7658    fn the_zadd_options_gate_what_gets_written() {
7659        let mut f = Fixture::new();
7660        f.run(&[b"ZADD", b"z", b"5", b"a"]);
7661        // NX leaves a member that is there alone, XX will not create one.
7662        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
7663        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
7664        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
7665        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
7666        // GT and LT only move a score one way.
7667        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
7668        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
7669        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
7670        // CH counts a moved score and plain ZADD does not.
7671        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
7672        assert_eq!(
7673            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
7674            ":2\r\n"
7675        );
7676    }
7677
7678    #[test]
7679    fn zadd_incr_answers_a_score_or_nothing_at_all() {
7680        let mut f = Fixture::new();
7681        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
7682        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
7683        // A gate that refuses is the string nil, because the reply it stands in
7684        // for is a score.
7685        assert_eq!(
7686            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
7687            "$-1\r\n"
7688        );
7689        assert_eq!(
7690            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
7691            "$-1\r\n"
7692        );
7693        assert_eq!(
7694            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
7695            "$-1\r\n"
7696        );
7697        assert_eq!(
7698            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
7699            "$1\r\n8\r\n"
7700        );
7701        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
7702        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
7703    }
7704
7705    #[test]
7706    fn the_two_infinities_will_not_be_added_together() {
7707        let mut f = Fixture::new();
7708        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
7709        let nan = "-ERR resulting score is not a number (NaN)\r\n";
7710        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
7711        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
7712        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
7713        // And a key made for an increment that then fails does not stay behind.
7714        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
7715    }
7716
7717    #[test]
7718    fn zadd_says_its_mistakes_the_way_redis_says_them() {
7719        let mut f = Fixture::new();
7720        // The pairs are counted before the options are looked at, so this is a
7721        // syntax error about having none and not a complaint about NX and XX.
7722        assert_eq!(
7723            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
7724            "-ERR syntax error\r\n"
7725        );
7726        assert_eq!(
7727            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
7728            "-ERR XX and NX options at the same time are not compatible\r\n"
7729        );
7730        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
7731        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
7732        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
7733        assert_eq!(
7734            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
7735            "-ERR INCR option supports a single increment-element pair\r\n"
7736        );
7737        // An odd number of arguments after the options.
7738        assert_eq!(
7739            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
7740            "-ERR syntax error\r\n"
7741        );
7742        // Every score is read before the first is stored.
7743        assert_eq!(
7744            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
7745            "-ERR value is not a valid float\r\n"
7746        );
7747        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7748    }
7749
7750    #[test]
7751    fn a_rank_says_where_a_member_sits_from_either_end() {
7752        let mut f = Fixture::new();
7753        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7754        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
7755        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
7756        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
7757        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
7758        // WITHSCORE changes both shapes: the answer and the nothing.
7759        assert_eq!(
7760            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
7761            "*2\r\n:1\r\n$1\r\n2\r\n"
7762        );
7763        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
7764        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
7765        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
7766        // A bad option is a syntax error and one argument too many is an arity
7767        // error, which is Redis's split.
7768        assert_eq!(
7769            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
7770            "-ERR syntax error\r\n"
7771        );
7772        assert_eq!(
7773            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
7774            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
7775        );
7776    }
7777
7778    #[test]
7779    fn the_two_counts_read_their_two_kinds_of_bound() {
7780        let mut f = Fixture::new();
7781        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7782        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
7783        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
7784        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
7785        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
7786        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
7787        assert_eq!(
7788            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
7789            "-ERR min or max is not a float\r\n"
7790        );
7791
7792        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
7793        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
7794        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
7795        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
7796        // A bare member is not a bound, because a member can start with any
7797        // byte and there would be no way to say the bracket if it were optional.
7798        assert_eq!(
7799            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
7800            "-ERR min or max not valid string range item\r\n"
7801        );
7802    }
7803
7804    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
7805    ///
7806    /// Every byte in here was read off a real 8.10.1 rather than worked out,
7807    /// because the interesting part of this command is not what it selects, it
7808    /// is which of the two ends the client is expected to name first.
7809    #[test]
7810    fn one_range_command_selects_by_rank_or_score_or_name() {
7811        let mut f = Fixture::new();
7812        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7813        assert_eq!(
7814            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7815            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7816        );
7817        assert_eq!(
7818            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
7819            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7820        );
7821        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
7822        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
7823        // REV over ranks reverses the walk and leaves the two arguments alone,
7824        // because a rank counts from the end the walk starts at.
7825        assert_eq!(
7826            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
7827            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7828        );
7829        assert_eq!(
7830            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
7831            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7832        );
7833        // And REV over scores does swap them, since a bound does not count from
7834        // anywhere. This is the one line of the parse that tells the two apart.
7835        assert_eq!(
7836            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
7837            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7838        );
7839        assert_eq!(
7840            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
7841            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7842        );
7843        assert_eq!(
7844            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
7845            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7846        );
7847    }
7848
7849    /// The older spellings, which are the same six windows with the mode in the
7850    /// name and the high end named first on the three that go backwards.
7851    #[test]
7852    fn the_older_range_spellings_name_their_high_end_first() {
7853        let mut f = Fixture::new();
7854        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7855        assert_eq!(
7856            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
7857            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7858        );
7859        assert_eq!(
7860            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
7861            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7862        );
7863        assert_eq!(
7864            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
7865            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7866        );
7867        assert_eq!(
7868            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
7869            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7870        );
7871        // The two arguments the wrong way round is an empty answer and not an
7872        // error, which is what the swap being in the parse rather than in the
7873        // window buys.
7874        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
7875        assert_eq!(
7876            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
7877            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7878        );
7879        assert_eq!(
7880            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
7881            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
7882        );
7883        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
7884        // way of spelling the mode, they are a syntax error.
7885        for cmd in [
7886            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
7887            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
7888            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
7889        ] {
7890            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
7891        }
7892    }
7893
7894    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
7895    /// only some of them accept.
7896    #[test]
7897    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
7898        let mut f = Fixture::new();
7899        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7900        assert_eq!(
7901            f.run(&[
7902                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
7903            ]),
7904            "*1\r\n$1\r\nb\r\n"
7905        );
7906        // A negative offset skips past everything, a negative count is no bound.
7907        assert_eq!(
7908            f.run(&[
7909                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
7910            ]),
7911            "*0\r\n"
7912        );
7913        assert_eq!(
7914            f.run(&[
7915                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
7916            ]),
7917            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7918        );
7919        // The two options in either order, which falls out of the parse loop.
7920        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";
7921        assert_eq!(
7922            f.run(&[
7923                b"ZRANGEBYSCORE",
7924                b"z",
7925                b"1",
7926                b"3",
7927                b"WITHSCORES",
7928                b"LIMIT",
7929                b"0",
7930                b"2"
7931            ]),
7932            both
7933        );
7934        assert_eq!(
7935            f.run(&[
7936                b"ZRANGEBYSCORE",
7937                b"z",
7938                b"1",
7939                b"3",
7940                b"LIMIT",
7941                b"0",
7942                b"2",
7943                b"WITHSCORES"
7944            ]),
7945            both
7946        );
7947        // LIMIT on a range by rank is refused after the whole option list has
7948        // been read, so this complains about LIMIT and not about WITHSCORES.
7949        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
7950        assert_eq!(
7951            f.run(&[
7952                b"ZREVRANGE",
7953                b"z",
7954                b"0",
7955                b"-1",
7956                b"WITHSCORES",
7957                b"LIMIT",
7958                b"0",
7959                b"1"
7960            ]),
7961            needs_by
7962        );
7963        assert_eq!(
7964            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
7965            needs_by
7966        );
7967        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
7968        assert_eq!(
7969            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
7970            not_bylex
7971        );
7972        assert_eq!(
7973            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
7974            not_bylex
7975        );
7976        // Two modes at once, an option nobody knows, a LIMIT missing its count,
7977        // and the three number errors, which are three different sentences.
7978        for cmd in [
7979            &[
7980                b"ZRANGE".as_slice(),
7981                b"z",
7982                b"0",
7983                b"-1",
7984                b"BYSCORE",
7985                b"BYLEX",
7986            ][..],
7987            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
7988            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
7989        ] {
7990            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
7991        }
7992        assert_eq!(
7993            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
7994            "-ERR min or max is not a float\r\n"
7995        );
7996        assert_eq!(
7997            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
7998            "-ERR min or max not valid string range item\r\n"
7999        );
8000        assert_eq!(
8001            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8002            "-ERR value is not an integer or out of range\r\n"
8003        );
8004    }
8005
8006    /// `WITHSCORES` is the one place in this group where the two protocols
8007    /// disagree about the shape of the reply and not just the type of a value.
8008    #[test]
8009    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8010        let mut f = Fixture::new();
8011        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8012        assert_eq!(
8013            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8014            "*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"
8015        );
8016        f.out = Out::new(Proto::Resp3);
8017        assert_eq!(
8018            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8019            "*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"
8020        );
8021        assert_eq!(
8022            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8023            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8024        );
8025    }
8026
8027    /// The store form, which is the same parse with the destination in front.
8028    #[test]
8029    fn a_range_store_writes_the_window_into_another_key() {
8030        let mut f = Fixture::new();
8031        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8032        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8033        // A window that selects nothing deletes the destination rather than
8034        // leaving an empty sorted set, because an empty one does not exist.
8035        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8036        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8037        assert_eq!(
8038            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8039            ":2\r\n"
8040        );
8041        assert_eq!(
8042            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8043            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8044        );
8045        // The destination is allowed to be the source, because the result is
8046        // built whole before anything is written over.
8047        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8048        assert_eq!(
8049            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8050            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8051        );
8052        // It takes every option ZRANGE takes except WITHSCORES, which is a
8053        // plain syntax error here and not the sentence about BYLEX.
8054        assert_eq!(
8055            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8056            "-ERR syntax error\r\n"
8057        );
8058    }
8059
8060    /// The three removals, which are the read side's window with the walk
8061    /// turned into a removal and no options at all.
8062    #[test]
8063    fn the_three_removals_share_their_window_with_the_reads() {
8064        let mut f = Fixture::new();
8065        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8066        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8067        assert_eq!(
8068            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8069            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8070        );
8071        assert_eq!(
8072            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8073            ":1\r\n"
8074        );
8075        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8076        // The last member going takes the key with it.
8077        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8078        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8079        assert_eq!(
8080            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8081            ":0\r\n"
8082        );
8083        assert_eq!(
8084            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8085            "-ERR value is not an integer or out of range\r\n"
8086        );
8087    }
8088
8089    /// The algebra, which is one gather and three names for it.
8090    #[test]
8091    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8092        let mut f = Fixture::new();
8093        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8094        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8095        assert_eq!(
8096            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8097            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8098        );
8099        // The scores are added where a member is in both, and the answer comes
8100        // out in the order those combined scores put it in.
8101        assert_eq!(
8102            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8103            "*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"
8104        );
8105        assert_eq!(
8106            f.run(&[
8107                b"ZUNION",
8108                b"2",
8109                b"z",
8110                b"y",
8111                b"WEIGHTS",
8112                b"2",
8113                b"3",
8114                b"WITHSCORES"
8115            ]),
8116            "*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"
8117        );
8118        assert_eq!(
8119            f.run(&[
8120                b"ZUNION",
8121                b"2",
8122                b"z",
8123                b"y",
8124                b"AGGREGATE",
8125                b"MIN",
8126                b"WITHSCORES"
8127            ]),
8128            "*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"
8129        );
8130        assert_eq!(
8131            f.run(&[
8132                b"ZUNION",
8133                b"2",
8134                b"z",
8135                b"y",
8136                b"AGGREGATE",
8137                b"MAX",
8138                b"WITHSCORES"
8139            ]),
8140            "*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"
8141        );
8142        assert_eq!(
8143            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8144            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8145        );
8146        assert_eq!(
8147            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8148            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8149        );
8150        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8151        // A plain set is an input, and it behaves as a sorted set in which
8152        // every member scores one.
8153        f.run(&[b"SADD", b"p", b"a", b"d"]);
8154        assert_eq!(
8155            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8156            "*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"
8157        );
8158        // A difference never combines two scores, so it has nothing for either
8159        // of the two options to do and refuses both.
8160        for cmd in [
8161            &[
8162                b"ZDIFF".as_slice(),
8163                b"2",
8164                b"z",
8165                b"y",
8166                b"WEIGHTS",
8167                b"1",
8168                b"1",
8169            ][..],
8170            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8171        ] {
8172            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8173        }
8174    }
8175
8176    /// The count of keys, which is what lets a key be named `WEIGHTS`.
8177    #[test]
8178    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8179        let mut f = Fixture::new();
8180        f.run(&[b"ZADD", b"z", b"1", b"a"]);
8181        f.run(&[b"ZADD", b"y", b"2", b"b"]);
8182        // Redis names the command in this one, so each spelling says its own.
8183        assert_eq!(
8184            f.run(&[b"ZUNION", b"0", b"z"]),
8185            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8186        );
8187        assert_eq!(
8188            f.run(&[b"ZUNION", b"-1", b"z"]),
8189            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8190        );
8191        assert_eq!(
8192            f.run(&[b"ZINTERCARD", b"0", b"z"]),
8193            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8194        );
8195        // A count bigger than the line is a plain syntax error, which reads
8196        // oddly and is what Redis says.
8197        assert_eq!(
8198            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8199            "-ERR syntax error\r\n"
8200        );
8201        assert_eq!(
8202            f.run(&[b"ZUNION", b"x", b"z"]),
8203            "-ERR value is not an integer or out of range\r\n"
8204        );
8205        // A WEIGHTS list that is not one per key is a syntax error, and a
8206        // weight that is not a number gets a sentence of its own.
8207        assert_eq!(
8208            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8209            "-ERR syntax error\r\n"
8210        );
8211        assert_eq!(
8212            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8213            "-ERR weight value is not a float\r\n"
8214        );
8215        assert_eq!(
8216            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8217            "-ERR syntax error\r\n"
8218        );
8219    }
8220
8221    /// The three store forms, which answer a count and take no WITHSCORES.
8222    #[test]
8223    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8224        let mut f = Fixture::new();
8225        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8226        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8227        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8228        assert_eq!(
8229            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8230            "*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"
8231        );
8232        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8233        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8234        // An empty result deletes the destination rather than leaving an empty
8235        // sorted set, because an empty one does not exist.
8236        assert_eq!(
8237            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8238            ":0\r\n"
8239        );
8240        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8241        // The destination is allowed to name its own source.
8242        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8243        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8244        for cmd in [
8245            &[
8246                b"ZUNIONSTORE".as_slice(),
8247                b"d",
8248                b"2",
8249                b"z",
8250                b"y",
8251                b"WITHSCORES",
8252            ][..],
8253            &[
8254                b"ZDIFFSTORE",
8255                b"d",
8256                b"2",
8257                b"z",
8258                b"y",
8259                b"WEIGHTS",
8260                b"1",
8261                b"1",
8262            ],
8263        ] {
8264            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8265        }
8266    }
8267
8268    /// `ZINTERCARD`, which counts without building anything.
8269    #[test]
8270    fn intercard_counts_and_stops_at_its_limit() {
8271        let mut f = Fixture::new();
8272        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8273        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8274        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8275        // A limit of zero is no limit, which is Redis's reading of it.
8276        assert_eq!(
8277            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8278            ":2\r\n"
8279        );
8280        assert_eq!(
8281            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8282            ":1\r\n"
8283        );
8284        // A negative limit and a limit that is not a number at all get the same
8285        // sentence, which looks like a mistake in Redis and is copied as one.
8286        let bad = "-ERR LIMIT can't be negative\r\n";
8287        assert_eq!(
8288            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8289            bad
8290        );
8291        assert_eq!(
8292            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8293            bad
8294        );
8295        for cmd in [
8296            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8297            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8298            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8299        ] {
8300            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8301        }
8302    }
8303
8304    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
8305    #[test]
8306    fn a_draw_answers_one_member_or_an_array_of_them() {
8307        let mut f = Fixture::new();
8308        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8309        // No count is one member or a nil, a count is an array that may be
8310        // empty, and those are two reply types the client has to tell apart.
8311        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8312        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8313        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8314        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8315        // A positive count draws without replacement, so a count over the size
8316        // answers the whole set and never a member twice.
8317        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8318        assert!(all.starts_with("*3\r\n"), "{all}");
8319        for m in ["a", "b", "c"] {
8320            assert!(all.contains(m), "{all}");
8321        }
8322        // A negative one draws with replacement and answers exactly as many as
8323        // it was asked for, whatever the size of the set.
8324        assert!(
8325            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8326            "five draws with replacement"
8327        );
8328        assert!(
8329            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8330                .starts_with("*4\r\n"),
8331            "two pairs, flat on RESP2"
8332        );
8333        f.out = Out::new(Proto::Resp3);
8334        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8335        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8336        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8337        f.out = Out::new(Proto::Resp2);
8338        assert_eq!(
8339            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8340            "-ERR syntax error\r\n"
8341        );
8342        assert_eq!(
8343            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8344            "-ERR value is not an integer or out of range\r\n"
8345        );
8346    }
8347
8348    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
8349    #[test]
8350    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8351        let mut f = Fixture::new();
8352        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8353        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";
8354        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8355        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8356        assert_eq!(
8357            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8358            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8359        );
8360        assert_eq!(
8361            f.run(&[b"ZSCAN", b"nokey", b"0"]),
8362            "*2\r\n$1\r\n0\r\n*0\r\n"
8363        );
8364        // A score stays a bulk string on RESP3, which is the one place the two
8365        // protocols agree about a score and everywhere else they do not.
8366        f.out = Out::new(Proto::Resp3);
8367        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8368        f.out = Out::new(Proto::Resp2);
8369        assert_eq!(
8370            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8371            "-ERR NOVALUES option can only be used in HSCAN\r\n"
8372        );
8373        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8374        assert_eq!(
8375            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8376            "-ERR syntax error\r\n"
8377        );
8378    }
8379
8380    /// The count is what decides the shape, and its value is not.
8381    #[test]
8382    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8383        let mut f = Fixture::new();
8384        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8385        // No count, so one flat pair, and the score is a bulk string on RESP2.
8386        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8387        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8388        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8389        // A count, so pairs, and on RESP2 they are flattened into one run.
8390        assert_eq!(
8391            f.run(&[b"ZPOPMIN", b"z", b"2"]),
8392            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8393        );
8394        // An empty array rather than a null, which is where a sorted set pop and
8395        // a list pop part company, and the same answer a count of zero gives.
8396        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8397        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8398        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8399        // The last member takes the key with it.
8400        assert_eq!(
8401            f.run(&[b"ZPOPMIN", b"z", b"9"]),
8402            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8403        );
8404        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8405
8406        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8407        f.out = Out::new(Proto::Resp3);
8408        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8409        assert_eq!(
8410            f.run(&[b"ZPOPMIN", b"z", b"1"]),
8411            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8412        );
8413        f.out = Out::new(Proto::Resp2);
8414        // Both of these are the range error rather than the usual sentence about
8415        // integers, which is the odd answer and so the one worth copying.
8416        let bad = "-ERR value is out of range, must be positive\r\n";
8417        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8418        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8419        assert_eq!(
8420            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8421            "-ERR syntax error\r\n"
8422        );
8423    }
8424
8425    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
8426    #[test]
8427    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8428        let mut f = Fixture::new();
8429        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8430        assert_eq!(
8431            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8432            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8433        );
8434        // Nested on RESP2 as well, because the key name is already in front of
8435        // the pairs and there is nothing left to flatten into.
8436        assert_eq!(
8437            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8438            "*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"
8439        );
8440        // A null array and not a null, the same as LMPOP.
8441        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8442        f.out = Out::new(Proto::Resp3);
8443        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8444        f.out = Out::new(Proto::Resp2);
8445        let numkeys = "-ERR numkeys should be greater than 0\r\n";
8446        for bad in [
8447            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8448            &[b"ZMPOP", b"-1", b"z", b"MIN"],
8449            &[b"ZMPOP", b"x", b"z", b"MIN"],
8450        ] {
8451            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8452        }
8453        let count = "-ERR count should be greater than 0\r\n";
8454        for bad in [
8455            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8456            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8457            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8458        ] {
8459            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8460        }
8461        let syntax = "-ERR syntax error\r\n";
8462        for bad in [
8463            // Two keys named and one given, so the word that should have been
8464            // the direction is a key and there is no direction left.
8465            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8466            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8467            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8468            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8469        ] {
8470            assert_eq!(f.run(bad), syntax, "{bad:?}");
8471        }
8472    }
8473
8474    /// The three that wait, when there is something there and they do not have
8475    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
8476    #[test]
8477    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8478        let mut f = Fixture::new();
8479        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8480        assert_eq!(
8481            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8482            (
8483                Flow::Continue,
8484                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8485            )
8486        );
8487        assert_eq!(
8488            f.run(&[b"BZPOPMAX", b"z", b"0"]),
8489            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8490        );
8491        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8492        assert_eq!(
8493            f.run(&[
8494                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8495            ]),
8496            "*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"
8497        );
8498        f.out = Out::new(Proto::Resp3);
8499        assert_eq!(
8500            f.run(&[b"BZPOPMIN", b"z", b"0"]),
8501            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8502        );
8503        f.out = Out::new(Proto::Resp2);
8504        // Nothing to take, so the client is parked and nothing was written.
8505        assert_eq!(
8506            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8507            (Flow::Block, String::new())
8508        );
8509        assert_eq!(
8510            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8511            (Flow::Block, String::new())
8512        );
8513        // The timeout is read before the key count, so this complains about the
8514        // timeout and not about the count.
8515        assert_eq!(
8516            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8517            "-ERR timeout is not a float or out of range\r\n"
8518        );
8519        assert_eq!(
8520            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8521            "-ERR numkeys should be greater than 0\r\n"
8522        );
8523        assert_eq!(
8524            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8525            "-ERR timeout is negative\r\n"
8526        );
8527    }
8528
8529    /// A parked sorted set client is served by whatever puts a member under one
8530    /// of its keys, and is not served by something of another type landing
8531    /// there.
8532    #[test]
8533    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8534        let mut f = Fixture::new();
8535        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8536        assert_eq!(f.server.waiters().len(), 1);
8537        // A string under the key is not what it asked for, so it stays parked
8538        // rather than being handed a WRONGTYPE on a command that was accepted.
8539        f.run(&[b"SET", b"z", b"v"]);
8540        let mut out = Out::new(Proto::Resp2);
8541        assert!(!f.server.serve_waiter(0, 0, &mut out));
8542        assert!(out.as_slice().is_empty());
8543        f.run(&[b"DEL", b"z"]);
8544        f.run(&[b"ZADD", b"z", b"5", b"m"]);
8545        assert!(f.server.serve_waiter(0, 0, &mut out));
8546        assert_eq!(
8547            core::str::from_utf8(out.as_slice()).expect("ascii"),
8548            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8549        );
8550        // And the member is gone, which is what makes a queue of workers on a
8551        // sorted set work at all.
8552        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8553    }
8554
8555    #[test]
8556    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8557        let mut f = Fixture::new();
8558        f.run(&[b"SET", b"s", b"v"]);
8559        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8560        for cmd in [
8561            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8562            &[b"ZINCRBY", b"s", b"1", b"a"],
8563            &[b"ZCARD", b"s"],
8564            &[b"ZSCORE", b"s", b"a"],
8565            &[b"ZMSCORE", b"s", b"a"],
8566            &[b"ZREM", b"s", b"a"],
8567            &[b"ZRANK", b"s", b"a"],
8568            &[b"ZREVRANK", b"s", b"a"],
8569            &[b"ZCOUNT", b"s", b"1", b"2"],
8570            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8571            &[b"ZRANGE", b"s", b"0", b"-1"],
8572            &[b"ZREVRANGE", b"s", b"0", b"-1"],
8573            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8574            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8575            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8576            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8577            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8578            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8579            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8580            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8581            &[b"ZUNION", b"1", b"s"],
8582            &[b"ZINTER", b"1", b"s"],
8583            &[b"ZDIFF", b"1", b"s"],
8584            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8585            &[b"ZINTERSTORE", b"d", b"1", b"s"],
8586            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8587            &[b"ZINTERCARD", b"1", b"s"],
8588            &[b"ZRANDMEMBER", b"s"],
8589            &[b"ZSCAN", b"s", b"0"],
8590            &[b"ZPOPMIN", b"s"],
8591            &[b"ZPOPMAX", b"s", b"2"],
8592            &[b"ZMPOP", b"1", b"s", b"MIN"],
8593            &[b"BZPOPMIN", b"s", b"0"],
8594            &[b"BZPOPMAX", b"s", b"0"],
8595            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
8596        ] {
8597            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
8598        }
8599        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
8600    }
8601
8602    /// The same churn the set, the string and the list get, because a sorted
8603    /// set that leaks a tree node per add looks exactly like one that does not
8604    /// until it has run for an afternoon.
8605    #[test]
8606    fn churning_sorted_sets_does_not_grow_the_server() {
8607        let mut f = Fixture::new();
8608        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
8609        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
8610        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
8611        for i in 0..200 {
8612            args.push(&scores[i]);
8613            args.push(&members[i]);
8614        }
8615
8616        f.run(&args);
8617        f.run(&[b"DEL", b"z"]);
8618        f.server.compact_step();
8619        let after_first = f.server.memory_bytes();
8620
8621        for _ in 0..200 {
8622            f.run(&args);
8623            f.run(&[b"DEL", b"z"]);
8624            f.server.compact_step();
8625        }
8626        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8627        assert!(
8628            f.server.memory_bytes() <= after_first * 2,
8629            "held {} after two hundred passes against {after_first} after one",
8630            f.server.memory_bytes()
8631        );
8632    }
8633
8634    // ------------------------------------------------------------------- geo
8635
8636    /// The three places every Redis geo example uses, and one more.
8637    ///
8638    /// Every reply this section asserts on came off a running 8.10.1 with these
8639    /// three loaded, byte for byte, including the number of digits in a
8640    /// coordinate and the four places on a distance.
8641    fn sicily(f: &mut Fixture) {
8642        f.run(&[
8643            b"GEOADD",
8644            b"Sicily",
8645            b"13.361389",
8646            b"38.115556",
8647            b"Palermo",
8648            b"15.087269",
8649            b"37.502669",
8650            b"Catania",
8651        ]);
8652        f.run(&[
8653            b"GEOADD",
8654            b"Sicily",
8655            b"13.583333",
8656            b"37.316667",
8657            b"Agrigento",
8658        ]);
8659    }
8660
8661    #[test]
8662    fn places_go_in_as_scores_and_come_back_as_positions() {
8663        let mut f = Fixture::new();
8664        assert_eq!(
8665            f.run(&[
8666                b"GEOADD",
8667                b"Sicily",
8668                b"13.361389",
8669                b"38.115556",
8670                b"Palermo",
8671                b"15.087269",
8672                b"37.502669",
8673                b"Catania"
8674            ]),
8675            ":2\r\n"
8676        );
8677        // A geo key is a sorted set and says so, which is not an implementation
8678        // detail either: a client removes a place with ZREM and counts them
8679        // with ZCARD, and the score is the number a real server stores.
8680        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
8681        assert_eq!(
8682            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
8683            "$16\r\n3479099956230698\r\n"
8684        );
8685        assert_eq!(
8686            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
8687            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
8688        );
8689        assert_eq!(
8690            f.run(&[
8691                b"GEOHASH",
8692                b"Sicily",
8693                b"Palermo",
8694                b"Catania",
8695                b"NonExisting"
8696            ]),
8697            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
8698        );
8699        // A key that is not there is an empty one, and the two nulls are not
8700        // the same null: GEOPOS answers the array one and GEOHASH the string
8701        // one, which a RESP2 client can tell apart.
8702        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
8703        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
8704    }
8705
8706    #[test]
8707    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
8708        let mut f = Fixture::new();
8709        sicily(&mut f);
8710        assert_eq!(
8711            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
8712            "$11\r\n166274.1516\r\n"
8713        );
8714        assert_eq!(
8715            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
8716            "$8\r\n166.2742\r\n"
8717        );
8718        assert_eq!(
8719            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
8720            "$8\r\n103.3182\r\n"
8721        );
8722        // A member that is not there and a key that is not there are the same
8723        // nil, and the unit is read before the key is looked up, so a bad unit
8724        // on a missing key is still an error.
8725        assert_eq!(
8726            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
8727            "$-1\r\n"
8728        );
8729        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
8730        assert_eq!(
8731            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
8732            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
8733        );
8734        assert_eq!(
8735            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
8736            "-ERR syntax error\r\n"
8737        );
8738    }
8739
8740    #[test]
8741    fn a_search_finds_what_is_inside_it_nearest_first() {
8742        let mut f = Fixture::new();
8743        sicily(&mut f);
8744        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
8745        assert_eq!(
8746            f.run(&[
8747                b"GEOSEARCH",
8748                b"Sicily",
8749                b"FROMLONLAT",
8750                b"15",
8751                b"37",
8752                b"BYRADIUS",
8753                b"200",
8754                b"km",
8755                b"ASC"
8756            ]),
8757            all
8758        );
8759        // The older spelling of the same search, which is the same nine boxes
8760        // and the same order.
8761        assert_eq!(
8762            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
8763            all
8764        );
8765        assert_eq!(
8766            f.run(&[
8767                b"GEORADIUS_RO",
8768                b"Sicily",
8769                b"15",
8770                b"37",
8771                b"200",
8772                b"km",
8773                b"ASC"
8774            ]),
8775            all
8776        );
8777        // A count with no ordering means the nearest ones, so DESC has to be
8778        // asked for to get the far end.
8779        assert_eq!(
8780            f.run(&[
8781                b"GEORADIUS",
8782                b"Sicily",
8783                b"15",
8784                b"37",
8785                b"200",
8786                b"km",
8787                b"DESC",
8788                b"COUNT",
8789                b"1"
8790            ]),
8791            "*1\r\n$7\r\nPalermo\r\n"
8792        );
8793        assert_eq!(
8794            f.run(&[
8795                b"GEORADIUS",
8796                b"Sicily",
8797                b"15",
8798                b"37",
8799                b"200",
8800                b"km",
8801                b"COUNT",
8802                b"1"
8803            ]),
8804            "*1\r\n$7\r\nCatania\r\n"
8805        );
8806        // Nothing inside a kilometre of that point, and nothing in a key that
8807        // is not there, and both are the empty array rather than an error.
8808        let empty = "*0\r\n";
8809        assert_eq!(
8810            f.run(&[
8811                b"GEOSEARCH",
8812                b"Sicily",
8813                b"FROMLONLAT",
8814                b"15",
8815                b"37",
8816                b"BYRADIUS",
8817                b"1",
8818                b"km"
8819            ]),
8820            empty
8821        );
8822        assert_eq!(
8823            f.run(&[
8824                b"GEOSEARCH",
8825                b"nokey",
8826                b"FROMLONLAT",
8827                b"15",
8828                b"37",
8829                b"BYRADIUS",
8830                b"1",
8831                b"km"
8832            ]),
8833            empty
8834        );
8835        assert_eq!(
8836            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
8837            empty
8838        );
8839    }
8840
8841    #[test]
8842    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
8843        let mut f = Fixture::new();
8844        sicily(&mut f);
8845        assert_eq!(
8846            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
8847            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8848        );
8849        // The member itself is nothing away from itself, which is where the
8850        // fixed point writer's zero shows up on the wire.
8851        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";
8852        assert_eq!(
8853            f.run(&[
8854                b"GEORADIUSBYMEMBER_RO",
8855                b"Sicily",
8856                b"Agrigento",
8857                b"100",
8858                b"km",
8859                b"WITHDIST"
8860            ]),
8861            with_dist
8862        );
8863        assert_eq!(
8864            f.run(&[
8865                b"GEOSEARCH",
8866                b"Sicily",
8867                b"FROMMEMBER",
8868                b"Agrigento",
8869                b"BYRADIUS",
8870                b"100",
8871                b"km",
8872                b"ASC",
8873                b"WITHDIST"
8874            ]),
8875            with_dist
8876        );
8877        assert_eq!(
8878            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
8879            "-ERR could not decode requested zset member\r\n"
8880        );
8881    }
8882
8883    #[test]
8884    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
8885        let mut f = Fixture::new();
8886        sicily(&mut f);
8887        // Three options asked for, so each result is a four element array of
8888        // the member, the distance, the hash and a pair. The order of the three
8889        // is Redis's and not the order they were written in the command.
8890        assert_eq!(
8891            f.run(&[
8892                b"GEOSEARCH",
8893                b"Sicily",
8894                b"FROMLONLAT",
8895                b"15",
8896                b"37",
8897                b"BYBOX",
8898                b"400",
8899                b"400",
8900                b"km",
8901                b"ASC",
8902                b"WITHCOORD",
8903                b"WITHDIST",
8904                b"WITHHASH"
8905            ]),
8906            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
8907             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
8908             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
8909             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
8910             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
8911             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
8912        );
8913    }
8914
8915    #[test]
8916    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
8917        let mut f = Fixture::new();
8918        sicily(&mut f);
8919        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
8920                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
8921                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
8922        assert_eq!(
8923            f.run(&[
8924                b"GEOSEARCHSTORE",
8925                b"dst",
8926                b"Sicily",
8927                b"FROMLONLAT",
8928                b"15",
8929                b"37",
8930                b"BYRADIUS",
8931                b"200",
8932                b"km",
8933                b"ASC"
8934            ]),
8935            ":3\r\n"
8936        );
8937        assert_eq!(
8938            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
8939            hashes
8940        );
8941        // The same again through the older spelling, which stores the same
8942        // scores, so a key written by either is a geo key.
8943        assert_eq!(
8944            f.run(&[
8945                b"GEORADIUS",
8946                b"Sicily",
8947                b"15",
8948                b"37",
8949                b"200",
8950                b"km",
8951                b"STORE",
8952                b"dst3"
8953            ]),
8954            ":3\r\n"
8955        );
8956        assert_eq!(
8957            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
8958            hashes
8959        );
8960        // STOREDIST stores the distance in the search unit instead, and those
8961        // are full doubles rather than the four places WITHDIST writes. The
8962        // numbers on the right are what 8.10.1 stored for this search, and they
8963        // are compared with a tolerance rather than byte for byte because the
8964        // last bit of a haversine is the platform's sin, cos and asin: this
8965        // machine and that one disagree in the sixteenth digit, and so do two
8966        // Redis builds. Everything a client actually reads back is four places
8967        // and is asserted exactly above.
8968        assert_eq!(
8969            f.run(&[
8970                b"GEOSEARCHSTORE",
8971                b"dst2",
8972                b"Sicily",
8973                b"FROMLONLAT",
8974                b"15",
8975                b"37",
8976                b"BYRADIUS",
8977                b"200",
8978                b"km",
8979                b"ASC",
8980                b"STOREDIST"
8981            ]),
8982            ":3\r\n"
8983        );
8984        for (member, want) in [
8985            ("Catania", 56.441_257_870_158_19),
8986            ("Agrigento", 130.423_487_067_147_14),
8987            ("Palermo", 190.442_429_847_757_92),
8988        ] {
8989            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
8990            let got: f64 = reply
8991                .trim_start_matches(|c: char| c != '\n')
8992                .trim()
8993                .parse()
8994                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
8995            assert!(
8996                (got - want).abs() < 1e-9,
8997                "{member} scored {got} not {want}"
8998            );
8999        }
9000        // The order they went in is the order the scores put them in, which is
9001        // the point of storing the distance rather than the hash.
9002        assert_eq!(
9003            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9004            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9005        );
9006        // A search that finds nothing takes the destination with it rather than
9007        // leaving what was there, and a source key that is not there is a
9008        // search that finds nothing.
9009        assert_eq!(
9010            f.run(&[
9011                b"GEOSEARCHSTORE",
9012                b"dst",
9013                b"nokey",
9014                b"FROMLONLAT",
9015                b"15",
9016                b"37",
9017                b"BYRADIUS",
9018                b"200",
9019                b"km"
9020            ]),
9021            ":0\r\n"
9022        );
9023        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9024    }
9025
9026    #[test]
9027    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9028        let mut f = Fixture::new();
9029        sicily(&mut f);
9030        // XX on a member that is already where it is changes nothing, and NX on
9031        // one that is there refuses to move it.
9032        assert_eq!(
9033            f.run(&[
9034                b"GEOADD",
9035                b"Sicily",
9036                b"XX",
9037                b"CH",
9038                b"13.361389",
9039                b"38.115556",
9040                b"Palermo"
9041            ]),
9042            ":0\r\n"
9043        );
9044        assert_eq!(
9045            f.run(&[
9046                b"GEOADD",
9047                b"Sicily",
9048                b"NX",
9049                b"13.361389",
9050                b"38.9",
9051                b"Palermo"
9052            ]),
9053            ":0\r\n"
9054        );
9055        assert_eq!(
9056            f.run(&[
9057                b"GEOADD",
9058                b"Sicily",
9059                b"CH",
9060                b"13.361389",
9061                b"38.9",
9062                b"Palermo"
9063            ]),
9064            ":1\r\n"
9065        );
9066        // Out of range, and nothing is stored: the whole call is refused rather
9067        // than the good pairs going in and the bad one stopping it.
9068        assert_eq!(
9069            f.run(&[
9070                b"GEOADD",
9071                b"new",
9072                b"13.361389",
9073                b"38.115556",
9074                b"here",
9075                b"181",
9076                b"38",
9077                b"there"
9078            ]),
9079            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9080        );
9081        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9082        assert_eq!(
9083            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9084            "-ERR value is not a valid float\r\n"
9085        );
9086        // The count of triples is checked before the two gates are, and a call
9087        // with no triples at all reaches the same sentence.
9088        assert_eq!(
9089            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9090            "-ERR syntax error\r\n"
9091        );
9092        assert_eq!(
9093            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9094            "-ERR syntax error\r\n"
9095        );
9096        assert_eq!(
9097            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9098            "-ERR syntax error\r\n"
9099        );
9100        assert_eq!(
9101            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9102            "-ERR wrong number of arguments for 'geoadd' command\r\n"
9103        );
9104    }
9105
9106    /// The sentences a search answers, which are its contract as much as the
9107    /// results are.
9108    #[test]
9109    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9110        let mut f = Fixture::new();
9111        sicily(&mut f);
9112        let cases: &[(&[&[u8]], &str)] = &[
9113            (
9114                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9115                "-ERR need numeric radius\r\n",
9116            ),
9117            (
9118                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9119                "-ERR radius cannot be negative\r\n",
9120            ),
9121            (
9122                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9123                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9124            ),
9125            (
9126                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9127                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9128            ),
9129            (
9130                &[
9131                    b"GEOSEARCH",
9132                    b"Sicily",
9133                    b"FROMLONLAT",
9134                    b"15",
9135                    b"37",
9136                    b"BYBOX",
9137                    b"x",
9138                    b"1",
9139                    b"km",
9140                ],
9141                "-ERR need numeric width\r\n",
9142            ),
9143            (
9144                &[
9145                    b"GEOSEARCH",
9146                    b"Sicily",
9147                    b"FROMLONLAT",
9148                    b"15",
9149                    b"37",
9150                    b"BYBOX",
9151                    b"1",
9152                    b"y",
9153                    b"km",
9154                ],
9155                "-ERR need numeric height\r\n",
9156            ),
9157            (
9158                &[
9159                    b"GEOSEARCH",
9160                    b"Sicily",
9161                    b"FROMLONLAT",
9162                    b"15",
9163                    b"37",
9164                    b"BYBOX",
9165                    b"-1",
9166                    b"1",
9167                    b"km",
9168                ],
9169                "-ERR height or width cannot be negative\r\n",
9170            ),
9171            (
9172                &[
9173                    b"GEOSEARCH",
9174                    b"Sicily",
9175                    b"FROMLONLAT",
9176                    b"15",
9177                    b"37",
9178                    b"BYRADIUS",
9179                    b"1",
9180                    b"km",
9181                    b"ANY",
9182                ],
9183                "-ERR the ANY argument requires COUNT argument\r\n",
9184            ),
9185            (
9186                &[
9187                    b"GEOSEARCH",
9188                    b"Sicily",
9189                    b"FROMLONLAT",
9190                    b"15",
9191                    b"37",
9192                    b"BYRADIUS",
9193                    b"1",
9194                    b"km",
9195                    b"COUNT",
9196                    b"0",
9197                ],
9198                "-ERR COUNT must be > 0\r\n",
9199            ),
9200            (
9201                &[
9202                    b"GEOSEARCH",
9203                    b"Sicily",
9204                    b"BYRADIUS",
9205                    b"1",
9206                    b"km",
9207                    b"BYBOX",
9208                    b"1",
9209                    b"1",
9210                    b"km",
9211                ],
9212                "-ERR syntax error\r\n",
9213            ),
9214            (
9215                &[
9216                    b"GEOSEARCH",
9217                    b"Sicily",
9218                    b"FROMMEMBER",
9219                    b"Palermo",
9220                    b"FROMLONLAT",
9221                    b"1",
9222                    b"2",
9223                    b"BYRADIUS",
9224                    b"1",
9225                    b"km",
9226                ],
9227                "-ERR syntax error\r\n",
9228            ),
9229            // The two options a GEOSEARCH cannot leave out, each with its own
9230            // sentence, and the command quoted the way the client spelled it.
9231            (
9232                &[
9233                    b"geosearch",
9234                    b"Sicily",
9235                    b"BYRADIUS",
9236                    b"1",
9237                    b"km",
9238                    b"ASC",
9239                    b"WITHDIST",
9240                ],
9241                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9242            ),
9243            (
9244                &[
9245                    b"GEOSEARCH",
9246                    b"Sicily",
9247                    b"FROMLONLAT",
9248                    b"15",
9249                    b"37",
9250                    b"ASC",
9251                    b"WITHDIST",
9252                ],
9253                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9254            ),
9255            // A store cannot also be asked for the distance, and the two
9256            // families name themselves differently in the same sentence.
9257            (
9258                &[
9259                    b"GEOSEARCHSTORE",
9260                    b"d",
9261                    b"Sicily",
9262                    b"FROMLONLAT",
9263                    b"15",
9264                    b"37",
9265                    b"BYRADIUS",
9266                    b"1",
9267                    b"km",
9268                    b"WITHCOORD",
9269                ],
9270                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9271            ),
9272            (
9273                &[
9274                    b"GEORADIUS",
9275                    b"Sicily",
9276                    b"15",
9277                    b"37",
9278                    b"1",
9279                    b"km",
9280                    b"WITHDIST",
9281                    b"STORE",
9282                    b"d",
9283                ],
9284                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9285            ),
9286            // The read only forms have no store at all, so the word is a stray
9287            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
9288            (
9289                &[
9290                    b"GEORADIUS_RO",
9291                    b"Sicily",
9292                    b"15",
9293                    b"37",
9294                    b"1",
9295                    b"km",
9296                    b"STORE",
9297                    b"d",
9298                ],
9299                "-ERR syntax error\r\n",
9300            ),
9301            (
9302                &[
9303                    b"GEOSEARCH",
9304                    b"Sicily",
9305                    b"FROMLONLAT",
9306                    b"15",
9307                    b"37",
9308                    b"BYRADIUS",
9309                    b"1",
9310                    b"km",
9311                    b"STOREDIST",
9312                ],
9313                "-ERR syntax error\r\n",
9314            ),
9315        ];
9316        for (parts, want) in cases {
9317            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9318        }
9319    }
9320
9321    /// A wrong type wins over a bad argument, because the key is looked up
9322    /// first, and every one of the ten says the same thing about it.
9323    #[test]
9324    fn every_geo_command_says_wrongtype() {
9325        let mut f = Fixture::new();
9326        f.run(&[b"SET", b"s", b"v"]);
9327        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9328        let cases: &[&[&[u8]]] = &[
9329            &[b"GEOADD", b"s", b"13", b"38", b"m"],
9330            &[b"GEOPOS", b"s", b"m"],
9331            &[b"GEOHASH", b"s", b"m"],
9332            &[b"GEODIST", b"s", b"a", b"b"],
9333            &[
9334                b"GEOSEARCH",
9335                b"s",
9336                b"FROMLONLAT",
9337                b"15",
9338                b"37",
9339                b"BYRADIUS",
9340                b"1",
9341                b"km",
9342            ],
9343            &[
9344                b"GEOSEARCHSTORE",
9345                b"d",
9346                b"s",
9347                b"FROMLONLAT",
9348                b"15",
9349                b"37",
9350                b"BYRADIUS",
9351                b"1",
9352                b"km",
9353            ],
9354            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9355            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9356            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9357            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9358        ];
9359        for case in cases {
9360            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9361        }
9362        // And it wins over an argument that will not parse, which is the whole
9363        // reason the lookup comes first.
9364        assert_eq!(
9365            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9366            wrong
9367        );
9368    }
9369
9370    // ----------------------------------------------------------------- array
9371
9372    #[test]
9373    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9374        let mut f = Fixture::new();
9375        // Three consecutive positions from a high index, and the reply is how
9376        // many of them were empty before rather than how many were written.
9377        assert_eq!(
9378            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9379            ":3\r\n"
9380        );
9381        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9382        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9383        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9384        // A hole and a key that is not there are the same answer.
9385        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9386        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9387        assert_eq!(
9388            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9389            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9390        );
9391        // Scattered pairs in one command, last write wins within it.
9392        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9393        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9394    }
9395
9396    /// The two numbers an array reports are not the same number, and one of
9397    /// them does not fit a signed integer.
9398    #[test]
9399    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9400        let mut f = Fixture::new();
9401        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9402        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9403        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9404        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9405        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9406        // Deleting in the middle leaves the high water mark where it was.
9407        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9408        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9409        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9410
9411        // The top of the space is addressable, and its length is a number with
9412        // bit sixty three set, so the reply has to be unsigned or it comes back
9413        // negative.
9414        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9415        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9416        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9417        // And one past it does not exist, so a write that would reach it fails
9418        // before any of it lands.
9419        assert_eq!(
9420            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9421            "-ERR array index overflow\r\n"
9422        );
9423        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9424    }
9425
9426    /// One reply per position and not one per element, which is the whole
9427    /// reason the range is capped.
9428    #[test]
9429    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9430        let mut f = Fixture::new();
9431        f.run(&[b"ARSET", b"a", b"1", b"x"]);
9432        assert_eq!(
9433            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9434            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9435        );
9436        // The two ends may come in either order, and the answer is reversed
9437        // rather than empty.
9438        assert_eq!(
9439            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9440            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9441        );
9442        // A key that is not there reads like an array of nothing but holes.
9443        assert_eq!(
9444            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9445            "*2\r\n$-1\r\n$-1\r\n"
9446        );
9447        // A range wider than a million positions is refused and not trimmed,
9448        // because against a missing key it is a request for as many nulls as
9449        // the range is wide.
9450        assert_eq!(
9451            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9452            "-ERR range exceeds maximum of 1000000 items\r\n"
9453        );
9454    }
9455
9456    /// Every index in the argument list is read before the key is touched, so
9457    /// a bad one at the end leaves nothing half written.
9458    #[test]
9459    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9460        let mut f = Fixture::new();
9461        assert_eq!(
9462            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9463            "-ERR invalid array index\r\n"
9464        );
9465        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9466        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9467        assert_eq!(
9468            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9469            "-ERR invalid array index\r\n"
9470        );
9471        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9472        // An index is unsigned here, so the numbers a list would take are not
9473        // the last element, they are errors.
9474        assert_eq!(
9475            f.run(&[b"ARGET", b"a", b"-1"]),
9476            "-ERR invalid array index\r\n"
9477        );
9478        // And a pair list with an odd tail is an arity error rather than a
9479        // syntax one.
9480        assert_eq!(
9481            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9482            "-ERR wrong number of arguments for 'armset' command\r\n"
9483        );
9484        assert_eq!(
9485            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9486            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9487        );
9488    }
9489
9490    #[test]
9491    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9492        let mut f = Fixture::new();
9493        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9494        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9495        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9496        // Two ranges in one command, and the second one covers the whole space
9497        // without walking it.
9498        assert_eq!(
9499            f.run(&[
9500                b"ARDELRANGE",
9501                b"a",
9502                b"100",
9503                b"200",
9504                b"0",
9505                b"18446744073709551614"
9506            ]),
9507            ":2\r\n"
9508        );
9509        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9510        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9511        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9512    }
9513
9514    /// A value goes out as the bytes it came in as, whichever of the three ways
9515    /// the array found to store it.
9516    #[test]
9517    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9518        let mut f = Fixture::new();
9519        let long = vec![b'v'; 200];
9520        f.run(&[
9521            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9522            b"short", b"5", &long, b"6", b"-0",
9523        ]);
9524        // 42 is an integer, 007 is not one because it does not print back the
9525        // same, 3.5 survives a double and 3.14 does not, and the last two are a
9526        // word packed string and a blob.
9527        assert_eq!(
9528            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9529            format!(
9530                "*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",
9531                String::from_utf8_lossy(&long)
9532            )
9533        );
9534    }
9535
9536    #[test]
9537    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9538        let mut f = Fixture::new();
9539        f.run(&[b"ARSET", b"a", b"0", b"x"]);
9540        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9541        assert_eq!(
9542            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9543            "$12\r\nsliced-array\r\n"
9544        );
9545        // And it is a body like any other, so the key commands work on it.
9546        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9547        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9548        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9549        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9550        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9551        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9552    }
9553
9554    #[test]
9555    fn every_array_command_refuses_a_key_holding_something_else() {
9556        let mut f = Fixture::new();
9557        f.run(&[b"SET", b"s", b"v"]);
9558        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9559        for cmd in [
9560            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9561            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9562            &[b"ARGET".as_ref(), b"s", b"0"][..],
9563            &[b"ARMGET".as_ref(), b"s", b"0"][..],
9564            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9565            &[b"ARLEN".as_ref(), b"s"][..],
9566            &[b"ARCOUNT".as_ref(), b"s"][..],
9567            &[b"ARDEL".as_ref(), b"s", b"0"][..],
9568            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9569            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9570            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9571            &[b"ARNEXT".as_ref(), b"s"][..],
9572            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9573            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9574            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9575            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9576            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9577            &[b"ARINFO".as_ref(), b"s"][..],
9578        ] {
9579            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9580        }
9581    }
9582
9583    /// Two of the array commands look the key up before they read the index and
9584    /// the rest read the index first, so the same broken argument gets two
9585    /// different errors depending on which command it went to.
9586    #[test]
9587    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9588        let mut f = Fixture::new();
9589        f.run(&[b"SET", b"s", b"v"]);
9590        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9591        let bad = "-ERR invalid array index\r\n";
9592        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
9593        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
9594        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
9595        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
9596        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
9597        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
9598        // And on a key that is an array the index is just an index.
9599        f.run(&[b"ARSET", b"a", b"0", b"x"]);
9600        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
9601        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
9602    }
9603
9604    #[test]
9605    fn an_append_follows_a_cursor_the_client_can_move() {
9606        let mut f = Fixture::new();
9607        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
9608        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
9609        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
9610        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
9611        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
9612
9613        // A seek says where the next one goes, and a missing key has no cursor
9614        // to move and is not created by the asking.
9615        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
9616        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
9617        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
9618        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
9619        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
9620        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
9621        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
9622
9623        // The top of the space is the one index only ARSEEK will take, and it
9624        // leaves the cursor with nowhere to go.
9625        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
9626        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
9627        assert_eq!(
9628            f.run(&[b"ARINSERT", b"a", b"x"]),
9629            "-ERR insert index overflow\r\n"
9630        );
9631        assert_eq!(
9632            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
9633            "-ERR invalid array index\r\n"
9634        );
9635    }
9636
9637    #[test]
9638    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
9639        let mut f = Fixture::new();
9640        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
9641        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
9642        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
9643        assert_eq!(
9644            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
9645            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
9646        );
9647        // Growing it after it has wrapped puts the survivors back in the order
9648        // they arrived, which is the whole point of paying for the rebuild.
9649        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
9650        assert_eq!(
9651            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
9652            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
9653        );
9654        // The size is read before the key, so a bad one is a bad size wherever
9655        // it is sent.
9656        assert_eq!(
9657            f.run(&[b"ARRING", b"r", b"0", b"x"]),
9658            "-ERR size must be positive\r\n"
9659        );
9660        assert_eq!(
9661            f.run(&[b"ARRING", b"r", b"big", b"x"]),
9662            "-ERR invalid size\r\n"
9663        );
9664    }
9665
9666    #[test]
9667    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
9668        let mut f = Fixture::new();
9669        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
9670        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
9671        assert_eq!(
9672            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
9673            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
9674        );
9675        assert_eq!(
9676            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
9677            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
9678        );
9679        assert_eq!(
9680            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
9681            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
9682            "more than there is gets what there is"
9683        );
9684        // Nothing asked for is an empty reply, and Redis answers that before it
9685        // has read the option or looked at the key.
9686        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
9687        assert_eq!(
9688            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
9689            "-ERR syntax error\r\n"
9690        );
9691        assert_eq!(
9692            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
9693            "-ERR invalid COUNT\r\n"
9694        );
9695
9696        // With no cursor the tail of the array is the anchor, and a hole inside
9697        // the window is reported as one.
9698        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
9699        assert_eq!(
9700            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
9701            "*2\r\n$-1\r\n$1\r\nz\r\n"
9702        );
9703    }
9704
9705    #[test]
9706    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
9707        let mut f = Fixture::new();
9708        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
9709        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
9710        // The whole index space, which ARGETRANGE refuses and this one answers
9711        // in three visits because holes cost nothing.
9712        assert_eq!(
9713            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
9714            "*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"
9715        );
9716        assert_eq!(
9717            f.run(&[
9718                b"ARSCAN",
9719                b"a",
9720                b"18446744073709551614",
9721                b"0",
9722                b"LIMIT",
9723                b"1"
9724            ]),
9725            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
9726        );
9727        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
9728        assert_eq!(
9729            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
9730            "-ERR LIMIT must be positive\r\n"
9731        );
9732        assert_eq!(
9733            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
9734            "-ERR syntax error\r\n"
9735        );
9736        assert_eq!(
9737            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
9738            "-ERR wrong number of arguments for 'arscan' command\r\n"
9739        );
9740    }
9741
9742    #[test]
9743    fn a_grep_answers_the_indexes_whose_elements_match() {
9744        let mut f = Fixture::new();
9745        assert_eq!(
9746            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
9747            "*0\r\n"
9748        );
9749        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
9750
9751        // The two bounds take the ends of the array as well as an index, and a
9752        // reversed range is walked backwards the way ARSCAN walks one.
9753        assert_eq!(
9754            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
9755            "*3\r\n:0\r\n:1\r\n:2\r\n"
9756        );
9757        assert_eq!(
9758            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
9759            "*3\r\n:2\r\n:1\r\n:0\r\n"
9760        );
9761        assert_eq!(
9762            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
9763            "*2\r\n:1\r\n:2\r\n"
9764        );
9765
9766        // One test each. NOCASE reaches all four of them and it may be written
9767        // after the pattern it applies to.
9768        assert_eq!(
9769            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
9770            "*1\r\n:0\r\n"
9771        );
9772        assert_eq!(
9773            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
9774            "*2\r\n:0\r\n:3\r\n"
9775        );
9776        assert_eq!(
9777            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
9778            "*1\r\n:2\r\n"
9779        );
9780        assert_eq!(
9781            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
9782            "*2\r\n:1\r\n:2\r\n"
9783        );
9784
9785        // OR is the default and AND has to be asked for, and either way the
9786        // last of a repeated option wins.
9787        let both: &[&[u8]] = &[
9788            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
9789        ];
9790        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
9791        assert_eq!(
9792            f.run(&[
9793                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
9794            ]),
9795            "*0\r\n"
9796        );
9797        assert_eq!(
9798            f.run(&[
9799                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
9800            ]),
9801            "*2\r\n:0\r\n:1\r\n"
9802        );
9803
9804        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
9805        // not the positions it had to look at.
9806        assert_eq!(
9807            f.run(&[
9808                b"ARGREP",
9809                b"a",
9810                b"-",
9811                b"+",
9812                b"MATCH",
9813                b"a",
9814                b"WITHVALUES",
9815                b"LIMIT",
9816                b"2"
9817            ]),
9818            "*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"
9819        );
9820        assert_eq!(
9821            f.run(&[
9822                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
9823            ]),
9824            "*1\r\n:3\r\n"
9825        );
9826    }
9827
9828    /// Everything ARGREP refuses, in the order it refuses it.
9829    #[test]
9830    fn a_grep_reports_a_broken_command_the_way_redis_does() {
9831        let mut f = Fixture::new();
9832        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
9833        let syntax = "-ERR syntax error\r\n";
9834
9835        // The bounds are read before the plan, so a bad index beats a bad
9836        // predicate whichever way round the two are written.
9837        assert_eq!(
9838            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
9839            "-ERR invalid array index\r\n"
9840        );
9841        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
9842        // A keyword with nothing after it, and a command that asks for nothing.
9843        assert_eq!(
9844            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
9845            syntax
9846        );
9847        assert_eq!(
9848            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
9849            syntax
9850        );
9851        assert_eq!(
9852            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
9853            syntax,
9854            "a command with no predicate in it at all"
9855        );
9856        assert_eq!(
9857            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
9858            "-ERR LIMIT must be positive\r\n"
9859        );
9860        assert_eq!(
9861            f.run(&[
9862                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
9863            ]),
9864            "-ERR value is not an integer or out of range\r\n"
9865        );
9866        assert_eq!(
9867            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
9868            "-ERR regular expression is empty\r\n"
9869        );
9870        assert_eq!(
9871            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
9872            "-ERR invalid regular expression: Missing ')'\r\n"
9873        );
9874        assert_eq!(
9875            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
9876            "-ERR regular expression backreferences are not supported\r\n"
9877        );
9878        // The arity is minus six, so a predicate keyword with no pattern after
9879        // it is short by one and never reaches the parser.
9880        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
9881        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
9882        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
9883    }
9884
9885    #[test]
9886    fn an_op_reduces_a_range_to_one_number() {
9887        let mut f = Fixture::new();
9888        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
9889        assert_eq!(
9890            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
9891            "$4\r\n-0.5\r\n"
9892        );
9893        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
9894        assert_eq!(
9895            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
9896            "$3\r\n2.5\r\n"
9897        );
9898        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
9899        assert_eq!(
9900            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
9901            ":1\r\n"
9902        );
9903        // An aggregate is written with seventeen significant digits, which is
9904        // Redis's own choice and not what a score comes back as.
9905        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
9906        assert_eq!(
9907            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
9908            "$19\r\n0.30000000000000004\r\n"
9909        );
9910        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
9911        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
9912
9913        // Nothing to work with is a null, and a missing key is a null for the
9914        // aggregates and a zero for the two that count.
9915        f.run(&[b"ARSET", b"w", b"0", b"word"]);
9916        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
9917        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
9918        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
9919
9920        assert_eq!(
9921            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
9922            "-ERR unknown operation\r\n"
9923        );
9924        assert_eq!(
9925            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
9926            "-ERR MATCH requires a value argument\r\n"
9927        );
9928        assert_eq!(
9929            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
9930            "-ERR wrong number of arguments for 'arop' command\r\n"
9931        );
9932    }
9933
9934    #[test]
9935    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
9936        let mut f = Fixture::new();
9937        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
9938        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
9939        let short = f.run(&[b"ARINFO", b"a"]);
9940        assert!(
9941            short.starts_with("*14\r\n"),
9942            "seven pairs on RESP2: {short}"
9943        );
9944        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
9945        assert!(
9946            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
9947            "{short}"
9948        );
9949        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
9950        let full = f.run(&[b"ARINFO", b"a", b"full"]);
9951        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
9952        // Two values one apart are held sparsely, so the dense count is zero and
9953        // the two dense averages have nothing to average.
9954        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
9955        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
9956        assert!(
9957            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
9958            "{full}"
9959        );
9960        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
9961
9962        // On RESP3 the same reply is a map and the averages are doubles.
9963        let mut g = Fixture::new();
9964        g.run(&[b"HELLO", b"3"]);
9965        g.run(&[b"ARINSERT", b"a", b"x"]);
9966        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
9967        assert!(map.starts_with("%12\r\n"), "{map}");
9968        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
9969        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
9970    }
9971
9972    #[test]
9973    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
9974        let mut f = Fixture::new();
9975        // Whole numbers up to two to the sixty second come back as integers,
9976        // and past that the digit generator takes over and uses an exponent.
9977        for (score, want) in [
9978            ("3", "3"),
9979            ("3.5", "3.5"),
9980            ("0.3", "0.3"),
9981            ("1e30", "1e+30"),
9982            ("1e19", "1e+19"),
9983            ("1e-7", "1e-7"),
9984            ("0.000001", "0.000001"),
9985            ("4611686018427387904", "4611686018427387904"),
9986            ("-0", "-0"),
9987        ] {
9988            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
9989            assert_eq!(
9990                f.run(&[b"ZSCORE", b"z", b"m"]),
9991                format!("${}\r\n{want}\r\n", want.len()),
9992                "score {score}"
9993            );
9994        }
9995
9996        // The same bytes on RESP3, where the reply is a double rather than a
9997        // bulk string.
9998        let mut g = Fixture::new();
9999        g.run(&[b"HELLO", b"3"]);
10000        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10001        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10002        // The two float increments are not this printer. They go through
10003        // ld2string in its human mode, which is a fixed point conversion with
10004        // the trailing zeros taken off, so they never write an exponent, and
10005        // they reply with a bulk string on both protocols.
10006        assert_eq!(
10007            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10008            "$31\r\n1000000000000000000000000000000\r\n"
10009        );
10010        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10011        assert_eq!(
10012            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10013            "$20\r\n10000000000000000000\r\n"
10014        );
10015    }
10016
10017    // ----------------------------------------------------------------- graph
10018
10019    #[test]
10020    fn a_node_comes_back_with_the_fields_it_went_in_with() {
10021        let mut f = Fixture::new();
10022        assert_eq!(
10023            f.run(&[
10024                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10025            ]),
10026            ":1\r\n"
10027        );
10028        // The year comes back as the four bytes that were sent and not as a
10029        // number, because every property is text and there is nothing on the
10030        // wire that says which of `1815` and `"1815"` the client meant. The
10031        // fields are in the document's order, which is sorted by name, because
10032        // that is what makes a field lookup a binary search.
10033        assert_eq!(
10034            f.run(&[b"G.NGET", b"social", b"ada"]),
10035            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10036        );
10037        // A second write to the same id replaces the document and says so with
10038        // a zero, so an ingest can count what it created.
10039        assert_eq!(
10040            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10041            ":0\r\n"
10042        );
10043        assert_eq!(
10044            f.run(&[b"G.NGET", b"social", b"ada"]),
10045            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10046        );
10047        // A node with no properties is an empty map and not a null, which is
10048        // how a client tells an isolated node from one that is not there.
10049        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10050        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10051        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10052        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10053
10054        // A field with no value creates nothing, because the pairs are checked
10055        // before the key is touched.
10056        assert_eq!(
10057            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10058            "-ERR syntax error\r\n"
10059        );
10060        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10061
10062        // On RESP3 the same reply is a map.
10063        let mut g = Fixture::new();
10064        g.run(&[b"HELLO", b"3"]);
10065        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10066        assert_eq!(
10067            g.run(&[b"G.NGET", b"social", b"ada"]),
10068            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10069        );
10070    }
10071
10072    #[test]
10073    fn an_edge_creates_the_ends_it_needs() {
10074        let mut f = Fixture::new();
10075        assert_eq!(
10076            f.run(&[
10077                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10078            ]),
10079            ":1\r\n"
10080        );
10081        // Neither end was written first and both are there, as empty nodes.
10082        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10083        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10084        assert_eq!(
10085            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10086            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10087        );
10088        assert_eq!(
10089            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10090            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10091        );
10092        // The same pair under the same label again updates the edge rather than
10093        // making a second one.
10094        assert_eq!(
10095            f.run(&[
10096                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10097            ]),
10098            ":0\r\n"
10099        );
10100        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10101        // A different label between the same pair is a different edge.
10102        assert_eq!(
10103            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10104            ":1\r\n"
10105        );
10106        assert_eq!(
10107            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10108            ":1\r\n"
10109        );
10110
10111        assert_eq!(
10112            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10113            ":1\r\n"
10114        );
10115        assert_eq!(
10116            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10117            ":0\r\n"
10118        );
10119        // A label nothing has used, an end that is not there, and a key that is
10120        // not there are all a zero rather than an error.
10121        assert_eq!(
10122            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10123            ":0\r\n"
10124        );
10125        assert_eq!(
10126            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10127            ":0\r\n"
10128        );
10129        assert_eq!(
10130            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10131            ":0\r\n"
10132        );
10133    }
10134
10135    /// A run is paged the way `SCAN` is paged, so a client that can walk one
10136    /// can walk the other.
10137    #[test]
10138    fn a_hop_answers_a_cursor_and_a_page() {
10139        let mut f = Fixture::new();
10140        for i in 0..25u32 {
10141            let dst = format!("n{i}");
10142            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10143        }
10144        // Ten without being asked, and the cursor is where to carry on from.
10145        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10146        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10147
10148        let mut seen = 0;
10149        let mut cursor = String::from("0");
10150        loop {
10151            let page = f.run(&[
10152                b"G.OUT",
10153                b"social",
10154                b"hub",
10155                b"FOLLOWS",
10156                b"COUNT",
10157                b"7",
10158                b"CURSOR",
10159                cursor.as_bytes(),
10160            ]);
10161            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10162            cursor = head
10163                .rsplit("\r\n")
10164                .next()
10165                .expect("the cursor line")
10166                .to_string();
10167            seen += rest
10168                .split_once("\r\n")
10169                .expect("the page length")
10170                .0
10171                .parse::<usize>()
10172                .expect("a length");
10173            if cursor == "0" {
10174                break;
10175            }
10176        }
10177        assert_eq!(seen, 25, "every neighbour once across the pages");
10178
10179        // A cursor past the end is an empty page and not an error, and so is a
10180        // key or a label that is not there.
10181        assert_eq!(
10182            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10183            "*2\r\n$1\r\n0\r\n*0\r\n"
10184        );
10185        assert_eq!(
10186            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10187            "*2\r\n$1\r\n0\r\n*0\r\n"
10188        );
10189        assert_eq!(
10190            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10191            "*2\r\n$1\r\n0\r\n*0\r\n"
10192        );
10193        assert_eq!(
10194            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10195            "-ERR COUNT must be a positive integer\r\n"
10196        );
10197        assert_eq!(
10198            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10199            "-ERR syntax error\r\n"
10200        );
10201    }
10202
10203    #[test]
10204    fn a_degree_counts_one_way_or_both() {
10205        let mut f = Fixture::new();
10206        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10207        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10208        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10209        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10210        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10211        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10212        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10213        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10214        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10215        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10216        assert_eq!(
10217            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10218            "-ERR syntax error\r\n"
10219        );
10220    }
10221
10222    /// A walk answers which nodes it can reach and not by how many routes, so a
10223    /// node two ways out is in the frontier once.
10224    #[test]
10225    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10226        let mut f = Fixture::new();
10227        for (src, dst) in [
10228            ("ada", "grace"),
10229            ("ada", "alan"),
10230            ("grace", "edsger"),
10231            ("alan", "edsger"),
10232            ("edsger", "barbara"),
10233        ] {
10234            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10235        }
10236        // Two hops without being asked, the start left out, and edsger once
10237        // even though both of the first hop's nodes point at it.
10238        assert_eq!(
10239            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10240            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10241        );
10242        assert_eq!(
10243            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10244            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10245        );
10246        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10247        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10248        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10249        // COUNT stops the walk rather than trimming what it found.
10250        assert_eq!(
10251            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10252            "*1\r\n$5\r\ngrace\r\n"
10253        );
10254        // A node nothing leaves is an empty array and not an error.
10255        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10256        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10257        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10258        assert_eq!(
10259            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10260            "-ERR DEPTH must be a positive integer\r\n"
10261        );
10262        assert_eq!(
10263            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10264            "-ERR syntax error\r\n"
10265        );
10266    }
10267
10268    /// The two sided search, which is the whole reason `G.PATH` is a command
10269    /// and not something a client builds out of `G.OUT`.
10270    #[test]
10271    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10272        let mut f = Fixture::new();
10273        // A chain of six, and a shortcut that makes a shorter way round under a
10274        // second label so the search has to take either kind of hop.
10275        for i in 0..6u32 {
10276            let src = format!("n{i}");
10277            let dst = format!("n{}", i + 1);
10278            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10279        }
10280        assert_eq!(
10281            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10282            "*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"
10283        );
10284        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10285        assert_eq!(
10286            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10287            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10288        );
10289        // A node to itself is a path of one, and a depth too short to reach is
10290        // no path at all.
10291        assert_eq!(
10292            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10293            "*1\r\n$2\r\nn2\r\n"
10294        );
10295        assert_eq!(
10296            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10297            "*0\r\n"
10298        );
10299        // Direction counts: the chain only goes one way.
10300        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10301        // An unreachable node, a node that is not there, and a key that is not
10302        // there are the same empty answer.
10303        f.run(&[b"G.NADD", b"road", b"island"]);
10304        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10305        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10306        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10307        assert_eq!(
10308            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10309            "-ERR syntax error\r\n"
10310        );
10311    }
10312
10313    /// The point of the escape in the record tag: the keyspace owns a graph key
10314    /// the way it owns every other key, and none of these commands know a graph
10315    /// exists.
10316    #[test]
10317    fn the_keyspace_sees_a_graph_key_like_any_other() {
10318        let mut f = Fixture::new();
10319        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10320        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10321        assert_eq!(
10322            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10323            "$9\r\nadjacency\r\n"
10324        );
10325        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10326        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10327        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10328        // A graph is counted against the server the way every other body is,
10329        // which is what `maxmemory` will read when this key is a million nodes.
10330        // There is no `MEMORY USAGE` command yet, so this asks the server.
10331        let held = f.server.memory_bytes();
10332        for i in 0..200u32 {
10333            let dst = format!("n{i}");
10334            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10335        }
10336        assert!(
10337            f.server.memory_bytes() > held,
10338            "two hundred edges cost something: {held} then {}",
10339            f.server.memory_bytes()
10340        );
10341        f.run(&[b"DEL", b"big"]);
10342
10343        // An expiry, then a rename, then a move to another database, all of
10344        // which are the keyspace moving a record it cannot look inside.
10345        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10346        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10347        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10348        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10349        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10350        f.run(&[b"SELECT", b"1"]);
10351        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10352
10353        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10354        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10355        f.run(&[b"G.NADD", b"g", b"n"]);
10356        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10357        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10358    }
10359
10360    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
10361    /// rather than answering the way they answer for a key that is not there.
10362    #[test]
10363    fn a_graph_cannot_be_copied_or_dumped() {
10364        let mut f = Fixture::new();
10365        f.run(&[b"G.NADD", b"social", b"ada"]);
10366        assert_eq!(
10367            f.run(&[b"COPY", b"social", b"other"]),
10368            "-ERR COPY is not supported for a graph\r\n"
10369        );
10370        assert_eq!(
10371            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10372            "-ERR COPY is not supported for a graph\r\n"
10373        );
10374        assert_eq!(
10375            f.run(&[b"DUMP", b"social"]),
10376            "-ERR DUMP is not supported for a graph\r\n"
10377        );
10378        // A refused copy leaves both keys exactly as they were.
10379        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10380    }
10381
10382    /// A graph key is a key, so the commands for the other types refuse it and
10383    /// the graph commands refuse theirs.
10384    #[test]
10385    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10386        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10387        let mut f = Fixture::new();
10388        f.run(&[b"G.NADD", b"social", b"ada"]);
10389        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10390        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10391        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10392
10393        f.run(&[b"SET", b"str", b"v"]);
10394        for cmd in [
10395            vec![b"G.NADD".as_ref(), b"str", b"n"],
10396            vec![b"G.NGET".as_ref(), b"str", b"n"],
10397            vec![b"G.NDEL".as_ref(), b"str", b"n"],
10398            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10399            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10400            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10401            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10402            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10403            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10404            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10405        ] {
10406            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10407        }
10408    }
10409
10410    /// Every other collection here takes its key with it when its last member
10411    /// goes, and a graph is no different.
10412    #[test]
10413    fn a_graph_goes_when_its_last_node_does() {
10414        let mut f = Fixture::new();
10415        f.run(&[
10416            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10417        ]);
10418        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10419        // The node and the edges that hung off it are both gone.
10420        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10421        assert_eq!(
10422            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10423            ":0\r\n"
10424        );
10425        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10426        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10427
10428        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10429        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10430        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10431        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10432
10433        // The id the removed node had is not handed out again, so a client
10434        // holding an id from an earlier reply cannot have it mean another node.
10435        f.run(&[b"G.NADD", b"social", b"first"]);
10436        f.run(&[b"G.NADD", b"social", b"second"]);
10437        f.run(&[b"G.NDEL", b"social", b"first"]);
10438        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10439        assert_eq!(
10440            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10441            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10442        );
10443    }
10444
10445    // ------------------------------------------------------------------ json
10446
10447    /// The two path syntaxes answer different shapes, which is the thing a
10448    /// client is most likely to be broken by and so the thing to pin first.
10449    #[test]
10450    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10451        let mut f = Fixture::new();
10452        let doc = br#"{"a":1,"b":{"c":true}}"#;
10453        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10454        // No path at all is the legacy root and not `$`, so the document comes
10455        // back as itself rather than wrapped.
10456        assert_eq!(
10457            f.run(&[b"JSON.GET", b"doc"]),
10458            bulk(r#"{"a":1,"b":{"c":true}}"#)
10459        );
10460        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10461        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10462        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10463        // A path that matched nothing is an empty set on one syntax and an
10464        // error on the other, and the error does not quote the path.
10465        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10466        assert_eq!(
10467            f.run(&[b"JSON.GET", b"doc", b".nope"]),
10468            "-ERR Path does not exist\r\n"
10469        );
10470        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10471        // The key is a document to the rest of the keyspace, under the name
10472        // RedisJSON registers, and every generic command works on it.
10473        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10474        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10475        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10476        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10477        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10478    }
10479
10480    /// The two error lines RedisJSON sends without a prefix in front of them.
10481    ///
10482    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
10483    /// two do not, on a real server, and a differential harness compares the
10484    /// whole line.
10485    #[test]
10486    fn the_two_json_errors_that_carry_no_prefix() {
10487        let mut f = Fixture::new();
10488        f.run(&[b"SET", b"plain", b"x"]);
10489        let wrong = "-Existing key has wrong Redis type\r\n";
10490        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10491        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10492        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10493        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10494        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10495
10496        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10497        // A wildcard that matched something writes to all of it. A wildcard
10498        // that matched nothing would have to invent a place, and that is the
10499        // other unprefixed line.
10500        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10501        assert_eq!(
10502            f.run(&[b"JSON.GET", b"doc"]),
10503            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10504        );
10505        assert_eq!(
10506            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10507            "-Err wrong static path\r\n"
10508        );
10509    }
10510
10511    /// What `JSON.SET` does with a path that named nowhere.
10512    #[test]
10513    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10514        let mut f = Fixture::new();
10515        // A key that is not there can only be written whole.
10516        assert_eq!(
10517            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10518            "-ERR new objects must be created at the root\r\n"
10519        );
10520        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10521        // The root check comes before NX and XX, which is the order a real
10522        // server checks them in.
10523        assert_eq!(
10524            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10525            "-ERR new objects must be created at the root\r\n"
10526        );
10527        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10528        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10529
10530        f.run(&[
10531            b"JSON.SET",
10532            b"doc",
10533            b"$",
10534            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10535        ]);
10536        // One step past a container that is there is a place to write.
10537        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10538        // One step past something that is not, or past something that is not an
10539        // object, is not an error and is not a write either.
10540        assert_eq!(
10541            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10542            "$-1\r\n"
10543        );
10544        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10545        // An index past the end does not append. JSON.ARRAPPEND appends.
10546        assert_eq!(
10547            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10548            "-ERR array index out of range\r\n"
10549        );
10550        assert_eq!(
10551            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10552            "-ERR array index out of range\r\n"
10553        );
10554        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10555        // NX on a path that is there and XX on a path that is not are both a
10556        // nil and neither changes anything.
10557        assert_eq!(
10558            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10559            "$-1\r\n"
10560        );
10561        assert_eq!(
10562            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10563            "$-1\r\n"
10564        );
10565        assert_eq!(
10566            f.run(&[b"JSON.GET", b"doc"]),
10567            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10568        );
10569        // Text that is not JSON is refused before the key is touched. The
10570        // line has no `ERR` in front of it, which is this command's and not
10571        // every command's, and is in D-37.
10572        assert!(
10573            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10574                .starts_with("-this is not the start of a value")
10575        );
10576        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10577    }
10578
10579    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
10580    /// answers a count or a word rather than text.
10581    #[test]
10582    fn the_json_commands_that_do_not_answer_text() {
10583        let mut f = Fixture::new();
10584        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10585        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10586
10587        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10588        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10589        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10590        assert_eq!(
10591            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
10592            format!("*1\r\n{}", bulk("integer"))
10593        );
10594        // The one place a legacy path that matched nothing is a nil rather than
10595        // an error, which lines up with a key that is not there.
10596        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
10597        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
10598
10599        // A boolean flips and answers the value it now has, as an integer on
10600        // one syntax and as the word on the other.
10601        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
10602        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
10603        // Something that is not a boolean is a hole on one syntax and one
10604        // sentence covering both cases on the other.
10605        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
10606        assert_eq!(
10607            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
10608            "-ERR Path does not exist or not a bool\r\n"
10609        );
10610        assert_eq!(
10611            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
10612            "-ERR Path does not exist or not a bool\r\n"
10613        );
10614        assert_eq!(
10615            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
10616            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10617        );
10618
10619        // Clearing empties containers and zeroes numbers and leaves everything
10620        // else alone, and counts only what it changed.
10621        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
10622        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
10623        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
10624        assert_eq!(
10625            f.run(&[b"JSON.GET", b"doc"]),
10626            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
10627        );
10628
10629        // Deleting counts what it removed, and deleting the root is deleting
10630        // the key.
10631        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
10632        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
10633        // Deleting the last member of the root container deletes the key, the
10634        // same way popping the last element off a list does. It is a rule about
10635        // deleting and not about shape: a document written as an empty object
10636        // by JSON.SET stays, because nothing was removed from it.
10637        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
10638        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
10639        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10640        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
10641        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
10642        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
10643        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
10644        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
10645    }
10646
10647    /// `JSON.GET` with more than one path, and with a layout.
10648    ///
10649    /// The wrapper the reply is built in is laid out too, so what a path
10650    /// matched starts one level in for a single JSONPath and two for one of
10651    /// several, and getting that wrong is the kind of thing only a byte for
10652    /// byte comparison catches.
10653    #[test]
10654    fn json_get_lays_out_the_wrapper_it_builds() {
10655        let mut f = Fixture::new();
10656        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
10657
10658        assert_eq!(
10659            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
10660            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
10661        );
10662        // Legacy paths are not wrapped, even when there are several of them.
10663        assert_eq!(
10664            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
10665            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
10666        );
10667        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
10668        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
10669        one.extend_from_slice(fmt);
10670        one.push(b"$.b");
10671        assert_eq!(
10672            f.run(&one),
10673            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
10674        );
10675        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
10676        two.extend_from_slice(fmt);
10677        two.push(b"$.a");
10678        two.push(b"$.nope");
10679        assert_eq!(
10680            f.run(&two),
10681            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
10682        );
10683        // The options are read before the paths and in any order, and a
10684        // document with nothing to lay out is the same either way.
10685        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
10686        root.push(b".a");
10687        assert_eq!(f.run(&root), bulk("1"));
10688    }
10689
10690    /// `JSON.MGET`, which is the only command here that reads more than one key
10691    /// and so the only one whose answer has holes in it.
10692    #[test]
10693    fn json_mget_answers_once_per_key_whatever_is_under_them() {
10694        let mut f = Fixture::new();
10695        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
10696        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
10697        f.run(&[b"SET", b"plain", b"x"]);
10698        assert_eq!(
10699            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
10700            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
10701        );
10702        // A key that is not there and a key holding something else are both a
10703        // hole rather than an error, the way MGET treats a hash.
10704        assert_eq!(
10705            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
10706            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
10707        );
10708        // A legacy path that matched nothing is a hole too, because one bad
10709        // answer should not lose the others.
10710        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
10711    }
10712
10713    /// The four commands that ask how big something is, and the four different
10714    /// sets of answers they give for the same three failures.
10715    ///
10716    /// There is no pattern in this and there is no reading it off the
10717    /// documentation either. It was read off a running RedisJSON one line at a
10718    /// time, and it is written down here because the error text is what a client
10719    /// library branches on.
10720    #[test]
10721    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
10722        let mut f = Fixture::new();
10723        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
10724        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10725
10726        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
10727        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
10728        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
10729        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
10730        assert_eq!(
10731            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
10732            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
10733        );
10734        // A JSONPath answers one entry per match and a hole for a match of the
10735        // wrong kind, which is the one shape all four agree on.
10736        assert_eq!(
10737            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
10738            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
10739        );
10740
10741        // A legacy path that matched nothing. Two of them are an error and two
10742        // of them are a nil, and the two errors do not use the same sentence.
10743        assert_eq!(
10744            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
10745            "-ERR Path does not exist\r\n"
10746        );
10747        assert_eq!(
10748            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
10749            "-ERR Path does not exist\r\n"
10750        );
10751        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
10752        // A nil bulk and not an empty array, even though the answer would have
10753        // been an array, which is what RedisJSON sends here too.
10754        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
10755        // The JSONPath spelling of the same question is an empty array, since
10756        // no match is not a failure on that syntax.
10757        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
10758
10759        // A legacy path that matched the wrong kind of value. Now two of them
10760        // are an ERR and two of them are a WRONGTYPE, and it is not the same
10761        // two.
10762        assert_eq!(
10763            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
10764            "-ERR Path does not exist or not an array\r\n"
10765        );
10766        assert_eq!(
10767            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
10768            "-ERR Path does not exist or not an object\r\n"
10769        );
10770        assert_eq!(
10771            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
10772            "-WRONGTYPE wrong type of path value - expected object\r\n"
10773        );
10774        assert_eq!(
10775            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
10776            "-WRONGTYPE wrong type of path value - expected string\r\n"
10777        );
10778
10779        // A key that is not there, where the two syntaxes swap over: the legacy
10780        // path is the quiet answer and the JSONPath is the error.
10781        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
10782        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
10783        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
10784        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
10785        assert_eq!(
10786            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
10787            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10788        );
10789        // Except this one, which answers about the path instead.
10790        assert_eq!(
10791            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
10792            "-ERR Path does not exist or not an object\r\n"
10793        );
10794    }
10795
10796    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
10797    ///
10798    /// The four of them share one error line for a path that named something
10799    /// that is not an array, and they disagree about what an index outside the
10800    /// array means: insert refuses it and the other two clamp.
10801    #[test]
10802    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
10803        let mut f = Fixture::new();
10804        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
10805
10806        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
10807        assert_eq!(
10808            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
10809            "*1\r\n:6\r\n"
10810        );
10811        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
10812
10813        // A negative index counts back from the end, and the end itself is a
10814        // place to insert at, so an insert at the length is an append.
10815        assert_eq!(
10816            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
10817            ":7\r\n"
10818        );
10819        assert_eq!(
10820            f.run(&[b"JSON.GET", b"doc", b".a"]),
10821            bulk("[1,2,3,4,5,0,6]")
10822        );
10823        assert_eq!(
10824            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
10825            ":8\r\n"
10826        );
10827        // One past the end is not, and neither is one before the front.
10828        assert_eq!(
10829            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
10830            "-ERR index out of bounds\r\n"
10831        );
10832        assert_eq!(
10833            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
10834            "-ERR index out of bounds\r\n"
10835        );
10836
10837        // Trim takes both ends inclusive and clamps both of them, so a start
10838        // past the end leaves an empty array rather than an error.
10839        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
10840        assert_eq!(
10841            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
10842            ":3\r\n"
10843        );
10844        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
10845        assert_eq!(
10846            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
10847            ":2\r\n"
10848        );
10849        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
10850        assert_eq!(
10851            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
10852            ":0\r\n"
10853        );
10854        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10855
10856        // Pop clamps as well, its default is the last element, and an empty
10857        // array pops a nil rather than failing.
10858        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
10859        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
10860        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
10861        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
10862        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
10863
10864        // One sentence covers a path that matched nothing and a path that
10865        // matched the wrong kind of value, for all four of them.
10866        for call in [
10867            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
10868            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
10869            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
10870            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
10871        ] {
10872            for path in [&b".n"[..], &b".nope"[..]] {
10873                let args: Vec<&[u8]> = call
10874                    .iter()
10875                    .map(|a| if *a == b"PATH" { path } else { *a })
10876                    .collect();
10877                assert_eq!(
10878                    f.run(&args),
10879                    "-ERR Path does not exist or not an array\r\n",
10880                    "{} {}",
10881                    String::from_utf8_lossy(call[0]),
10882                    String::from_utf8_lossy(path)
10883                );
10884            }
10885        }
10886
10887        // A key that is not there is the same sentence for all four, on either
10888        // syntax, and it is about the key and not about the path.
10889        assert_eq!(
10890            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
10891            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10892        );
10893        assert_eq!(
10894            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
10895            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10896        );
10897
10898        // The values are parsed before the key is touched, so text that is not
10899        // JSON leaves the document alone.
10900        // Text that is not JSON is refused before the key is touched, and
10901        // the line has no `ERR` in front of it, which is D-37.
10902        assert!(
10903            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
10904                .starts_with("-this is not the start of a value")
10905        );
10906        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10907    }
10908
10909    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
10910    /// path matched cannot take the index, which is D-36.
10911    ///
10912    /// RedisJSON walks the matches, inserts into each one it can, and returns
10913    /// the error on the first one it cannot, leaving the earlier inserts in the
10914    /// document. A write here is one list of edits applied together, so either
10915    /// all of them happen or none of them do.
10916    #[test]
10917    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
10918        let mut f = Fixture::new();
10919        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
10920        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10921        assert_eq!(
10922            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
10923            "-ERR index out of bounds\r\n"
10924        );
10925        assert_eq!(
10926            f.run(&[b"JSON.GET", b"doc"]),
10927            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
10928        );
10929        // Every match can take the index, so every match gets it.
10930        assert_eq!(
10931            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
10932            "*3\r\n:4\r\n:3\r\n:2\r\n"
10933        );
10934        assert_eq!(
10935            f.run(&[b"JSON.GET", b"doc"]),
10936            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
10937        );
10938    }
10939
10940    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
10941    /// last element rather than to one past it.
10942    ///
10943    /// Both of those read like mistakes and both are what RedisJSON does. The
10944    /// start is the one that bites: a start of five into an array of four still
10945    /// looks at the fourth, so a search that should have run out of array comes
10946    /// back with an answer.
10947    #[test]
10948    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
10949        let mut f = Fixture::new();
10950        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
10951
10952        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
10953        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
10954        assert_eq!(
10955            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
10956            "*1\r\n:1\r\n"
10957        );
10958
10959        // Zero as the stop means the end rather than the front, so leaving it
10960        // off and passing it are the same thing.
10961        assert_eq!(
10962            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
10963            ":3\r\n"
10964        );
10965        // The stop is exclusive, so a stop of three does not look at index
10966        // three.
10967        assert_eq!(
10968            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
10969            ":-1\r\n"
10970        );
10971
10972        // The start clamps to the last element in both directions, which is why
10973        // a start of four, five or minus one all find the 1 at index three.
10974        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
10975            assert_eq!(
10976                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
10977                ":3\r\n",
10978                "{}",
10979                String::from_utf8_lossy(start)
10980            );
10981        }
10982        assert_eq!(
10983            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
10984            ":0\r\n"
10985        );
10986        // An empty array is the one case that comes back with nothing, since
10987        // the stop is zero and the loop never starts.
10988        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
10989        assert_eq!(
10990            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
10991            ":-1\r\n"
10992        );
10993
10994        // The comparison is structural rather than one of the encoded bytes,
10995        // because an object in a stored document holds its keys as intern table
10996        // ids where one parsed off the wire holds them as bytes.
10997        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
10998        assert_eq!(
10999            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11000            ":0\r\n"
11001        );
11002        assert_eq!(
11003            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11004            ":1\r\n"
11005        );
11006        assert_eq!(
11007            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11008            ":-1\r\n"
11009        );
11010
11011        // Its errors are a third set again: a missing legacy path is the short
11012        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
11013        // not there is about the path on either syntax.
11014        assert_eq!(
11015            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11016            "-ERR Path does not exist\r\n"
11017        );
11018        assert_eq!(
11019            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11020            "-WRONGTYPE wrong type of path value - expected array\r\n"
11021        );
11022        assert_eq!(
11023            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11024            "-ERR Path does not exist\r\n"
11025        );
11026        assert_eq!(
11027            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11028            "-ERR Path does not exist\r\n"
11029        );
11030    }
11031
11032    /// The number family answers text and keeps an integer an integer until
11033    /// something in the sum is not one.
11034    #[test]
11035    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11036        let mut f = Fixture::new();
11037        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11038        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11039
11040        // A legacy path answers the new value as JSON text in a bulk string,
11041        // not as a number, which is the shape all three of them use.
11042        assert_eq!(
11043            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11044            bulk("9").as_str()
11045        );
11046        // A JSONPath answers a bulk string holding a JSON array.
11047        assert_eq!(
11048            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11049            bulk("[11]").as_str()
11050        );
11051        // Two integers stay an integer and a double anywhere in it makes the
11052        // answer a double, which the document then holds.
11053        assert_eq!(
11054            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11055            bulk("13.0").as_str()
11056        );
11057        assert_eq!(
11058            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11059            bulk("number").as_str()
11060        );
11061        assert_eq!(
11062            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11063            bulk("3.0").as_str()
11064        );
11065        assert_eq!(
11066            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11067            bulk("-8").as_str()
11068        );
11069        // A power of a half is a square root, and the square root of a negative
11070        // number is the error that says the answer is not a number.
11071        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11072        assert_eq!(
11073            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11074            bulk("1.224744871391589").as_str()
11075        );
11076        assert_eq!(
11077            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11078            "-ERR result is not a number\r\n"
11079        );
11080        // An integer answer that does not fit is refused rather than promoted,
11081        // and a negative exponent lands in the same error because there is no
11082        // integer answer to two to the minus one.
11083        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11084        assert_eq!(
11085            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11086            "-ERR numeric overflow\r\n"
11087        );
11088        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11089        assert_eq!(
11090            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11091            "-ERR numeric overflow\r\n"
11092        );
11093        // A double that leaves the finite numbers is the other error.
11094        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11095        assert_eq!(
11096            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11097            "-ERR result is not a number\r\n"
11098        );
11099
11100        // A match that is not a number is a null inside the array on a
11101        // JSONPath, and a legacy path that found no number at all is the error
11102        // with the module's own typo in it.
11103        assert_eq!(
11104            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11105            bulk("[null]").as_str()
11106        );
11107        assert_eq!(
11108            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11109            bulk("[]").as_str()
11110        );
11111        assert_eq!(
11112            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11113            "-ERR Path does not exist or does not contains a number\r\n"
11114        );
11115        assert_eq!(
11116            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11117            "-ERR Path does not exist or does not contains a number\r\n"
11118        );
11119        // The operand is JSON and has to be a number. Valid JSON that is not
11120        // one is a line of its own, and it goes out without a prefix.
11121        assert_eq!(
11122            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11123            "-bad input number\r\n"
11124        );
11125        assert_eq!(
11126            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11127            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11128        );
11129        assert_eq!(
11130            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11131            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11132        );
11133    }
11134
11135    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
11136    /// which nothing else in the group does.
11137    #[test]
11138    fn json_strappend_reads_its_shape_off_the_argument_count() {
11139        let mut f = Fixture::new();
11140        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11141
11142        assert_eq!(
11143            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11144            ":3\r\n"
11145        );
11146        assert_eq!(
11147            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11148            "*1\r\n:4\r\n"
11149        );
11150        // The length is in bytes and not in characters, so one two byte letter
11151        // takes it up by two.
11152        assert_eq!(
11153            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11154            ":6\r\n"
11155        );
11156        // Three arguments means the value is the last one and the path is the
11157        // root, so this appends to a document that is a string on its own.
11158        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11159        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11160        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11161
11162        // The value is JSON and has to be a JSON string. A number is a
11163        // WRONGTYPE about a path value even though it was the value that was
11164        // wrong, which is the module's wording and not a slip here.
11165        assert_eq!(
11166            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11167            "-WRONGTYPE wrong type of path value - expected string\r\n"
11168        );
11169        assert_eq!(
11170            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11171            "*1\r\n$-1\r\n"
11172        );
11173        assert_eq!(
11174            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11175            "-ERR Path does not exist or not a string\r\n"
11176        );
11177        assert_eq!(
11178            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11179            "*0\r\n"
11180        );
11181        assert_eq!(
11182            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11183            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11184        );
11185    }
11186
11187    /// A legacy path can match more than one value, and which of them the one
11188    /// answer comes from is not the same choice twice.
11189    #[test]
11190    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11191        let mut f = Fixture::new();
11192        // Three arrays of one, two and three elements, which tells the first
11193        // match and the last match apart in a single command.
11194        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11195
11196        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11197        assert_eq!(
11198            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11199            ":4\r\n"
11200        );
11201        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11202        assert_eq!(
11203            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11204            ":2\r\n"
11205        );
11206        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11207        assert_eq!(
11208            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11209            ":1\r\n"
11210        );
11211        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11212        assert_eq!(
11213            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11214            bulk("1").as_str()
11215        );
11216        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11217        assert_eq!(
11218            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11219            bulk("13").as_str()
11220        );
11221        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11222        assert_eq!(
11223            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11224            ":4\r\n"
11225        );
11226        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11227        assert_eq!(
11228            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11229            bulk("false").as_str()
11230        );
11231        // Every one of them wrote to all three matches, whichever one it chose
11232        // to answer about.
11233        assert_eq!(
11234            f.run(&[b"JSON.GET", b"doc", b".a"]),
11235            bulk("[false,true,false]").as_str()
11236        );
11237
11238        // A match of the wrong kind is skipped rather than being the answer, so
11239        // a path that found a string and then two arrays still answers.
11240        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11241        assert_eq!(
11242            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11243            ":3\r\n"
11244        );
11245        assert_eq!(
11246            f.run(&[b"JSON.GET", b"doc", b".a"]),
11247            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11248        );
11249        // Nothing of the right kind anywhere is the error, and that is the only
11250        // case that is.
11251        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11252        assert_eq!(
11253            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11254            "-ERR Path does not exist or not an array\r\n"
11255        );
11256        assert_eq!(
11257            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11258            "-ERR Path does not exist or not a bool\r\n"
11259        );
11260        // The one array that was there and had nothing in it is an answer and
11261        // not a skip, so the pop answers about it rather than about the array
11262        // after it.
11263        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11264        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11265        assert_eq!(
11266            f.run(&[b"JSON.GET", b"doc", b".a"]),
11267            bulk("[[],[2]]").as_str()
11268        );
11269    }
11270
11271    /// A path that matched a value and something inside that value writes to
11272    /// both, which is what `$..` and a nested wildcard are for.
11273    #[test]
11274    fn a_write_reaches_a_match_that_sits_inside_another_match() {
11275        let mut f = Fixture::new();
11276        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11277
11278        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11279        assert_eq!(
11280            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11281            "*3\r\n:3\r\n:2\r\n:3\r\n"
11282        );
11283        assert_eq!(
11284            f.run(&[b"JSON.GET", b"doc", b"$"]),
11285            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11286        );
11287
11288        // The same for a trim, where the outer array keeps the two elements the
11289        // inner writes landed in.
11290        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11291        assert_eq!(
11292            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11293            "*3\r\n:1\r\n:1\r\n:1\r\n"
11294        );
11295        assert_eq!(
11296            f.run(&[b"JSON.GET", b"doc", b"$"]),
11297            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11298        );
11299
11300        // And for a number, where the first match is the object the outer array
11301        // holds and only the two inside it are numbers.
11302        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11303        assert_eq!(
11304            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11305            bulk("[null,8,8]").as_str()
11306        );
11307    }
11308
11309    /// The value a write is given is looked at only once the path has found
11310    /// something of the right kind to use it on.
11311    #[test]
11312    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11313        let mut f = Fixture::new();
11314        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11315
11316        // A string is not a number, so the path answers first and the `"x"` is
11317        // never looked at. Same for the value that is not JSON at all.
11318        assert_eq!(
11319            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11320            bulk("[null]").as_str()
11321        );
11322        assert_eq!(
11323            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11324            bulk("[null]").as_str()
11325        );
11326        assert_eq!(
11327            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11328            bulk("[]").as_str()
11329        );
11330        assert_eq!(
11331            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11332            "-ERR Path does not exist or does not contains a number\r\n"
11333        );
11334        // A number match anywhere and the value is looked at after all.
11335        assert_eq!(
11336            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11337            "-bad input number\r\n"
11338        );
11339
11340        // JSON.STRAPPEND follows the same order with its own two answers.
11341        assert_eq!(
11342            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11343            "*1\r\n$-1\r\n"
11344        );
11345        assert_eq!(
11346            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11347            "-ERR Path does not exist or not a string\r\n"
11348        );
11349        assert_eq!(
11350            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11351            "-WRONGTYPE wrong type of path value - expected string\r\n"
11352        );
11353
11354        // A key that is not there still comes before either of them.
11355        assert_eq!(
11356            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11357            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11358        );
11359        assert_eq!(
11360            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11361            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11362        );
11363    }
11364
11365    /// RFC 7386 in one test: a null deletes, everything else merges, and a
11366    /// patch that is not an object replaces what it lands on.
11367    #[test]
11368    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11369        let mut f = Fixture::new();
11370
11371        // A key that is not there is created at the root, nulls and all,
11372        // because a deletion with nothing to delete is still what the client
11373        // sent.
11374        assert_eq!(
11375            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11376            "+OK\r\n"
11377        );
11378        assert_eq!(
11379            f.run(&[b"JSON.GET", b"doc", b"$"]),
11380            bulk(r#"[{"x":null,"y":1}]"#).as_str()
11381        );
11382
11383        // Onto something that is there, a null deletes the member of that name
11384        // and the rest is merged one level at a time.
11385        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11386        assert_eq!(
11387            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11388            "+OK\r\n"
11389        );
11390        assert_eq!(
11391            f.run(&[b"JSON.GET", b"doc", b"$"]),
11392            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11393        );
11394
11395        // A patch that is not an object replaces what it is merged onto.
11396        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11397        assert_eq!(
11398            f.run(&[b"JSON.GET", b"doc", b"$"]),
11399            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11400        );
11401
11402        // A patch object onto a value that is not an object starts from an
11403        // empty object, so this time the null has nothing to delete and is
11404        // dropped rather than stored.
11405        assert_eq!(
11406            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11407            "+OK\r\n"
11408        );
11409        assert_eq!(
11410            f.run(&[b"JSON.GET", b"doc", b"$"]),
11411            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11412        );
11413
11414        // A member one level past the end of the document is created and keeps
11415        // its nulls, two levels past it is a write that did not happen, and a
11416        // path that would have to invent where it goes is the unprefixed line.
11417        assert_eq!(
11418            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11419            "+OK\r\n"
11420        );
11421        assert_eq!(
11422            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11423            bulk(r#"[{"z":null}]"#).as_str()
11424        );
11425        assert_eq!(
11426            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11427            "$-1\r\n"
11428        );
11429        assert_eq!(
11430            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11431            "-Err wrong static path\r\n"
11432        );
11433
11434        // A wildcard merges every match.
11435        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11436        assert_eq!(
11437            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11438            "+OK\r\n"
11439        );
11440        assert_eq!(
11441            f.run(&[b"JSON.GET", b"doc", b"$"]),
11442            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11443        );
11444
11445        // The three ways to get it wrong.
11446        assert_eq!(
11447            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11448            "-ERR syntax error\r\n"
11449        );
11450        assert_eq!(
11451            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11452            "-ERR new objects must be created at the root\r\n"
11453        );
11454        f.run(&[b"SET", b"str", b"x"]);
11455        assert_eq!(
11456            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11457            "-Existing key has wrong Redis type\r\n"
11458        );
11459    }
11460
11461    /// A descent is the one path that matches a value and something inside that
11462    /// same value, and the inner merge has to survive the outer one.
11463    #[test]
11464    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11465        let mut f = Fixture::new();
11466        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11467        assert_eq!(
11468            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11469            "+OK\r\n"
11470        );
11471        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
11472        // merged onto the result, so the `{"m":1}` written into `a.b` is still
11473        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
11474        assert_eq!(
11475            f.run(&[b"JSON.GET", b"doc", b"$"]),
11476            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11477        );
11478
11479        // A deletion down the same path, which is the case where the inner
11480        // merge empties the object the outer one then copies.
11481        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11482        assert_eq!(
11483            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11484            "+OK\r\n"
11485        );
11486        assert_eq!(
11487            f.run(&[b"JSON.GET", b"doc", b"$"]),
11488            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11489        );
11490    }
11491
11492    /// A filter is a selector like any other, so every command that takes a path
11493    /// takes one, reads and writes alike.
11494    #[test]
11495    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11496        let mut f = Fixture::new();
11497        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11498        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11499
11500        assert_eq!(
11501            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11502            bulk(r#"["a","c"]"#).as_str()
11503        );
11504        // `$` inside the expression is the document, so a member can be measured
11505        // against something that is not inside it.
11506        assert_eq!(
11507            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11508            bulk(r#"["a","c"]"#).as_str()
11509        );
11510        // The legacy syntax takes one too, and answers the first match.
11511        assert_eq!(
11512            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11513            bulk(r#""a""#).as_str()
11514        );
11515        assert_eq!(
11516            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11517            "*1\r\n$6\r\nobject\r\n"
11518        );
11519
11520        // A write goes through it as far as a value that is already there. A
11521        // field that is not there yet has nowhere definite to go, which is the
11522        // same refusal a wildcard gets.
11523        assert_eq!(
11524            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11525            bulk("[9,10]").as_str()
11526        );
11527        assert_eq!(
11528            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11529            "+OK\r\n"
11530        );
11531        assert_eq!(
11532            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11533            "-Err wrong static path\r\n"
11534        );
11535        assert_eq!(
11536            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11537            ":2\r\n"
11538        );
11539        assert_eq!(
11540            f.run(&[b"JSON.GET", b"doc", b"$"]),
11541            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11542        );
11543
11544        // A path that does not parse is refused before the document is read, so
11545        // a key that is not there answers the same way.
11546        assert!(
11547            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11548                .starts_with("-ERR")
11549        );
11550        assert!(
11551            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11552                .starts_with("-ERR")
11553        );
11554    }
11555
11556    /// The operators past the comparisons, over the wire rather than in the
11557    /// parser's own tests, so that a client can reach all of them.
11558    #[test]
11559    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11560        let mut f = Fixture::new();
11561        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11562        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11563
11564        for (path, want) in [
11565            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11566            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11567            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11568            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11569            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11570            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11571            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11572            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11573            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11574            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11575            (b"$.box[?(@.n~)].t", "[]"),
11576            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11577            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11578            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11579            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11580        ] {
11581            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11582        }
11583
11584        // A write goes through one of these the same way it goes through a
11585        // comparison.
11586        assert_eq!(
11587            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11588            "+OK\r\n"
11589        );
11590        assert_eq!(
11591            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
11592            bulk(r#"["b"]"#).as_str()
11593        );
11594    }
11595
11596    /// D-41. RedisJSON refuses this one, and which document it refuses is
11597    /// decided by how it happens to hold an array of numbers.
11598    #[test]
11599    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
11600        let mut f = Fixture::new();
11601        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
11602        assert_eq!(
11603            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11604            "+OK\r\n"
11605        );
11606        assert_eq!(
11607            f.run(&[b"JSON.GET", b"doc", b"$"]),
11608            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
11609        );
11610        // The same document with one element that is not an integer is the one
11611        // RedisJSON is happy with, and it goes the same way here.
11612        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
11613        assert_eq!(
11614            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11615            "+OK\r\n"
11616        );
11617        assert_eq!(
11618            f.run(&[b"JSON.GET", b"doc", b"$"]),
11619            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
11620        );
11621    }
11622
11623    /// `JSON.MSET` checks what it can before it writes anything and skips the
11624    /// one thing it cannot, which is a path with nowhere to put its value.
11625    #[test]
11626    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
11627        let mut f = Fixture::new();
11628        assert_eq!(
11629            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
11630            "+OK\r\n"
11631        );
11632        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
11633        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
11634
11635        // A repeated key takes the last write.
11636        assert_eq!(
11637            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
11638            "+OK\r\n"
11639        );
11640        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
11641
11642        // A triple whose path names nowhere is skipped, the others are still
11643        // written and the reply turns into a nil. Both ways round, because a
11644        // loop that gave up at the first skip would agree with this on one
11645        // order and not on the other.
11646        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
11647        assert_eq!(
11648            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
11649            "$-1\r\n"
11650        );
11651        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
11652        assert_eq!(
11653            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
11654            "$-1\r\n"
11655        );
11656        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11657
11658        // A value that is not JSON, a key holding something else and a path
11659        // that would have to create a document below its own root are all
11660        // checked before anything is written, so the good triple next to them
11661        // does not happen either.
11662        f.run(&[b"SET", b"str", b"x"]);
11663        assert_eq!(
11664            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
11665            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
11666        );
11667        assert_eq!(
11668            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
11669            "-Existing key has wrong Redis type\r\n"
11670        );
11671        assert_eq!(
11672            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
11673            "-ERR new objects must be created at the root\r\n"
11674        );
11675        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
11676
11677        // The two errors a path can be are checked up front as well, so the
11678        // triple before them is not written either. A wildcard that matched
11679        // nothing has nowhere to invent, and an index that is not in the array
11680        // is out of range, and both of them stop the whole command.
11681        assert_eq!(
11682            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
11683            "-Err wrong static path\r\n"
11684        );
11685        assert_eq!(
11686            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
11687            "-ERR array index out of range\r\n"
11688        );
11689        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11690
11691        // Every triple is worked out against the keyspace as the command found
11692        // it, so a second triple on the same key does not see the first one and
11693        // the last write is the one that stays.
11694        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
11695        assert_eq!(
11696            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
11697            "+OK\r\n"
11698        );
11699        assert_eq!(
11700            f.run(&[b"JSON.GET", b"c", b"$"]),
11701            bulk(r#"[{"n":3}]"#).as_str()
11702        );
11703
11704        // An argument count that is not a run of key, path and value is the
11705        // arity error rather than a syntax one.
11706        assert_eq!(
11707            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
11708            "-ERR wrong number of arguments for 'json.mset' command\r\n"
11709        );
11710    }
11711
11712    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
11713    /// an empty array and an empty object apart.
11714    #[test]
11715    fn json_resp_answers_the_document_as_resp_types() {
11716        let mut f = Fixture::new();
11717        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
11718        assert_eq!(
11719            f.run(&[b"JSON.RESP", b"doc"]),
11720            "*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"
11721        );
11722        // A JSONPath wraps the same answer in one more array.
11723        assert_eq!(
11724            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
11725            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
11726        );
11727
11728        f.run(&[
11729            b"JSON.SET",
11730            b"doc",
11731            b"$",
11732            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
11733        ]);
11734        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
11735        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
11736        // A double goes out as its text, so a client reads the same digits
11737        // `JSON.GET` would have given it.
11738        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
11739        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
11740        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
11741
11742        // A missing legacy path is an error, a missing JSONPath is an empty
11743        // array, and a key that is not there is a nil on either.
11744        assert_eq!(
11745            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
11746            "-ERR Path does not exist\r\n"
11747        );
11748        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
11749        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
11750        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
11751    }
11752
11753    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
11754    /// pins the shapes and that the two syntaxes agree rather than a number
11755    /// read off another server. That is D-42.
11756    #[test]
11757    fn json_debug_answers_a_byte_count_and_its_own_help() {
11758        let mut f = Fixture::new();
11759        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
11760        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
11761        assert!(one.starts_with(':'), "{one}");
11762        assert_eq!(
11763            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
11764            format!("*1\r\n{one}")
11765        );
11766        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
11767        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
11768
11769        // A key that is not there is a zero on a legacy path and an empty set
11770        // on a JSONPath, which is the one reader here that does not answer nil
11771        // for it.
11772        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
11773        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
11774        assert_eq!(
11775            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
11776            "-ERR Path does not exist\r\n"
11777        );
11778        assert_eq!(
11779            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
11780            "*0\r\n"
11781        );
11782
11783        assert_eq!(
11784            f.run(&[b"JSON.DEBUG", b"HELP"]),
11785            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
11786             $34\r\nHELP                - this message\r\n"
11787        );
11788        assert_eq!(
11789            f.run(&[b"JSON.DEBUG", b"NOPE"]),
11790            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
11791        );
11792        assert_eq!(
11793            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
11794            "-ERR wrong number of arguments for 'json.debug' command\r\n"
11795        );
11796    }
11797
11798    // ---------------------------------------------------------------- vector
11799
11800    /// The first `VADD` fixes the dimension and every one after it has to
11801    /// agree, because there is no create command to say it earlier.
11802    #[test]
11803    fn the_first_vadd_decides_how_wide_the_set_is() {
11804        let mut f = Fixture::new();
11805        assert_eq!(
11806            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
11807            ":1\r\n"
11808        );
11809        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
11810        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11811        // A second vector under the same name replaces it and says so with a
11812        // zero, so an ingest can count what it created.
11813        assert_eq!(
11814            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
11815            ":0\r\n"
11816        );
11817        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11818        // Three dimensions into a two dimensional set names both numbers, since
11819        // a client that gets this wrong needs to know which end is which.
11820        assert_eq!(
11821            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
11822            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
11823        );
11824        // A vector of zeros has no direction, and it is taken anyway and comes
11825        // back as the origin, because that is what a real server does with it.
11826        assert_eq!(
11827            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
11828            ":1\r\n"
11829        );
11830        assert_eq!(
11831            f.run(&[b"VEMB", b"v", b"nowhere"]),
11832            "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
11833        );
11834        // A set is made with one quantisation and keeps it, and a `VADD` that
11835        // names another is refused. Naming none names `Q8`, which is why this
11836        // set is a `Q8` one.
11837        assert_eq!(
11838            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
11839            "-ERR asked quantization mismatch with existing vector set\r\n"
11840        );
11841        // Nothing above created a key, and a set that never took a vector has
11842        // no dimension to report.
11843        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
11844        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
11845        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
11846    }
11847
11848    /// What a client sent comes back out, and what a client asked for is a
11849    /// similarity and not the distance underneath it.
11850    #[test]
11851    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
11852        let mut f = Fixture::new();
11853        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
11854        // The set stored the direction and the length is multiplied back on the
11855        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
11856        // either, because nobody named a quantisation and that means `Q8`: the
11857        // wider coordinate lands on a code exactly and the other one does not.
11858        // Both numbers are a real server's answers for the same input.
11859        assert_eq!(
11860            f.run(&[b"VEMB", b"v", b"a"]),
11861            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
11862        );
11863        // NOQUANT is the way to ask for what went in to come back out.
11864        f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
11865        assert_eq!(
11866            f.run(&[b"VEMB", b"n", b"a"]),
11867            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
11868        );
11869        // BIN keeps the signs and nothing else, and does not multiply the
11870        // length back on, since a sign has no length in it to scale.
11871        f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
11872        assert_eq!(
11873            f.run(&[b"VEMB", b"b", b"a"]),
11874            "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
11875        );
11876        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
11877        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
11878
11879        // On the axes, where the unit vector is exact and so is the dot
11880        // product, both ends of the scale come out exact: the same direction is
11881        // 1 and the opposite one is 0, with a right angle at a half.
11882        let mut f = Fixture::new();
11883        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
11884        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
11885        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
11886        assert_eq!(
11887            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
11888            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
11889             $8\r\nopposite\r\n$1\r\n0\r\n"
11890        );
11891        // A search from an element leaves that element out, since it is always
11892        // its own nearest neighbour.
11893        assert_eq!(
11894            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
11895            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11896        );
11897        // An element that is not there is an empty answer and not an error,
11898        // which is what a missing key gives too.
11899        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
11900        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
11901        // COUNT bounds it and TRUTH reads every vector rather than the codes,
11902        // which has to agree with the index on a set this small.
11903        assert_eq!(
11904            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
11905            "*1\r\n$6\r\nacross\r\n"
11906        );
11907        assert_eq!(
11908            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
11909            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11910        );
11911        // EF widens how much of the index is read and does not change how many
11912        // answers come back, so a wide search still returns what COUNT asked
11913        // for.
11914        assert_eq!(
11915            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
11916            "*1\r\n$6\r\nacross\r\n"
11917        );
11918
11919        // On RESP3 a scored search is a map, which is what the vector set
11920        // module replies and is not what ZRANGE does here.
11921        let mut g = Fixture::new();
11922        g.run(&[b"HELLO", b"3"]);
11923        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11924        assert_eq!(
11925            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
11926            "%1\r\n$4\r\neast\r\n,1\r\n"
11927        );
11928    }
11929
11930    /// The attribute pair, and the one reply that means two things.
11931    #[test]
11932    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
11933        let mut f = Fixture::new();
11934        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11935        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11936        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
11937        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
11938        // Not parsed as JSON, because nothing reads into it yet and refusing a
11939        // write for a rule nothing enforces would be the wrong trade.
11940        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
11941        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
11942        // An empty string clears it, which is Redis's spelling of the removal.
11943        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
11944        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11945        // An element that is not there answers zero rather than being created,
11946        // since an attribute with no vector under it is not a thing this holds.
11947        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
11948        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
11949        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
11950        // A null for an element with no attribute and a null for one that is
11951        // not there. VISMEMBER is how a client tells the two apart.
11952        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
11953        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
11954        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
11955        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
11956
11957        // WITHATTRIBS carries it alongside the answers.
11958        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11959        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11960        assert_eq!(
11961            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
11962            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
11963        );
11964    }
11965
11966    /// The slot a removed element had is reused, and nothing that was beside it
11967    /// comes back with the next element to get it.
11968    #[test]
11969    fn vrem_takes_the_attribute_with_it() {
11970        let mut f = Fixture::new();
11971        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11972        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11973        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
11974        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
11975        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
11976        // The key went with the last element, the way every other collection
11977        // here works.
11978        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11979
11980        // The next element is given the slot the removed one had, and it comes
11981        // with no attribute on it.
11982        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11983        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11984        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11985        f.run(&[b"VREM", b"v", b"east"]);
11986        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
11987        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
11988    }
11989
11990    /// `VINFO` says what the index is before it says anything a client could
11991    /// mistake for a graph.
11992    #[test]
11993    fn vinfo_says_partition_first() {
11994        let mut f = Fixture::new();
11995        f.run(&[
11996            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
11997        ]);
11998        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
11999        let info = f.run(&[b"VINFO", b"v"]);
12000        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12001        // What the client asked for and not what happened to the tuning, which
12002        // is `10` section 7: M is recorded and changes nothing.
12003        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12004        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12005        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12006        // Nobody named a quantisation, so this set is a `Q8` one and every
12007        // element in it is stored that way.
12008        assert!(
12009            info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12010            "{info}"
12011        );
12012        let mut f = Fixture::new();
12013        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12014        assert!(
12015            f.run(&[b"VINFO", b"v"])
12016                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12017        );
12018        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12019    }
12020
12021    /// A set to read ranges of names out of.
12022    fn named() -> Fixture {
12023        let mut f = Fixture::new();
12024        for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12025            .iter()
12026            .enumerate()
12027        {
12028            let x = (i + 1).to_string();
12029            f.run(&[
12030                b"VADD",
12031                b"r",
12032                b"VALUES",
12033                b"2",
12034                x.as_bytes(),
12035                b"1",
12036                name.as_bytes(),
12037            ]);
12038        }
12039        f
12040    }
12041
12042    /// `VRANGE` reads the names in the order bytes come in and pays no
12043    /// attention to where the vectors point.
12044    #[test]
12045    fn vrange_walks_the_names_and_not_the_vectors() {
12046        let mut f = named();
12047        assert_eq!(
12048            f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12049            "*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"
12050        );
12051        assert_eq!(
12052            f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12053            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12054            "the high end is a name and not a prefix, so delta is past it"
12055        );
12056        assert_eq!(
12057            f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12058            "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12059        );
12060        assert_eq!(
12061            f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12062            "*1\r\n$4\r\nbeta\r\n"
12063        );
12064        assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12065        // Bytes and not letters, so an upper case name sorts before every lower
12066        // case one rather than beside its own spelling.
12067        f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12068        assert_eq!(
12069            f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12070            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12071        );
12072        assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12073    }
12074
12075    /// The count cuts the answer after the range is decided, and zero is not
12076    /// the same as leaving it out.
12077    #[test]
12078    fn a_vrange_count_of_zero_asks_for_nothing() {
12079        let mut f = named();
12080        assert_eq!(
12081            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12082            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12083        );
12084        assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12085        assert!(
12086            f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12087                .starts_with("*5\r\n"),
12088            "a negative count is no limit at all"
12089        );
12090    }
12091
12092    /// Both ends are read before either is placed, and the count is read before
12093    /// either end.
12094    #[test]
12095    fn vrange_says_which_end_it_could_not_read() {
12096        let mut f = named();
12097        assert_eq!(
12098            f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12099            "-ERR invalid start range format\r\n"
12100        );
12101        assert_eq!(
12102            f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12103            "-ERR invalid end range format\r\n",
12104            "the high end is spelled wrong, which is worth saying before the \
12105             low end being on the wrong side"
12106        );
12107        assert_eq!(
12108            f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12109            "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12110        );
12111        // A bracket with nothing after it is not the empty name here, though an
12112        // element really can be called that.
12113        assert_eq!(
12114            f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12115            "-ERR invalid start range format\r\n"
12116        );
12117        assert_eq!(
12118            f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12119            "-ERR invalid COUNT value\r\n"
12120        );
12121        assert_eq!(
12122            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12123            "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12124        );
12125        f.run(&[b"SET", b"s", b"x"]);
12126        assert!(
12127            f.run(&[b"VRANGE", b"s", b"-", b"+"])
12128                .starts_with("-WRONGTYPE")
12129        );
12130    }
12131
12132    /// The option that asks for something this index does not have says so
12133    /// rather than doing something else quietly.
12134    #[test]
12135    fn reduce_is_refused_and_not_ignored() {
12136        let mut f = Fixture::new();
12137        let reduce = f.run(&[
12138            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12139        ]);
12140        assert!(
12141            reduce.starts_with("-ERR REDUCE is not supported."),
12142            "{reduce}"
12143        );
12144        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12145    }
12146
12147    /// A filtered search answers with the nearest elements that match, and an
12148    /// expression that is not one is an error before the key is looked at.
12149    #[test]
12150    fn vsim_filter_reads_the_attributes() {
12151        let mut f = Fixture::new();
12152        for (name, x, y, attr) in [
12153            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12154            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12155            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12156            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12157        ] {
12158            f.run(&[
12159                b"VADD",
12160                b"v",
12161                b"VALUES",
12162                b"2",
12163                x.as_bytes(),
12164                y.as_bytes(),
12165                name.as_bytes(),
12166                b"SETATTR",
12167                attr.as_bytes(),
12168            ]);
12169        }
12170        // `b` is the nearest to the query and is the one the filter drops, so
12171        // this is the answer a filter applied afterwards would have got wrong.
12172        assert_eq!(
12173            f.run(&[
12174                b"VSIM",
12175                b"v",
12176                b"VALUES",
12177                b"2",
12178                b"9",
12179                b"1",
12180                b"COUNT",
12181                b"2",
12182                b"FILTER",
12183                b".lang == \"en\"",
12184            ]),
12185            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12186        );
12187        // A number is compared as a number, and the two halves of an `and` both
12188        // have to hold.
12189        assert_eq!(
12190            f.run(&[
12191                b"VSIM",
12192                b"v",
12193                b"VALUES",
12194                b"2",
12195                b"9",
12196                b"1",
12197                b"FILTER",
12198                b".lang == 'en' and .year > 1980",
12199            ]),
12200            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12201        );
12202        // A list, and a field an element does not have.
12203        assert_eq!(
12204            f.run(&[
12205                b"VSIM",
12206                b"v",
12207                b"VALUES",
12208                b"2",
12209                b"9",
12210                b"1",
12211                b"FILTER",
12212                b".lang in ['fr', 'de']",
12213            ]),
12214            "*1\r\n$1\r\nb\r\n"
12215        );
12216        assert_eq!(
12217            f.run(&[
12218                b"VSIM",
12219                b"v",
12220                b"VALUES",
12221                b"2",
12222                b"9",
12223                b"1",
12224                b"FILTER",
12225                b".rating > 3"
12226            ]),
12227            "*0\r\n"
12228        );
12229        // TRUTH measures every vector, and the filter still decides which ones
12230        // are measured.
12231        assert_eq!(
12232            f.run(&[
12233                b"VSIM",
12234                b"v",
12235                b"VALUES",
12236                b"2",
12237                b"9",
12238                b"1",
12239                b"TRUTH",
12240                b"FILTER",
12241                b".year < 1980",
12242            ]),
12243            "*1\r\n$1\r\nc\r\n"
12244        );
12245        // VSETATTR moves an element in and out of a filter, which means the tag
12246        // beside its code was rewritten and not just the string.
12247        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12248        assert_eq!(
12249            f.run(&[
12250                b"VSIM",
12251                b"v",
12252                b"VALUES",
12253                b"2",
12254                b"9",
12255                b"1",
12256                b"COUNT",
12257                b"1",
12258                b"FILTER",
12259                b".lang == \"en\"",
12260            ]),
12261            "*1\r\n$1\r\nb\r\n"
12262        );
12263        // And a VADD that replaces the vector keeps the attribute and the tag,
12264        // which is the same rewrite from the other end.
12265        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12266        assert_eq!(
12267            f.run(&[
12268                b"VSIM",
12269                b"v",
12270                b"VALUES",
12271                b"2",
12272                b"9",
12273                b"1",
12274                b"COUNT",
12275                b"1",
12276                b"FILTER",
12277                b".lang == \"en\"",
12278            ]),
12279            "*1\r\n$1\r\nb\r\n"
12280        );
12281
12282        // The expression is parsed before the key is read, so a bad one is an
12283        // error whether or not the key is there.
12284        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12285        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12286        assert_eq!(
12287            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12288            "-ERR invalid FILTER expression\r\n"
12289        );
12290        // FILTER-EF raises the effort rather than capping it, and zero is
12291        // Redis's word for no limit, so neither is an error.
12292        assert_eq!(
12293            f.run(&[
12294                b"VSIM",
12295                b"v",
12296                b"VALUES",
12297                b"2",
12298                b"9",
12299                b"1",
12300                b"COUNT",
12301                b"1",
12302                b"FILTER-EF",
12303                b"500",
12304                b"FILTER",
12305                b".lang == 'en'",
12306            ]),
12307            "*1\r\n$1\r\nb\r\n"
12308        );
12309        assert_eq!(
12310            f.run(&[
12311                b"VSIM",
12312                b"v",
12313                b"VALUES",
12314                b"2",
12315                b"9",
12316                b"1",
12317                b"COUNT",
12318                b"1",
12319                b"FILTER-EF",
12320                b"0"
12321            ]),
12322            "*1\r\n$1\r\nb\r\n"
12323        );
12324        assert_eq!(
12325            f.run(&[
12326                b"VSIM",
12327                b"v",
12328                b"VALUES",
12329                b"2",
12330                b"9",
12331                b"1",
12332                b"FILTER-EF",
12333                b"lots"
12334            ]),
12335            "-ERR EF must be a positive integer\r\n"
12336        );
12337    }
12338
12339    /// A vector set key is a key, so the keyspace owns it the way it owns every
12340    /// other one and none of those commands know what is inside it.
12341    #[test]
12342    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12343        let mut f = Fixture::new();
12344        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12345        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12346        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12347        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12348        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12349        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12350        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12351        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12352        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12353        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12354        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12355
12356        // And the wrong type is the wrong type in both directions.
12357        f.run(&[b"SET", b"s", b"1"]);
12358        assert_eq!(
12359            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12360            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12361        );
12362        assert_eq!(
12363            f.run(&[b"VCARD", b"s"]),
12364            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12365        );
12366        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12367        assert_eq!(
12368            f.run(&[b"GET", b"v"]),
12369            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12370        );
12371        // A graph and a vector set share the escape in the record tag and are
12372        // still two different types, which is the case the tag alone cannot
12373        // decide.
12374        f.run(&[b"G.NADD", b"social", b"ada"]);
12375        assert_eq!(
12376            f.run(&[b"VCARD", b"social"]),
12377            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12378        );
12379        assert_eq!(
12380            f.run(&[b"G.NGET", b"v", b"ada"]),
12381            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12382        );
12383    }
12384
12385    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
12386    /// shapes, off the database's own generator.
12387    #[test]
12388    fn vrandmember_has_the_two_shapes_srandmember_has() {
12389        let mut f = Fixture::new();
12390        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12391            let x = (i + 1).to_string();
12392            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12393        }
12394        // One element is a bulk string and not an array of one.
12395        let one = f.run(&[b"VRANDMEMBER", b"v"]);
12396        assert!(one.starts_with("$1\r\n"), "{one}");
12397        // A positive count is distinct and stops at the size of the set.
12398        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12399        assert!(all.starts_with("*3\r\n"), "{all}");
12400        for name in ["a", "b", "c"] {
12401            assert!(all.contains(name), "{all} is missing {name}");
12402        }
12403        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12404        assert!(all.starts_with("*2\r\n"), "{all}");
12405        // A negative one draws that many and allows repeats.
12406        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12407        assert!(many.starts_with("*5\r\n"), "{many}");
12408        // A key that is not there answers the shape that was asked for.
12409        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12410        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12411    }
12412
12413    /// `VLINKS` answers about the index that is here rather than the graph that
12414    /// is not, which is D-2.
12415    #[test]
12416    fn vlinks_reports_one_layer_of_partition_neighbours() {
12417        let mut f = Fixture::new();
12418        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12419        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12420        // One layer deep, because the index is one layer deep, so a client
12421        // walking layers gets a short list and not a shape it cannot parse.
12422        assert_eq!(
12423            f.run(&[b"VLINKS", b"v", b"east"]),
12424            "*1\r\n*1\r\n$5\r\nnorth\r\n"
12425        );
12426        assert_eq!(
12427            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12428            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12429        );
12430        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12431        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12432    }
12433
12434    /// A vector arrives either as digits or as bytes, and the two have to mean
12435    /// the same thing.
12436    #[test]
12437    fn fp32_and_values_are_the_same_vector() {
12438        let mut f = Fixture::new();
12439        let mut blob = Vec::new();
12440        for x in [3.0f32, 4.0] {
12441            blob.extend_from_slice(&x.to_le_bytes());
12442        }
12443        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12444        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12445        assert_eq!(
12446            f.run(&[b"VEMB", b"v", b"a"]),
12447            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12448        );
12449        // RAW is the stored bytes and the numbers that turn them back into the
12450        // client's vector, which for `Q8` is a code a coordinate, the length the
12451        // vector arrived with and the scale the codes are measured against. The
12452        // name of the form is a simple string, which is a real server's shape,
12453        // and all four of these are a real server's answers.
12454        assert_eq!(
12455            f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12456            "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12457        );
12458        // A blob that is not a whole number of floats is not a vector.
12459        assert_eq!(
12460            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12461            "-ERR invalid vector specification\r\n"
12462        );
12463        // Neither is a count that promises more than arrived.
12464        assert_eq!(
12465            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12466            "-ERR syntax error\r\n"
12467        );
12468        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12469    }
12470
12471    // ----------------------------------------------------------------- bloom
12472
12473    /// The filter a client gets when it does not describe one, and the two
12474    /// answers an add can give.
12475    #[test]
12476    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12477        let mut f = Fixture::new();
12478        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12479        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12480        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12481        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12482        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12483        // The defaults are the module's configs and not anything the command
12484        // said, which is 100 entries at a hundredth and a growth of 2.
12485        assert_eq!(
12486            f.run(&[b"BF.INFO", b"b"]),
12487            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12488             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12489             +Expansion rate\r\n:2\r\n"
12490        );
12491        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12492        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12493        // A key that is not there has no filter to report on, and answers two
12494        // different ways about it depending on which command asked.
12495        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12496        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12497    }
12498
12499    /// `BF.EXISTS` on a key holding something else answers a miss, and
12500    /// everything else in the family answers `WRONGTYPE`.
12501    ///
12502    /// The two halves of a check and set disagree about what that key is, which
12503    /// is the module's behaviour and not a decision taken here.
12504    #[test]
12505    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12506        let mut f = Fixture::new();
12507        f.run(&[b"SET", b"s", b"text"]);
12508        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12509        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12510        for cmd in [
12511            vec![&b"BF.ADD"[..], b"s", b"x"],
12512            vec![&b"BF.MADD"[..], b"s", b"x"],
12513            vec![&b"BF.CARD"[..], b"s"],
12514            vec![&b"BF.INFO"[..], b"s"],
12515            vec![&b"BF.DEBUG"[..], b"s"],
12516            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12517        ] {
12518            let name = String::from_utf8_lossy(cmd[0]).into_owned();
12519            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12520        }
12521        // The arguments are read before the key is, so a reserve with a bad
12522        // error rate complains about the rate and never learns about the string.
12523        assert_eq!(
12524            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12525            "-ERR bad error rate\r\n"
12526        );
12527        assert!(
12528            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12529                .starts_with("-WRONGTYPE")
12530        );
12531    }
12532
12533    /// A chain grows by its expansion factor and each link is half as wrong as
12534    /// the one before, which is what makes the whole filter hold its rate.
12535    #[test]
12536    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12537        let mut f = Fixture::new();
12538        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12539        for i in 0..10u32 {
12540            assert_eq!(
12541                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12542                ":1\r\n"
12543            );
12544        }
12545        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12546        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12547        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12548        // Capacity is the sum of every link and not the number that was asked
12549        // for, so it is 10 and then 10 plus 20.
12550        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12551        assert_eq!(
12552            f.run(&[b"BF.DEBUG", b"g"]),
12553            "*3\r\n$7\r\nsize:11\r\n\
12554             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12555             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12556        );
12557
12558        // The same filter told not to grow fills instead.
12559        assert_eq!(
12560            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12561            "+OK\r\n"
12562        );
12563        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12564        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12565        assert_eq!(
12566            f.run(&[b"BF.ADD", b"n", b"c"]),
12567            "-ERR non scaling filter is full\r\n"
12568        );
12569        // And an item that is already in it still answers, because membership
12570        // is checked before fullness.
12571        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12572        // A filter that will not grow has no expansion rate to report, in
12573        // either of the two spellings that make one.
12574        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12575        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12576        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12577        // Asking for both at once is refused, which is one of the module's
12578        // errors that carries no prefix at all.
12579        assert_eq!(
12580            f.run(&[
12581                b"BF.RESERVE",
12582                b"q",
12583                b"0.01",
12584                b"2",
12585                b"NONSCALING",
12586                b"EXPANSION",
12587                b"2"
12588            ]),
12589            "-Nonscaling filters cannot expand\r\n"
12590        );
12591    }
12592
12593    /// A multi add stops where the filter did, so the reply can be shorter than
12594    /// the argument list.
12595    #[test]
12596    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
12597        let mut f = Fixture::new();
12598        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
12599        assert_eq!(
12600            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
12601            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
12602        );
12603        assert_eq!(
12604            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
12605            "*2\r\n:1\r\n:0\r\n"
12606        );
12607    }
12608
12609    /// `BF.INSERT` describes a filter and fills it in one command, with its own
12610    /// spelling of every complaint.
12611    #[test]
12612    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
12613        let mut f = Fixture::new();
12614        assert_eq!(
12615            f.run(&[
12616                b"BF.INSERT",
12617                b"i",
12618                b"CAPACITY",
12619                b"50",
12620                b"ERROR",
12621                b"0.001",
12622                b"ITEMS",
12623                b"a",
12624                b"b"
12625            ]),
12626            "*2\r\n:1\r\n:1\r\n"
12627        );
12628        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
12629        // NOCREATE is the only way to add without making the key.
12630        assert_eq!(
12631            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12632            "-ERR not found\r\n"
12633        );
12634        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12635        // The same mistakes as BF.RESERVE, in the sentences this command uses
12636        // for them, and one sentence where BF.RESERVE has two.
12637        assert_eq!(
12638            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12639            "-Bad capacity\r\n"
12640        );
12641        assert_eq!(
12642            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
12643            "-Bad error rate\r\n"
12644        );
12645        assert_eq!(
12646            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
12647            "-Bad expansion\r\n"
12648        );
12649        // An option is matched on its first letter and not on the word, so a
12650        // token nobody meant as an option is one anyway if it starts with the
12651        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
12652        // builds says so.
12653        assert_eq!(
12654            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
12655            "*1\r\n:1\r\n"
12656        );
12657        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
12658        // Only E and N need a second look, one for ERROR against EXPANSION and
12659        // the other for NOCREATE against NONSCALING, and both stop as soon as
12660        // they can tell the two apart.
12661        assert_eq!(
12662            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
12663            "*1\r\n:1\r\n"
12664        );
12665        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
12666        assert_eq!(
12667            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
12668            "*1\r\n:1\r\n"
12669        );
12670        assert_eq!(
12671            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
12672            "-ERR not found\r\n"
12673        );
12674        // A letter that starts nothing is the one case that is refused.
12675        assert_eq!(
12676            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12677            "-Unknown argument received\r\n"
12678        );
12679        // Everything after ITEMS is an item, even when it spells an option.
12680        assert_eq!(
12681            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12682            "*1\r\n:1\r\n"
12683        );
12684        // And ITEMS with nothing after it is the same as leaving it out.
12685        assert!(
12686            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12687                .contains("wrong number of arguments")
12688        );
12689    }
12690
12691    /// A filter dumped a chunk at a time and put back into another key is the
12692    /// same filter.
12693    #[test]
12694    fn a_dump_replays_into_a_filter_that_answers_the_same() {
12695        let mut f = Fixture::new();
12696        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
12697        for i in 0..25u32 {
12698            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
12699        }
12700        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
12701
12702        // Iterator zero asks for the header and every one after it is a running
12703        // byte offset, and a chunk never spans two links.
12704        let mut iter = b"0".to_vec();
12705        let mut chunks = 0;
12706        loop {
12707            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
12708            let text = String::from_utf8_lossy(&raw).into_owned();
12709            let next = text
12710                .split("\r\n")
12711                .nth(1)
12712                .and_then(|n| n.strip_prefix(':'))
12713                .expect("a two element reply of an iterator and a chunk")
12714                .to_owned();
12715            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12716            let data = &body[body
12717                .windows(2)
12718                .position(|w| w == b"\r\n")
12719                .expect("a length line")
12720                + 2..body.len() - 2];
12721            if next == "0" {
12722                assert!(data.is_empty(), "the last chunk is empty");
12723                break;
12724            }
12725            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
12726            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
12727            iter = next.into_bytes();
12728            chunks += 1;
12729        }
12730        assert_eq!(chunks, 3, "a header and one chunk per link");
12731
12732        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
12733        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
12734        for i in 0..25u32 {
12735            assert_eq!(
12736                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
12737                ":1\r\n"
12738            );
12739        }
12740
12741        // A header on top of a filter is refused rather than merged, and so is
12742        // one that no filter wrote.
12743        assert_eq!(
12744            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
12745            "-ERR received bad data\r\n"
12746        );
12747        assert_eq!(
12748            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
12749            "-ERR received bad data\r\n"
12750        );
12751        // An offset past the end of the filter names itself.
12752        assert_eq!(
12753            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
12754            "-ERR invalid offset - no link found\r\n"
12755        );
12756        assert_eq!(
12757            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
12758            "-ERR Second argument must be numeric\r\n"
12759        );
12760        // The same complaint without the prefix on the way out, which is the
12761        // module's inconsistency and not a slip here.
12762        assert_eq!(
12763            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
12764            "-Second argument must be numeric\r\n"
12765        );
12766    }
12767
12768    /// The argument checks, which have a sentence each and read numbers the way
12769    /// Redis reads them everywhere else.
12770    #[test]
12771    fn reserve_reads_its_numbers_the_way_string2ll_does() {
12772        let mut f = Fixture::new();
12773        for (args, want) in [
12774            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
12775            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
12776            (
12777                vec![&b"0"[..], b"10"],
12778                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12779            ),
12780            (
12781                vec![&b"1"[..], b"10"],
12782                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12783            ),
12784            (
12785                vec![&b"inf"[..], b"10"],
12786                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12787            ),
12788            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
12789            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
12790            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
12791            (
12792                vec![&b"0.01"[..], b"0"],
12793                "-ERR capacity must be in the range [1, 1073741824]\r\n",
12794            ),
12795            (
12796                vec![&b"0.01"[..], b"1073741825"],
12797                "-ERR capacity must be in the range [1, 1073741824]\r\n",
12798            ),
12799        ] {
12800            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
12801            cmd.extend(args.iter().copied());
12802            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
12803        }
12804        assert_eq!(
12805            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
12806            "-ERR no expansion\r\n"
12807        );
12808        assert_eq!(
12809            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
12810            "-ERR bad expansion\r\n"
12811        );
12812        assert_eq!(
12813            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
12814            "-ERR expansion must be in the range [0, 32768]\r\n"
12815        );
12816        // Trailing rubbish after the capacity is ignored rather than refused.
12817        assert_eq!(
12818            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
12819            "+OK\r\n"
12820        );
12821        assert_eq!(
12822            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
12823            "-ERR item exists\r\n"
12824        );
12825        assert_eq!(
12826            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
12827            "-Invalid information value\r\n"
12828        );
12829        assert!(
12830            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
12831                .contains("wrong number of arguments")
12832        );
12833    }
12834
12835    /// The RESP3 shapes, which are where this family differs most from RESP2.
12836    #[test]
12837    fn the_bloom_family_answers_in_resp3_spelling_too() {
12838        let mut f = Fixture::new();
12839        f.out.set_proto(Proto::Resp3);
12840        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
12841        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
12842        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
12843        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
12844        assert_eq!(
12845            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
12846            "*2\r\n#t\r\n#f\r\n"
12847        );
12848        // The count stays an integer, because it counts rather than answers.
12849        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
12850        assert_eq!(
12851            f.run(&[b"BF.INFO", b"b"]),
12852            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12853             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
12854             +Expansion rate\r\n:2\r\n"
12855        );
12856        // One field is a map of one here and a bare array of one on RESP2, so
12857        // this is the reply where the two protocols carry different facts.
12858        assert_eq!(
12859            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
12860            "%1\r\n+Capacity\r\n:100\r\n"
12861        );
12862    }
12863
12864    // ---------------------------------------------------------------- cuckoo
12865
12866    /// A dump header, which is the four counts and the three widths a filter
12867    /// writes in front of its fingerprints.
12868    ///
12869    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
12870    /// tests below want out of it is the states a filter cannot be put into
12871    /// from the wire.
12872    fn cf_header(
12873        items: u64,
12874        buckets: u64,
12875        deletes: u64,
12876        filters: u64,
12877        geometry: [u16; 3],
12878    ) -> Vec<u8> {
12879        let mut out = Vec::with_capacity(38);
12880        for n in [items, buckets, deletes, filters] {
12881            out.extend_from_slice(&n.to_le_bytes());
12882        }
12883        for n in geometry {
12884            out.extend_from_slice(&n.to_le_bytes());
12885        }
12886        out
12887    }
12888
12889    /// The filter a client gets when it does not describe one, and the thing a
12890    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
12891    /// take them out again.
12892    #[test]
12893    fn cf_add_makes_the_filter_and_counts_the_copies() {
12894        let mut f = Fixture::new();
12895        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12896        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12897        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
12898        // The NX form is the one that looks first, which is why it is a command
12899        // of its own rather than an option.
12900        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
12901        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
12902        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
12903        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
12904        assert_eq!(
12905            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
12906            "*2\r\n:1\r\n:0\r\n"
12907        );
12908        // The defaults are the module's configs: 1024 entries over buckets of
12909        // two, twenty kicks and a chain that grows by one.
12910        assert_eq!(
12911            f.run(&[b"CF.INFO", b"d"]),
12912            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
12913             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
12914             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
12915             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12916        );
12917        assert_eq!(
12918            f.run(&[b"CF.DEBUG", b"d"]),
12919            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
12920             max_iterations:20 expansion:1\r\n"
12921        );
12922        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
12923        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
12924
12925        // A delete takes one copy, so the same item goes twice and then stops.
12926        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12927        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
12928        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12929        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
12930        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
12931
12932        // A key with no filter under it gets three different sentences and one
12933        // plain miss, depending on which command asked.
12934        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
12935        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
12936        assert_eq!(
12937            f.run(&[b"CF.COMPACT", b"gone"]),
12938            "-Cuckoo filter was not found\r\n"
12939        );
12940        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
12941        // And `CF.COMPACT` is declared as taking any number of keys and takes
12942        // exactly one, which is the module's own arity being wrong rather than
12943        // this table's.
12944        assert!(
12945            f.run(&[b"CF.COMPACT", b"a", b"b"])
12946                .contains("wrong number of arguments")
12947        );
12948    }
12949
12950    /// The four that only read fingerprints treat a key holding something else
12951    /// as a key with no filter, and everything else answers `WRONGTYPE`.
12952    #[test]
12953    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
12954        let mut f = Fixture::new();
12955        f.run(&[b"SET", b"s", b"text"]);
12956        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
12957        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12958        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
12959        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
12960        // and is declared read only, so neither of the two halves of the family
12961        // is the same set as the flags say.
12962        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
12963        assert_eq!(
12964            f.run(&[b"CF.COMPACT", b"s"]),
12965            "-Cuckoo filter was not found\r\n"
12966        );
12967        for cmd in [
12968            vec![&b"CF.ADD"[..], b"s", b"x"],
12969            vec![&b"CF.ADDNX"[..], b"s", b"x"],
12970            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
12971            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
12972            vec![&b"CF.INFO"[..], b"s"],
12973            vec![&b"CF.DEBUG"[..], b"s"],
12974            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
12975            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
12976            vec![&b"CF.RESERVE"[..], b"s", b"64"],
12977        ] {
12978            let name = String::from_utf8_lossy(cmd[0]).into_owned();
12979            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12980        }
12981    }
12982
12983    /// `CF.RESERVE` reads its options by name in an order of its own, and the
12984    /// first pair with a given name is the only one it looks at.
12985    #[test]
12986    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
12987        let mut f = Fixture::new();
12988        assert_eq!(
12989            f.run(&[
12990                b"CF.RESERVE",
12991                b"r",
12992                b"64",
12993                b"BUCKETSIZE",
12994                b"1",
12995                b"MAXITERATIONS",
12996                b"7",
12997                b"EXPANSION",
12998                b"4"
12999            ]),
13000            "+OK\r\n"
13001        );
13002        assert_eq!(
13003            f.run(&[b"CF.DEBUG", b"r"]),
13004            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13005             max_iterations:7 expansion:4\r\n"
13006        );
13007        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13008
13009        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13010        assert_eq!(
13011            f.run(&[b"CF.RESERVE", b"q", b"1"]),
13012            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13013        );
13014        // The range is the bucket size's and not a constant, so a capacity that
13015        // was fine at two slots a bucket is not at four.
13016        assert_eq!(
13017            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13018            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13019        );
13020        assert_eq!(
13021            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13022            "+OK\r\n"
13023        );
13024
13025        // The capacity is checked last, so a command that is wrong twice
13026        // answers about the option. Which option it answers about is the order
13027        // the module looks for them in and not the order they were written, so
13028        // a bad kick budget wins over a bad bucket size wherever the two sit.
13029        assert_eq!(
13030            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13031            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13032        );
13033        assert_eq!(
13034            f.run(&[
13035                b"CF.RESERVE",
13036                b"q2",
13037                b"64",
13038                b"EXPANSION",
13039                b"xx",
13040                b"BUCKETSIZE",
13041                b"0"
13042            ]),
13043            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13044        );
13045        assert_eq!(
13046            f.run(&[
13047                b"CF.RESERVE",
13048                b"q2",
13049                b"64",
13050                b"MAXITERATIONS",
13051                b"0",
13052                b"BUCKETSIZE",
13053                b"0"
13054            ]),
13055            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13056        );
13057        // A second pair with a name that has already been read is not looked at
13058        // at all, so this one is a filter with buckets of one rather than an
13059        // error about a bucket size of zero.
13060        assert_eq!(
13061            f.run(&[
13062                b"CF.RESERVE",
13063                b"q3",
13064                b"64",
13065                b"BUCKETSIZE",
13066                b"1",
13067                b"BUCKETSIZE",
13068                b"0"
13069            ]),
13070            "+OK\r\n"
13071        );
13072        // A pair nobody knows is dropped, which is the opposite of what
13073        // `CF.INSERT` does with the same mistake.
13074        assert_eq!(
13075            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13076            "+OK\r\n"
13077        );
13078        assert_eq!(
13079            f.run(&[b"CF.DEBUG", b"q4"]),
13080            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13081             max_iterations:20 expansion:1\r\n"
13082        );
13083        // And an option with nothing after it leaves an odd number of them,
13084        // which is an arity error rather than a complaint about the option.
13085        assert!(
13086            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13087                .contains("wrong number of arguments")
13088        );
13089    }
13090
13091    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
13092    /// with `CF.RESERVE` about nothing.
13093    #[test]
13094    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13095        let mut f = Fixture::new();
13096        assert_eq!(
13097            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13098            "*2\r\n:1\r\n:1\r\n"
13099        );
13100        assert_eq!(
13101            f.run(&[b"CF.DEBUG", b"i"]),
13102            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13103             max_iterations:20 expansion:1\r\n"
13104        );
13105        // The NX form has three answers rather than two, which is why it stays
13106        // integers on both protocols.
13107        assert_eq!(
13108            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13109            "*2\r\n:0\r\n:1\r\n"
13110        );
13111        assert_eq!(
13112            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13113            "-ERR not found\r\n"
13114        );
13115        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13116
13117        assert_eq!(
13118            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13119            "-Bad capacity\r\n"
13120        );
13121        // The bucket size cannot be given here, so the range names the config
13122        // that holds it instead of the option `CF.RESERVE` names.
13123        assert_eq!(
13124            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13125            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13126        );
13127        // Every occurrence is checked, which is where this differs from
13128        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
13129        // one is the one that would have been used.
13130        assert_eq!(
13131            f.run(&[
13132                b"CF.INSERT",
13133                b"i",
13134                b"CAPACITY",
13135                b"8",
13136                b"CAPACITY",
13137                b"2",
13138                b"ITEMS",
13139                b"a"
13140            ]),
13141            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13142        );
13143        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
13144        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
13145        // refused.
13146        assert_eq!(
13147            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13148            "*1\r\n:1\r\n"
13149        );
13150        assert_eq!(
13151            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13152            "*1\r\n:1\r\n"
13153        );
13154        assert_eq!(
13155            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13156            "-Unknown argument received\r\n"
13157        );
13158        // Everything after ITEMS is an item, even when it spells an option.
13159        assert_eq!(
13160            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13161            "*1\r\n:1\r\n"
13162        );
13163        // And the two ways of sending no items at all are the same complaint.
13164        assert!(
13165            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13166                .contains("wrong number of arguments")
13167        );
13168        assert!(
13169            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13170                .contains("wrong number of arguments")
13171        );
13172    }
13173
13174    /// The two walls a filter can hit, which say different things and are not
13175    /// the same wall.
13176    #[test]
13177    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13178        let mut f = Fixture::new();
13179        f.run(&[
13180            b"CF.RESERVE",
13181            b"s",
13182            b"4",
13183            b"BUCKETSIZE",
13184            b"1",
13185            b"EXPANSION",
13186            b"0",
13187        ]);
13188        for i in 0..4u32 {
13189            assert_eq!(
13190                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13191                ":1\r\n"
13192            );
13193        }
13194        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13195        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13196        // The add commands say it in a sentence and the insert commands say it
13197        // in the array, one value per item, and the array is never short.
13198        assert_eq!(
13199            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13200            "*2\r\n:-1\r\n:-1\r\n"
13201        );
13202        assert_eq!(
13203            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13204            "*2\r\n:0\r\n:-1\r\n"
13205        );
13206
13207        // A chain that is allowed to grow stops for a different reason, and the
13208        // count it stops at is the filter limit rather than the room: this one
13209        // gives up with three slots free. Loading a chain that already has
13210        // every filter it is allowed shows why, since it refuses an item
13211        // straight into an empty one.
13212        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13213        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13214        assert_eq!(
13215            f.run(&[b"CF.ADD", b"g", b"q"]),
13216            "-Maximum expansions reached\r\n"
13217        );
13218        assert_eq!(
13219            f.run(&[b"CF.INFO", b"g"]),
13220            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13221             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13222             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13223             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13224        );
13225    }
13226
13227    /// A filter dumped a chunk at a time and put back under another key is the
13228    /// same filter, and the headers that describe one nobody could build are
13229    /// refused on the way in.
13230    #[test]
13231    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13232        let mut f = Fixture::new();
13233        f.run(&[
13234            b"CF.RESERVE",
13235            b"src",
13236            b"8",
13237            b"BUCKETSIZE",
13238            b"2",
13239            b"EXPANSION",
13240            b"2",
13241        ]);
13242        for i in 0..40u32 {
13243            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13244        }
13245        // Position zero asks for the header and every one after it is a byte
13246        // offset across every filter laid end to end, and the walk ends on a
13247        // zero and a nil rather than an empty chunk.
13248        let mut pos = b"0".to_vec();
13249        let mut chunks = 0;
13250        loop {
13251            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13252            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13253            let next = head
13254                .split("\r\n")
13255                .nth(1)
13256                .and_then(|n| n.strip_prefix(':'))
13257                .expect("a two element reply of a position and a chunk")
13258                .to_owned();
13259            if next == "0" {
13260                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13261                break;
13262            }
13263            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13264            let at = body
13265                .windows(2)
13266                .position(|w| w == b"\r\n")
13267                .expect("a length line")
13268                + 2;
13269            let data = &body[at..body.len() - 2];
13270            assert_eq!(
13271                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13272                "+OK\r\n",
13273                "loading chunk {chunks}"
13274            );
13275            pos = next.into_bytes();
13276            chunks += 1;
13277        }
13278        assert!(chunks >= 2, "a header and at least one chunk");
13279
13280        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13281        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13282        for i in 0..40u32 {
13283            assert_eq!(
13284                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13285                ":1\r\n"
13286            );
13287        }
13288
13289        // A filter with nothing in it hands out no header at all, so a client
13290        // that dumps one has nothing to load back.
13291        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13292        assert_eq!(
13293            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13294            "*2\r\n:0\r\n$-1\r\n"
13295        );
13296
13297        // The positions this end will not take, which are not the same set at
13298        // both ends: a dump refuses a negative one and a load takes it as an
13299        // offset and fails to find anything there.
13300        assert_eq!(
13301            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13302            "-Invalid position\r\n"
13303        );
13304        assert_eq!(
13305            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13306            "-Invalid position\r\n"
13307        );
13308        assert_eq!(
13309            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13310            "-Invalid position\r\n"
13311        );
13312        assert_eq!(
13313            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13314            "-Couldn't load chunk!\r\n"
13315        );
13316        // A header on top of a filter is refused rather than merged.
13317        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13318        assert_eq!(
13319            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13320            "-ERR item exists\r\n"
13321        );
13322        // A chunk that is not the size of a header where a header should have
13323        // been is one sentence, and one that is the size of a header and
13324        // describes a filter nobody could build is another.
13325        assert_eq!(
13326            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13327            "-Invalid header\r\n"
13328        );
13329        for (why, bad) in [
13330            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13331            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13332            (
13333                "a bucket count that is not a power of two",
13334                cf_header(0, 3, 0, 1, [2, 20, 1]),
13335            ),
13336            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13337            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13338            (
13339                "a growth nobody could reach",
13340                cf_header(0, 8, 0, 1, [2, 20, 32769]),
13341            ),
13342            (
13343                "a chain that cannot grow and did",
13344                cf_header(0, 8, 0, 2, [2, 20, 0]),
13345            ),
13346            // The count is written in eight bytes and read into two, so a
13347            // number that is a multiple of the second arrives as none.
13348            (
13349                "a filter count that wraps",
13350                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13351            ),
13352        ] {
13353            assert_eq!(
13354                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13355                "-Couldn't create filter!\r\n",
13356                "{why}"
13357            );
13358        }
13359    }
13360
13361    /// The RESP3 shapes, which are where this family differs most from RESP2
13362    /// and where one of its answers stops being readable.
13363    #[test]
13364    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13365        let mut f = Fixture::new();
13366        f.out.set_proto(Proto::Resp3);
13367        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13368        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13369        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13370        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13371        assert_eq!(
13372            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13373            "*2\r\n#t\r\n#f\r\n"
13374        );
13375        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13376        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13377        // The count stays an integer, because it counts rather than answers.
13378        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13379        assert_eq!(
13380            f.run(&[b"CF.INFO", b"c"]),
13381            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13382             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13383             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13384             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13385        );
13386
13387        // `CF.INSERT` writes a boolean per item here and an integer per item on
13388        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
13389        // client cannot tell an item that did not fit from one that is already
13390        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
13391        f.run(&[
13392            b"CF.RESERVE",
13393            b"s",
13394            b"4",
13395            b"BUCKETSIZE",
13396            b"1",
13397            b"EXPANSION",
13398            b"0",
13399        ]);
13400        assert_eq!(
13401            f.run(&[
13402                b"CF.INSERT",
13403                b"s",
13404                b"ITEMS",
13405                b"a",
13406                b"b",
13407                b"c",
13408                b"d",
13409                b"e",
13410                b"f"
13411            ]),
13412            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13413        );
13414        assert_eq!(
13415            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13416            "*2\r\n:0\r\n:-1\r\n"
13417        );
13418        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13419        // The end of a dump is a nil and not an empty chunk, which is one
13420        // underscore here and a negative length on RESP2.
13421        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13422    }
13423
13424    // ------------------------------------------------------------------- cms
13425
13426    /// A sketch is made from either end, and both constructors look at the key
13427    /// before they look at their arguments.
13428    #[test]
13429    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13430        let mut f = Fixture::new();
13431        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13432        assert_eq!(
13433            f.run(&[b"CMS.INFO", b"d"]),
13434            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13435        );
13436        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13437        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13438        // Two over the error rounded up, and the log of the probability over the
13439        // log of a half rounded up, which for these two is 200 by 6.
13440        assert_eq!(
13441            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13442            "+OK\r\n"
13443        );
13444        assert_eq!(
13445            f.run(&[b"CMS.INFO", b"p"]),
13446            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13447        );
13448        // The key is checked first, so a width of zero at a key that is already
13449        // there is about the key and not about the width.
13450        assert_eq!(
13451            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13452            "-CMS: key already exists\r\n"
13453        );
13454        assert_eq!(
13455            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13456            "-CMS: invalid width\r\n"
13457        );
13458        assert_eq!(
13459            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13460            "-CMS: invalid depth\r\n"
13461        );
13462        assert_eq!(
13463            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13464            "-CMS: invalid overestimation value\r\n"
13465        );
13466        assert_eq!(
13467            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13468            "-CMS: invalid prob value\r\n"
13469        );
13470        // A probability whose float conversion is zero has no depth, and a width
13471        // past a signed sixty four bit integer has no width, and both are the
13472        // same sentence.
13473        assert_eq!(
13474            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13475            "-CMS: invalid init arguments\r\n"
13476        );
13477        // And a sketch bigger than a gibibyte of counters is refused here where
13478        // the reference reserves address space nobody has touched, which is
13479        // D-47.
13480        assert_eq!(
13481            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13482            "-CMS: Insufficient memory to create the key\r\n"
13483        );
13484        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13485    }
13486
13487    /// Every pair is parsed before any of them lands, the counters saturate,
13488    /// and the count is a signed total of what was asked for.
13489    #[test]
13490    fn increments_are_parsed_whole_and_the_counters_saturate() {
13491        let mut f = Fixture::new();
13492        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13493        assert_eq!(
13494            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13495            "*2\r\n:3\r\n:4\r\n"
13496        );
13497        // An item that is incremented twice in one call sees its own first
13498        // increment in the reply to the second.
13499        assert_eq!(
13500            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13501            "*2\r\n:4\r\n:5\r\n"
13502        );
13503        // A bad number anywhere means nothing at all is applied.
13504        assert_eq!(
13505            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13506            "-CMS: Cannot parse number\r\n"
13507        );
13508        assert_eq!(
13509            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13510            "-CMS: Number cannot be negative\r\n"
13511        );
13512        assert_eq!(
13513            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13514            "*2\r\n:5\r\n:4\r\n"
13515        );
13516        // The counters stop at four billion and the item that stopped says so in
13517        // its own slot while the one beside it answers a number.
13518        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13519        assert_eq!(
13520            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13521            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13522        );
13523        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13524        // The count is what was asked for rather than what landed, and it is
13525        // signed, so a big enough total comes back negative.
13526        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13527        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13528        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13529        assert_eq!(
13530            f.run(&[b"CMS.INFO", b"w"]),
13531            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13532        );
13533        // An odd number of arguments after the key is an arity error and not a
13534        // syntax one.
13535        assert!(
13536            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13537                .contains("wrong number of arguments")
13538        );
13539        assert_eq!(
13540            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13541            "-CMS: key does not exist\r\n"
13542        );
13543        assert_eq!(
13544            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13545            "-CMS: key does not exist\r\n"
13546        );
13547    }
13548
13549    /// A merge overwrites its destination, and it is worked out in full before
13550    /// any of it is written.
13551    #[test]
13552    fn a_merge_lands_whole_or_not_at_all() {
13553        let mut f = Fixture::new();
13554        for name in [&b"m1"[..], b"m2", b"dst"] {
13555            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13556        }
13557        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13558        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13559        assert_eq!(
13560            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13561            "+OK\r\n"
13562        );
13563        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13564        // Overwritten and not added to, so the same merge twice is the same
13565        // answer twice.
13566        assert_eq!(
13567            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13568            "+OK\r\n"
13569        );
13570        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13571        assert_eq!(
13572            f.run(&[
13573                b"CMS.MERGE",
13574                b"dst",
13575                b"2",
13576                b"m1",
13577                b"m2",
13578                b"WEIGHTS",
13579                b"2",
13580                b"3"
13581            ]),
13582            "+OK\r\n"
13583        );
13584        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13585        // A cell times a weight is checked wide rather than wrapped, so this is
13586        // a refusal and the destination is left exactly as it was.
13587        assert_eq!(
13588            f.run(&[
13589                b"CMS.MERGE",
13590                b"dst",
13591                b"1",
13592                b"m1",
13593                b"WEIGHTS",
13594                b"4611686018427387904"
13595            ]),
13596            "-CMS: MERGE overflow\r\n"
13597        );
13598        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13599        // The destination comes first, then the count, then the layout, then the
13600        // weights, then the sources one at a time.
13601        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
13602        assert_eq!(
13603            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
13604            "-CMS: key does not exist\r\n"
13605        );
13606        assert_eq!(
13607            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
13608            "-CMS: Number of keys must be positive\r\n"
13609        );
13610        assert_eq!(
13611            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
13612            "-CMS: wrong number of keys\r\n"
13613        );
13614        assert_eq!(
13615            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
13616            "-CMS: wrong number of keys/weights\r\n"
13617        );
13618        assert_eq!(
13619            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
13620            "-CMS: width/depth is not equal\r\n"
13621        );
13622        assert_eq!(
13623            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
13624            "-CMS: key does not exist\r\n"
13625        );
13626    }
13627
13628    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
13629    /// a sketch is refused by the two commands that would have to serialise it.
13630    #[test]
13631    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13632        let mut f = Fixture::new();
13633        f.run(&[b"SET", b"s", b"text"]);
13634        for cmd in [
13635            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
13636            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
13637            vec![&b"CMS.QUERY"[..], b"s", b"a"],
13638            vec![&b"CMS.INFO"[..], b"s"],
13639            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
13640        ] {
13641            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13642            let reply = f.run(&cmd);
13643            // The two constructors see the key before anything else and say so
13644            // in the module's own words, and the rest are `WRONGTYPE`.
13645            assert!(
13646                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
13647                "{name}: {reply}"
13648            );
13649        }
13650        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
13651        // Redis refuses to copy a module key that has no copy callback, and
13652        // these are its words rather than ours. `DUMP` is the other half of
13653        // D-48: the reference has a payload for one of these and we do not.
13654        assert_eq!(
13655            f.run(&[b"COPY", b"c", b"c2"]),
13656            "-ERR not supported for this module key\r\n"
13657        );
13658        assert_eq!(
13659            f.run(&[b"DUMP", b"c"]),
13660            "-ERR DUMP is not supported for this module key\r\n"
13661        );
13662        // A graph is nobody's module and keeps its own sentence.
13663        f.run(&[b"G.NADD", b"g", b"a"]);
13664        assert_eq!(
13665            f.run(&[b"COPY", b"g", b"g2"]),
13666            "-ERR COPY is not supported for a graph\r\n"
13667        );
13668        assert_eq!(
13669            f.run(&[b"DUMP", b"g"]),
13670            "-ERR DUMP is not supported for a graph\r\n"
13671        );
13672        // Everything that does not need a byte shape works on a sketch key the
13673        // way it works on any other.
13674        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
13675        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
13676        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
13677        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
13678        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
13679    }
13680
13681    // ------------------------------------------------------------------ topk
13682
13683    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
13684    /// it looks at any of them.
13685    #[test]
13686    fn a_reserve_takes_three_arguments_or_six() {
13687        let mut f = Fixture::new();
13688        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
13689        assert_eq!(
13690            f.run(&[b"TOPK.INFO", b"t"]),
13691            "*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"
13692        );
13693        // Four arguments and five are an arity error rather than a defaulted
13694        // depth or decay.
13695        for cmd in [
13696            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
13697            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
13698        ] {
13699            assert!(f.run(&cmd).contains("wrong number of arguments"));
13700        }
13701        assert_eq!(
13702            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
13703            "+OK\r\n"
13704        );
13705        // The key is checked first, so a reserve with nothing else right at a
13706        // key that is taken still says the key is taken.
13707        assert_eq!(
13708            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
13709            "-TopK: key already exists\r\n"
13710        );
13711        assert_eq!(
13712            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
13713            "-TopK: invalid k\r\n"
13714        );
13715        assert_eq!(
13716            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
13717            "-TopK: invalid width\r\n"
13718        );
13719        assert_eq!(
13720            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
13721            "-TopK: invalid depth\r\n"
13722        );
13723        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
13724        assert_eq!(
13725            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
13726            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
13727        );
13728        assert_eq!(
13729            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
13730            "+OK\r\n"
13731        );
13732        // Past the cap, with the one sentence in the family that has a prefix.
13733        assert_eq!(
13734            f.run(&[
13735                b"TOPK.RESERVE",
13736                b"w",
13737                b"1",
13738                b"4294967295",
13739                b"4294967295",
13740                b"0.9"
13741            ]),
13742            "-ERR Insufficient memory to create topk data structure\r\n"
13743        );
13744    }
13745
13746    /// What the sketch keeps, and the three ways of asking about it.
13747    #[test]
13748    fn the_kept_set_is_what_query_and_list_answer_from() {
13749        let mut f = Fixture::new();
13750        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
13751        // A null an item while there is room, then the name of whatever was
13752        // pushed out.
13753        assert_eq!(
13754            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
13755            "*2\r\n$-1\r\n$-1\r\n"
13756        );
13757        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
13758        // Two slots are full and `c` arrives with a count of one, which is not
13759        // under the smallest kept count, so it takes that slot straight away.
13760        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
13761        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
13762        assert_eq!(
13763            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
13764            "*3\r\n:1\r\n:0\r\n:1\r\n"
13765        );
13766        // The table still counts what the kept set let go of.
13767        assert_eq!(
13768            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13769            "*3\r\n:11\r\n:1\r\n:6\r\n"
13770        );
13771        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
13772        assert_eq!(
13773            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
13774            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
13775        );
13776        // Any prefix of the keyword turns the counts on, the empty string
13777        // included, and only a longer word or a different one is refused.
13778        assert_eq!(
13779            f.run(&[b"TOPK.LIST", b"t", b"w"]),
13780            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13781        );
13782        assert_eq!(
13783            f.run(&[b"TOPK.LIST", b"t", b""]),
13784            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13785        );
13786        assert_eq!(
13787            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
13788            "-WITHCOUNT keyword expected\r\n"
13789        );
13790        // And the keyword is looked at before the key, so a missing key with a
13791        // bad keyword complains about the keyword.
13792        assert_eq!(
13793            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
13794            "-WITHCOUNT keyword expected\r\n"
13795        );
13796        assert_eq!(
13797            f.run(&[b"TOPK.LIST", b"missing"]),
13798            "-TopK: key does not exist\r\n"
13799        );
13800        // An item counted zero times is kept and not listed.
13801        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
13802        assert_eq!(
13803            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
13804            "*1\r\n$-1\r\n"
13805        );
13806        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
13807        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
13808    }
13809
13810    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
13811    /// before it counted, and the reply counts what it wrote.
13812    #[test]
13813    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
13814        let mut f = Fixture::new();
13815        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
13816        // Three pairs, the middle one bad: two elements come back, one of them
13817        // the error, and the array header says two rather than three. That last
13818        // part is D-51 and it is why a client here stays in step.
13819        assert_eq!(
13820            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
13821            format!(
13822                "*2\r\n$-1\r\n-{}\r\n",
13823                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
13824            )
13825        );
13826        assert_eq!(
13827            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13828            "*3\r\n:3\r\n:0\r\n:0\r\n"
13829        );
13830        // A hundred thousand is in and one more is out.
13831        assert_eq!(
13832            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
13833            "*1\r\n$-1\r\n"
13834        );
13835        assert!(
13836            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
13837                .contains("smaller or equal to 100,000")
13838        );
13839        // Pairs have to be pairs.
13840        assert!(
13841            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
13842                .contains("wrong number of arguments")
13843        );
13844        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
13845    }
13846
13847    /// The RESP3 shapes, which are the two the protocols disagree about.
13848    #[test]
13849    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
13850        let mut f = Fixture::new();
13851        f.run(&[b"HELLO", b"3"]);
13852        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
13853        f.run(&[b"TOPK.ADD", b"t", b"a"]);
13854        assert_eq!(
13855            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
13856            "*2\r\n#t\r\n#f\r\n"
13857        );
13858        // The count stays an integer on both protocols.
13859        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
13860        assert_eq!(
13861            f.run(&[b"TOPK.INFO", b"t"]),
13862            "%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"
13863        );
13864        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
13865    }
13866
13867    /// A top k key answers the module sentences the other sketch families
13868    /// answer, and its own word for its type.
13869    #[test]
13870    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13871        let mut f = Fixture::new();
13872        f.run(&[b"SET", b"s", b"text"]);
13873        for cmd in [
13874            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
13875            vec![&b"TOPK.ADD"[..], b"s", b"a"],
13876            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
13877            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
13878            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
13879            vec![&b"TOPK.LIST"[..], b"s"],
13880            vec![&b"TOPK.INFO"[..], b"s"],
13881        ] {
13882            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13883            let reply = f.run(&cmd);
13884            assert!(
13885                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
13886                "{name}: {reply}"
13887            );
13888        }
13889        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
13890        assert_eq!(
13891            f.run(&[b"COPY", b"t", b"t2"]),
13892            "-ERR not supported for this module key\r\n"
13893        );
13894        assert_eq!(
13895            f.run(&[b"DUMP", b"t"]),
13896            "-ERR DUMP is not supported for this module key\r\n"
13897        );
13898        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
13899        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
13900        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
13901        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
13902        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
13903        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
13904        // Every one of the six that is not the constructor says the same thing
13905        // about a key that is not there.
13906        assert_eq!(
13907            f.run(&[b"TOPK.INFO", b"t3"]),
13908            "-TopK: key does not exist\r\n"
13909        );
13910    }
13911
13912    // --------------------------------------------------------------- tdigest
13913
13914    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
13915    /// search rather than a lookup.
13916    #[test]
13917    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
13918        let mut f = Fixture::new();
13919        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
13920        // A hundred is the default and the capacity is six times it plus ten.
13921        assert_eq!(
13922            f.run(&[b"TDIGEST.INFO", b"t"]),
13923            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
13924             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
13925             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
13926        );
13927        assert_eq!(
13928            f.run(&[b"TDIGEST.CREATE", b"t"]),
13929            "-ERR T-Digest: key already exists\r\n"
13930        );
13931        // Three arguments is an arity error and not a missing keyword.
13932        assert!(
13933            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
13934                .contains("wrong number of arguments")
13935        );
13936        assert_eq!(
13937            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
13938            "+OK\r\n"
13939        );
13940        assert_eq!(
13941            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
13942            "+OK\r\n"
13943        );
13944        // The word is looked for across both trailing arguments and the number
13945        // is then read out of the last one whatever was found, so this looks for
13946        // a number inside the word `COMPRESSION` and does not find one.
13947        assert_eq!(
13948            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
13949            "-ERR T-Digest: error parsing compression parameter\r\n"
13950        );
13951        assert_eq!(
13952            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
13953            "-ERR T-Digest: wrong keyword\r\n"
13954        );
13955        assert_eq!(
13956            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
13957            "-ERR T-Digest: error parsing compression parameter\r\n"
13958        );
13959        assert_eq!(
13960            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
13961            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
13962        );
13963        // The reference's own ceiling, which is where the capacity stops fitting
13964        // in an int, and one past it.
13965        assert_eq!(
13966            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
13967            "-ERR T-Digest: allocation failed\r\n"
13968        );
13969        // And ours, which is a gibibyte of centroids and is D-52.
13970        assert_eq!(
13971            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
13972            "-ERR T-Digest: allocation failed\r\n"
13973        );
13974        // The key is checked before the arguments, so a bad compression at a key
13975        // that is already a digest still says the key is taken.
13976        assert_eq!(
13977            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
13978            "-ERR T-Digest: key already exists\r\n"
13979        );
13980    }
13981
13982    /// The four samples every note about this family is written against, and the
13983    /// answers a real 8.10.1 gives for them.
13984    #[test]
13985    fn the_quantile_family_answers_what_the_module_answers() {
13986        let mut f = Fixture::new();
13987        f.run(&[b"TDIGEST.CREATE", b"s"]);
13988        assert_eq!(
13989            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
13990            "+OK\r\n"
13991        );
13992        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
13993        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
13994        // The cdf of a sample is the weight below it plus half its own.
13995        assert_eq!(
13996            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
13997            "*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"
13998        );
13999        assert_eq!(
14000            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14001            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14002        );
14003        // Out of order, the walk restarts, and 0.5 answers 3 either way while
14004        // the two after it are read from the front again.
14005        assert_eq!(
14006            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14007            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14008        );
14009        assert_eq!(
14010            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14011            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14012        );
14013        assert_eq!(
14014            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14015            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14016        );
14017        assert_eq!(
14018            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14019            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14020        );
14021        assert_eq!(
14022            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14023            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14024        );
14025        assert_eq!(
14026            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14027            "$3\r\n2.5\r\n"
14028        );
14029        assert_eq!(
14030            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14031            "$3\r\n2.5\r\n"
14032        );
14033        // The ranges, which are separate sentences from the parse failures.
14034        assert_eq!(
14035            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14036            "-ERR T-Digest: quantile should be in [0,1]\r\n"
14037        );
14038        assert_eq!(
14039            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14040            "-ERR T-Digest: error parsing quantile\r\n"
14041        );
14042        assert_eq!(
14043            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14044            "-ERR T-Digest: error parsing cdf\r\n"
14045        );
14046        assert_eq!(
14047            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14048            "-ERR T-Digest: error parsing value\r\n"
14049        );
14050        assert_eq!(
14051            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14052            "-ERR T-Digest: rank needs to be non negative\r\n"
14053        );
14054        assert_eq!(
14055            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14056            "-ERR T-Digest: error parsing rank\r\n"
14057        );
14058        // Both cuts have their own parse sentence and share the range one, and
14059        // equal cuts are refused rather than answering nothing.
14060        assert_eq!(
14061            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14062            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14063        );
14064        assert_eq!(
14065            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14066            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14067        );
14068        assert_eq!(
14069            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14070            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14071        );
14072        assert_eq!(
14073            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14074            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14075        );
14076    }
14077
14078    /// An empty digest answers every question, and answers most of them with
14079    /// something that is not a number.
14080    #[test]
14081    fn an_empty_digest_has_an_answer_for_everything() {
14082        let mut f = Fixture::new();
14083        f.run(&[b"TDIGEST.CREATE", b"e"]);
14084        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14085        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14086        assert_eq!(
14087            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14088            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14089        );
14090        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14091        assert_eq!(
14092            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14093            "$3\r\nnan\r\n"
14094        );
14095        // Minus two, which is a number no rank on a digest with samples in it
14096        // can ever be.
14097        assert_eq!(
14098            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14099            "*2\r\n:-2\r\n:-2\r\n"
14100        );
14101        assert_eq!(
14102            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14103            "*2\r\n:-2\r\n:-2\r\n"
14104        );
14105        assert_eq!(
14106            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14107            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14108        );
14109        // A reset puts a digest with samples back into exactly this state.
14110        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14111        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14112        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14113        // Down to the compression count, so a reset digest and a fresh one of
14114        // the same compression report the same nine numbers.
14115        f.run(&[b"TDIGEST.CREATE", b"e2"]);
14116        assert_eq!(
14117            f.run(&[b"TDIGEST.INFO", b"e"]),
14118            f.run(&[b"TDIGEST.INFO", b"e2"])
14119        );
14120    }
14121
14122    /// The double parser is Redis's and not this engine's, and the two disagree
14123    /// at both ends of the range.
14124    #[test]
14125    fn a_sample_is_read_the_way_redis_reads_a_double() {
14126        let mut f = Fixture::new();
14127        f.run(&[b"TDIGEST.CREATE", b"a"]);
14128        // Overflow and underflow are parse failures rather than an infinity and
14129        // a zero, which is where this parts company with the rest of the engine.
14130        for bad in [
14131            &b"nan"[..],
14132            b"1e400",
14133            b"-1e400",
14134            b"1e309",
14135            b"1e-400",
14136            b"",
14137            b" 1",
14138            b"1 ",
14139            b"1e",
14140            b"--1",
14141        ] {
14142            assert_eq!(
14143                f.run(&[b"TDIGEST.ADD", b"a", bad]),
14144                "-ERR T-Digest: error parsing val parameter\r\n",
14145                "{}",
14146                String::from_utf8_lossy(bad)
14147            );
14148        }
14149        // An infinity spelled out parses and is then refused for being one, with
14150        // a different sentence.
14151        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14152            assert_eq!(
14153                f.run(&[b"TDIGEST.ADD", b"a", word]),
14154                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14155                "{}",
14156                String::from_utf8_lossy(word)
14157            );
14158        }
14159        // These all parse: hex, a bare point either side, and the smallest
14160        // subnormal the reference will take.
14161        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14162            assert_eq!(
14163                f.run(&[b"TDIGEST.ADD", b"a", good]),
14164                "+OK\r\n",
14165                "{}",
14166                String::from_utf8_lossy(good)
14167            );
14168        }
14169        // Nothing landed from the failures, so six samples is what there is.
14170        assert!(
14171            f.run(&[b"TDIGEST.INFO", b"a"])
14172                .contains("Observations\r\n:6\r\n")
14173        );
14174        // Every value is parsed before any is added, so this whole command is a
14175        // no op.
14176        assert_eq!(
14177            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14178            "-ERR T-Digest: error parsing val parameter\r\n"
14179        );
14180        assert!(
14181            f.run(&[b"TDIGEST.INFO", b"a"])
14182                .contains("Observations\r\n:6\r\n")
14183        );
14184    }
14185
14186    /// What a merge does to its destination, to its inputs and to the buffer
14187    /// split `TDIGEST.INFO` reports.
14188    #[test]
14189    fn a_merge_sweeps_the_destination_between_its_inputs() {
14190        let mut f = Fixture::new();
14191        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14192        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14193        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14194        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14195        assert_eq!(
14196            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14197            "+OK\r\n"
14198        );
14199        // The destination did not exist, so the compression is the largest of
14200        // the inputs. The three from the first input were swept in before the
14201        // three from the second arrived, which is the one visible effect of the
14202        // reference folding one input at a time.
14203        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14204        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14205        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14206        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14207        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14208        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14209        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14210        // Reading a source sweeps it too, so a merge writes to keys it only
14211        // reads from.
14212        assert!(
14213            f.run(&[b"TDIGEST.INFO", b"m1"])
14214                .contains("Merged nodes\r\n:3\r\n")
14215        );
14216        // Without OVERRIDE the destination joins its own inputs, so this takes
14217        // it to nine observations and keeps its own compression.
14218        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14219        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14220        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14221        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14222        // With OVERRIDE the old destination is dropped and the compression goes
14223        // back to the largest of the inputs.
14224        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14225        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14226        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14227        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14228        // And COMPRESSION beats both.
14229        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14230        assert!(
14231            f.run(&[b"TDIGEST.INFO", b"d"])
14232                .contains("Compression\r\n:500\r\n")
14233        );
14234        // Naming the destination as a source folds it in twice.
14235        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14236        assert!(
14237            f.run(&[b"TDIGEST.INFO", b"d"])
14238                .contains("Observations\r\n:12\r\n")
14239        );
14240        // The arguments, in the order the reference checks them.
14241        assert_eq!(
14242            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14243            "-ERR T-Digest: error parsing numkeys\r\n"
14244        );
14245        assert_eq!(
14246            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14247            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14248        );
14249        assert!(
14250            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14251                .contains("wrong number of arguments")
14252        );
14253        assert!(
14254            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14255                .contains("wrong number of arguments")
14256        );
14257        assert_eq!(
14258            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14259            "-ERR T-Digest: wrong keyword\r\n"
14260        );
14261        // A source that is not there stops the whole thing, and the destination
14262        // is left as it was.
14263        assert_eq!(
14264            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14265            "-ERR T-Digest: key does not exist\r\n"
14266        );
14267        assert!(
14268            f.run(&[b"TDIGEST.INFO", b"d"])
14269                .contains("Observations\r\n:12\r\n")
14270        );
14271        // A destination that is not there and is also named as a source is the
14272        // same sentence rather than an empty merge.
14273        assert_eq!(
14274            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14275            "-ERR T-Digest: key does not exist\r\n"
14276        );
14277    }
14278
14279    /// The RESP3 shapes, which are the two the protocols disagree about.
14280    #[test]
14281    fn a_digest_answers_doubles_and_a_map_on_resp3() {
14282        let mut f = Fixture::new();
14283        f.run(&[b"HELLO", b"3"]);
14284        f.run(&[b"TDIGEST.CREATE", b"s"]);
14285        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14286        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14287        assert_eq!(
14288            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14289            "*2\r\n,1\r\n,4\r\n"
14290        );
14291        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14292        // The two infinities and the NaN go out as the bare words.
14293        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14294        assert_eq!(
14295            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14296            "*1\r\n,-inf\r\n"
14297        );
14298        f.run(&[b"TDIGEST.CREATE", b"e"]);
14299        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14300        // The ranks stay integers on both protocols.
14301        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14302        // Every question above swept the buffer in, so the four samples are all
14303        // merged by now and the compression count says it happened once.
14304        assert_eq!(
14305            f.run(&[b"TDIGEST.INFO", b"s"]),
14306            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14307             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14308             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14309        );
14310    }
14311
14312    /// A t digest key answers the module sentences the other sketch families
14313    /// answer, and its own word for its type.
14314    #[test]
14315    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14316        let mut f = Fixture::new();
14317        f.run(&[b"SET", b"s", b"text"]);
14318        for cmd in [
14319            vec![&b"TDIGEST.CREATE"[..], b"s"],
14320            vec![&b"TDIGEST.RESET"[..], b"s"],
14321            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14322            vec![&b"TDIGEST.MIN"[..], b"s"],
14323            vec![&b"TDIGEST.MAX"[..], b"s"],
14324            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14325            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14326            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14327            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14328            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14329            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14330            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14331            vec![&b"TDIGEST.INFO"[..], b"s"],
14332        ] {
14333            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14334            let reply = f.run(&cmd);
14335            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14336        }
14337        // The merge checks its destination the same way, and its sources too.
14338        f.run(&[b"TDIGEST.CREATE", b"t"]);
14339        assert!(
14340            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14341                .starts_with("-WRONGTYPE")
14342        );
14343        assert!(
14344            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14345                .starts_with("-WRONGTYPE")
14346        );
14347        assert_eq!(
14348            f.run(&[b"COPY", b"t", b"t2"]),
14349            "-ERR not supported for this module key\r\n"
14350        );
14351        assert_eq!(
14352            f.run(&[b"DUMP", b"t"]),
14353            "-ERR DUMP is not supported for this module key\r\n"
14354        );
14355        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14356        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14357        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14358        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14359        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14360        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14361        // An empty digest is still a key, so the twelve that are not the
14362        // constructor all say the same thing once it is gone.
14363        assert_eq!(
14364            f.run(&[b"TDIGEST.INFO", b"t3"]),
14365            "-ERR T-Digest: key does not exist\r\n"
14366        );
14367        // The key is looked at before the arguments, so a bad argument at a key
14368        // that is not there still says the key is not there.
14369        assert_eq!(
14370            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14371            "-ERR T-Digest: key does not exist\r\n"
14372        );
14373    }
14374
14375    // -------------------------------------------------------------------- ts
14376
14377    /// A `TS.INFO` reply with the memory usage taken out of it.
14378    ///
14379    /// That number is what a series costs here rather than what one costs in the
14380    /// module, which is D-53, and it moves whenever the layout of a chunk does.
14381    /// Everything either side of it is the wire contract and is worth pinning
14382    /// down exactly, so the tests below check the whole reply with the one
14383    /// number lifted out.
14384    fn without_memory(reply: &str) -> String {
14385        let head = "+memoryUsage\r\n:";
14386        let at = reply.find(head).expect("every TS.INFO reports memory");
14387        let rest = &reply[at + head.len()..];
14388        let end = rest.find("\r\n").expect("and it is a whole number");
14389        format!("{}{}", &reply[..at + head.len()], &rest[end..])
14390    }
14391
14392    /// A series is made empty and still says it has a chunk, and the options are
14393    /// read before the key is looked at.
14394    #[test]
14395    fn a_series_is_made_empty_and_reports_on_itself() {
14396        let mut f = Fixture::new();
14397        assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14398        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14399        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14400        // Fourteen fields, so twenty eight elements. An empty series reports one
14401        // chunk and zero at both ends, and neither the chunk type nor the
14402        // duplicate policy is ever a nil.
14403        assert_eq!(
14404            without_memory(&f.run(&[b"TS.INFO", b"t"])),
14405            "*28\r\n\
14406             +totalSamples\r\n:0\r\n\
14407             +memoryUsage\r\n:\r\n\
14408             +firstTimestamp\r\n:0\r\n\
14409             +lastTimestamp\r\n:0\r\n\
14410             +retentionTime\r\n:0\r\n\
14411             +chunkCount\r\n:1\r\n\
14412             +chunkSize\r\n:4096\r\n\
14413             +chunkType\r\n+compressed\r\n\
14414             +duplicatePolicy\r\n+block\r\n\
14415             +labels\r\n*0\r\n\
14416             +sourceKey\r\n$-1\r\n\
14417             +rules\r\n*0\r\n\
14418             +ignoreMaxTimeDiff\r\n:0\r\n\
14419             +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14420        );
14421        // A key that is already there is about the key whatever it holds, and
14422        // the existence is what is checked rather than the type.
14423        assert_eq!(
14424            f.run(&[b"TS.CREATE", b"t"]),
14425            "-ERR TSDB: key already exists\r\n"
14426        );
14427        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14428        assert_eq!(
14429            f.run(&[b"TS.CREATE", b"str"]),
14430            "-ERR TSDB: key already exists\r\n"
14431        );
14432        // But the arguments are read first, so a bad one at a key that is there
14433        // answers about the argument.
14434        assert_eq!(
14435            f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14436            "-ERR TSDB: Couldn't parse RETENTION\r\n"
14437        );
14438        // The seven that will not make a series say WRONGTYPE about a key
14439        // holding something else, where the two that would say a sentence.
14440        // The word is inside the sentence and not in front of it, because the
14441        // module writes its own error text and Redis puts ERR on the front of
14442        // anything a module writes.
14443        let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14444        assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14445        assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14446        assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14447        assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14448        assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14449        assert_eq!(
14450            f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14451            "-ERR TSDB: the key is not a TSDB key\r\n"
14452        );
14453        // And the ones that will not make one say so about a key that is gone.
14454        assert_eq!(
14455            f.run(&[b"TS.INFO", b"nope"]),
14456            "-ERR TSDB: the key does not exist\r\n"
14457        );
14458        assert_eq!(
14459            f.run(&[b"TS.GET", b"nope"]),
14460            "-ERR TSDB: the key does not exist\r\n"
14461        );
14462        assert_eq!(
14463            f.run(&[b"TS.ALTER", b"nope"]),
14464            "-ERR TSDB: the key does not exist\r\n"
14465        );
14466        assert_eq!(
14467            f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14468            "-ERR TSDB: the key does not exist\r\n"
14469        );
14470    }
14471
14472    /// Every option word, including the ones that are wrong, and the scan that
14473    /// finds them.
14474    #[test]
14475    fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14476        let mut f = Fixture::new();
14477        assert_eq!(
14478            f.run(&[
14479                b"TS.CREATE",
14480                b"t",
14481                b"RETENTION",
14482                b"5000",
14483                b"ENCODING",
14484                b"UNCOMPRESSED",
14485                b"CHUNK_SIZE",
14486                b"128",
14487                b"DUPLICATE_POLICY",
14488                b"LAST",
14489                b"IGNORE",
14490                b"10",
14491                b"0.5",
14492                b"LABELS",
14493                b"room",
14494                b"kitchen"
14495            ]),
14496            "+OK\r\n"
14497        );
14498        let info = f.run(&[b"TS.INFO", b"t"]);
14499        assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14500        assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14501        assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14502        assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14503        assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14504        // A plain double here, where a sample value out of TS.GET is the
14505        // shortest digits that read back as the same number.
14506        assert!(
14507            info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14508            "{info}"
14509        );
14510        assert!(
14511            info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
14512            "{info}"
14513        );
14514
14515        // A word that is not an option is read past rather than refused.
14516        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14517        // LABELS eats everything after it in pairs, and the later scans still
14518        // look inside what it ate, so this sets a retention and stores a label
14519        // called RETENTION at the same time.
14520        assert_eq!(
14521            f.run(&[
14522                b"TS.CREATE",
14523                b"g",
14524                b"LABELS",
14525                b"a",
14526                b"b",
14527                b"RETENTION",
14528                b"5"
14529            ]),
14530            "+OK\r\n"
14531        );
14532        let greedy = f.run(&[b"TS.INFO", b"g"]);
14533        assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
14534        assert!(
14535            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"),
14536            "{greedy}"
14537        );
14538
14539        // Every way an option can be wrong, in the order the module reads them.
14540        assert_eq!(
14541            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
14542            "-ERR TSDB: Couldn't parse LABELS\r\n"
14543        );
14544        assert_eq!(
14545            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
14546            "-ERR TSDB: Couldn't parse LABELS\r\n"
14547        );
14548        assert_eq!(
14549            f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
14550            "-ERR TSDB: Couldn't parse RETENTION\r\n"
14551        );
14552        // A retention below zero is one of the two the module writes with no
14553        // ERR in front of it, where one that is not a number gets one.
14554        assert_eq!(
14555            f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
14556            "-TSDB: Couldn't parse RETENTION\r\n"
14557        );
14558        assert_eq!(
14559            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
14560            "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
14561        );
14562        assert_eq!(
14563            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
14564            "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
14565        );
14566        assert_eq!(
14567            f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
14568            "-ERR TSDB: unknown ENCODING parameter\r\n"
14569        );
14570        // And an ENCODING with nothing behind it is an arity error where every
14571        // other keyword in the same spot is a sentence.
14572        assert!(
14573            f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
14574                .contains("wrong number of arguments for 'ts.create' command")
14575        );
14576        assert_eq!(
14577            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
14578            "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
14579        );
14580        assert_eq!(
14581            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
14582            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14583        );
14584        assert_eq!(
14585            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
14586            "-ERR TSDB: Couldn't parse IGNORE\r\n"
14587        );
14588        assert_eq!(
14589            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
14590            "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
14591        );
14592        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
14593
14594        // An alter changes what was named and leaves the rest alone, and reads
14595        // an encoding only far enough to refuse a bad one.
14596        assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
14597        let after = f.run(&[b"TS.INFO", b"t"]);
14598        assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
14599        assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
14600        assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
14601        assert_eq!(
14602            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
14603            "-ERR TSDB: unknown ENCODING parameter\r\n"
14604        );
14605        // An encoding it does take is still not applied.
14606        assert_eq!(
14607            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
14608            "+OK\r\n"
14609        );
14610        assert!(
14611            f.run(&[b"TS.INFO", b"t"])
14612                .contains("+chunkType\r\n+uncompressed\r\n")
14613        );
14614    }
14615
14616    /// Samples go in, come back out and are refused for the reasons the module
14617    /// refuses them.
14618    #[test]
14619    fn samples_land_where_they_are_put_and_the_newest_comes_back() {
14620        let mut f = Fixture::new();
14621        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
14622        // The series was made on the way in.
14623        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14624        assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
14625        // A sample value goes out as a simple string of the shortest digits
14626        // that read back as the same number.
14627        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
14628        assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
14629        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
14630        // An empty series has no newest sample and answers an empty array
14631        // rather than a nil.
14632        assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
14633        assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
14634
14635        // The value is read before the key, so a bad one against a key holding
14636        // a string is about the value.
14637        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14638        assert_eq!(
14639            f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
14640            "-ERR TSDB: invalid value\r\n"
14641        );
14642        // The grammar is tighter than the one a number argument usually gets:
14643        // no leading plus, no bare fraction, no infinity and nothing that does
14644        // not fit.
14645        for bad in [
14646            &b".5"[..],
14647            b"1.",
14648            b"+1",
14649            b" 1",
14650            b"0x10",
14651            b"inf",
14652            b"1e400",
14653            b"--1",
14654            b"1e",
14655        ] {
14656            assert_eq!(
14657                f.run(&[b"TS.ADD", b"v", b"1", bad]),
14658                "-ERR TSDB: invalid value\r\n",
14659                "{}",
14660                String::from_utf8_lossy(bad)
14661            );
14662        }
14663        // And a reading that is not a number is one of three words.
14664        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
14665
14666        // A timestamp that is not a number, and one that is and is below zero,
14667        // are two different sentences.
14668        assert_eq!(
14669            f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
14670            "-ERR TSDB: invalid timestamp\r\n"
14671        );
14672        assert_eq!(
14673            f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
14674            "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
14675        );
14676
14677        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
14678        // command beats what the series was told.
14679        assert_eq!(
14680            f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
14681            "-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"
14682        );
14683        assert_eq!(
14684            f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
14685            ":300\r\n"
14686        );
14687        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
14688        // ON_DUPLICATE is only read when the key was already there, which is
14689        // why a policy word that is not a policy passes on a fresh key.
14690        assert_eq!(
14691            f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
14692            ":1\r\n"
14693        );
14694        assert_eq!(
14695            f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
14696            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14697        );
14698
14699        // Retention is exact and it is checked before anything else happens, so
14700        // a sample landing behind the window is refused rather than trimmed.
14701        assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
14702        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
14703        assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
14704        assert_eq!(
14705            f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
14706            "-ERR TSDB: Timestamp is older than retention\r\n"
14707        );
14708        // And the window trims as it moves.
14709        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
14710        assert!(
14711            f.run(&[b"TS.INFO", b"r"])
14712                .contains("+totalSamples\r\n:1\r\n")
14713        );
14714
14715        // An ignore window drops a sample close enough to the newest one to be
14716        // uninteresting, and answers the newest timestamp so a client can tell.
14717        assert_eq!(
14718            f.run(&[
14719                b"TS.CREATE",
14720                b"i",
14721                b"DUPLICATE_POLICY",
14722                b"LAST",
14723                b"IGNORE",
14724                b"10",
14725                b"0.5"
14726            ]),
14727            "+OK\r\n"
14728        );
14729        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
14730        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
14731        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
14732    }
14733
14734    /// Every triple in a `TS.MADD` is answered on its own, and none of them
14735    /// makes a series.
14736    #[test]
14737    fn a_madd_answers_each_triple_and_creates_nothing() {
14738        let mut f = Fixture::new();
14739        assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
14740        assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
14741        assert_eq!(
14742            f.run(&[
14743                b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
14744            ]),
14745            "*3\r\n:100\r\n:100\r\n:200\r\n"
14746        );
14747        // A key that is not a series is an error in its own slot and the ones
14748        // after it still land.
14749        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14750        assert_eq!(
14751            f.run(&[
14752                b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
14753            ]),
14754            "*3\r\n\
14755             -ERR TSDB: the key is not a TSDB key\r\n\
14756             -ERR TSDB: the key is not a TSDB key\r\n\
14757             :300\r\n"
14758        );
14759        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
14760        // A bad value and a bad timestamp are answered in their slots too.
14761        assert_eq!(
14762            f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
14763            "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
14764        );
14765        // And a list that is not made of triples is an arity error.
14766        assert!(
14767            f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
14768                .contains("wrong number of arguments for 'ts.madd' command")
14769        );
14770    }
14771
14772    /// The two increments, which only ever write forwards.
14773    #[test]
14774    fn an_increment_walks_the_newest_value_up_and_down() {
14775        let mut f = Fixture::new();
14776        assert_eq!(
14777            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14778            ":100\r\n"
14779        );
14780        assert_eq!(
14781            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14782            ":100\r\n"
14783        );
14784        // Two on one timestamp add up rather than collide, because the sample
14785        // goes in under the last policy whatever the series says.
14786        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
14787        assert_eq!(
14788            f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
14789            ":200\r\n"
14790        );
14791        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
14792        // A timestamp behind the newest sample is the other of the two errors
14793        // the module writes with no ERR in front of it.
14794        assert_eq!(
14795            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
14796            "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
14797        );
14798        // The increment goes through the ordinary number reader, so it takes
14799        // what a sample value will not and refuses a NaN that a sample value
14800        // takes.
14801        assert_eq!(
14802            f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
14803            ":1\r\n"
14804        );
14805        assert_eq!(
14806            f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
14807            ":1\r\n"
14808        );
14809        assert_eq!(
14810            f.run(&[b"TS.INCRBY", b"t", b"nan"]),
14811            "-ERR TSDB: invalid increase/decrease value\r\n"
14812        );
14813        assert_eq!(
14814            f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
14815            "-ERR TSDB: invalid increase/decrease value\r\n"
14816        );
14817        // A key holding something else is WRONGTYPE and is answered before the
14818        // number is looked at.
14819        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14820        assert_eq!(
14821            f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
14822            "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
14823        );
14824        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
14825        // The reference reads one past the end of its own arguments here and
14826        // answers whatever was in that memory, so there is nothing to copy and
14827        // this answers the same thing every time.
14828        assert_eq!(
14829            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
14830            "-ERR TSDB: invalid timestamp\r\n"
14831        );
14832        // And one behind a LABELS is a label name rather than the keyword, so
14833        // this lands at the clock rather than at 5.
14834        assert_eq!(
14835            f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
14836            format!(":{}\r\n", f.server.now_ms())
14837        );
14838        // Adding to a series whose newest value is not a number has no answer.
14839        assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
14840        assert_eq!(
14841            f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
14842            "-ERR TSDB: cannot increment/decrement NaN value\r\n"
14843        );
14844    }
14845
14846    /// Deleting a span, both ends included.
14847    #[test]
14848    fn deleting_takes_out_a_span_and_answers_how_many_went() {
14849        let mut f = Fixture::new();
14850        for at in [b"100".as_slice(), b"200", b"300", b"400"] {
14851            f.run(&[b"TS.ADD", b"t", at, b"1"]);
14852        }
14853        assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
14854        assert!(
14855            f.run(&[b"TS.INFO", b"t"])
14856                .contains("+totalSamples\r\n:2\r\n")
14857        );
14858        // Ends the wrong way round take nothing out rather than being an error.
14859        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
14860        // The two open ends.
14861        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
14862        // A series everything has been deleted from keeps its chunk and reports
14863        // zero at both ends again.
14864        let empty = f.run(&[b"TS.INFO", b"t"]);
14865        assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
14866        assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
14867        assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
14868        assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
14869        assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
14870        // The two ends have their own sentences.
14871        assert_eq!(
14872            f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
14873            "-ERR TSDB: wrong fromTimestamp\r\n"
14874        );
14875        assert_eq!(
14876            f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
14877            "-ERR TSDB: wrong toTimestamp\r\n"
14878        );
14879        assert_eq!(
14880            f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
14881            "-ERR TSDB: wrong fromTimestamp\r\n"
14882        );
14883    }
14884
14885    /// What RESP3 changes, which is the two places a number is written and the
14886    /// shape of `TS.INFO`.
14887    #[test]
14888    fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
14889        let mut f = Fixture::new();
14890        f.out = Out::new(Proto::Resp3);
14891        assert_eq!(
14892            f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
14893            "+OK\r\n"
14894        );
14895        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
14896        // A double rather than the simple string RESP2 gets.
14897        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
14898        assert_eq!(
14899            without_memory(&f.run(&[b"TS.INFO", b"t"])),
14900            "%14\r\n\
14901             +totalSamples\r\n:1\r\n\
14902             +memoryUsage\r\n:\r\n\
14903             +firstTimestamp\r\n:100\r\n\
14904             +lastTimestamp\r\n:100\r\n\
14905             +retentionTime\r\n:0\r\n\
14906             +chunkCount\r\n:1\r\n\
14907             +chunkSize\r\n:4096\r\n\
14908             +chunkType\r\n+compressed\r\n\
14909             +duplicatePolicy\r\n+block\r\n\
14910             +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
14911             +sourceKey\r\n_\r\n\
14912             +rules\r\n%0\r\n\
14913             +ignoreMaxTimeDiff\r\n:0\r\n\
14914             +ignoreMaxValDiff\r\n,0\r\n"
14915        );
14916    }
14917
14918    /// Reading a span back, both ways round, with the two ends and the three
14919    /// things that trim what comes out.
14920    #[test]
14921    fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
14922        let mut f = Fixture::new();
14923        for (at, v) in [
14924            (b"100".as_slice(), b"1".as_slice()),
14925            (b"200", b"2"),
14926            (b"300", b"3"),
14927            (b"400", b"4"),
14928        ] {
14929            f.run(&[b"TS.ADD", b"t", at, v]);
14930        }
14931        assert_eq!(
14932            f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
14933            "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
14934             *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
14935        );
14936        // Both ends are included.
14937        assert_eq!(
14938            f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
14939            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14940        );
14941        // Backwards, and the count takes from the front of what comes out, so
14942        // backwards it takes the newest.
14943        assert_eq!(
14944            f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
14945            "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
14946        );
14947        // Ends the wrong way round are empty rather than an error.
14948        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
14949        // The two filters.
14950        assert_eq!(
14951            f.run(&[
14952                b"TS.RANGE",
14953                b"t",
14954                b"-",
14955                b"+",
14956                b"FILTER_BY_VALUE",
14957                b"2",
14958                b"3"
14959            ]),
14960            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14961        );
14962        assert_eq!(
14963            f.run(&[
14964                b"TS.RANGE",
14965                b"t",
14966                b"-",
14967                b"+",
14968                b"FILTER_BY_TS",
14969                b"100",
14970                b"400"
14971            ]),
14972            "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
14973        );
14974        // A word that is not an option is ignored wherever it sits.
14975        assert_eq!(
14976            f.run(&[
14977                b"TS.RANGE",
14978                b"t",
14979                b"-",
14980                b"+",
14981                b"ZZZ",
14982                b"FILTER_BY_TS",
14983                b"400"
14984            ]),
14985            "*1\r\n*2\r\n:400\r\n+4\r\n"
14986        );
14987        // `LATEST` means nothing until there is a compaction rule to follow.
14988        assert_eq!(
14989            f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
14990            "*1\r\n*2\r\n:100\r\n+1\r\n"
14991        );
14992    }
14993
14994    /// The bucketing, which is one column a reduction and a flat row.
14995    #[test]
14996    fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
14997        let mut f = Fixture::new();
14998        for (at, v) in [
14999            (b"100".as_slice(), b"1".as_slice()),
15000            (b"200", b"2"),
15001            (b"300", b"3"),
15002            (b"400", b"4"),
15003        ] {
15004            f.run(&[b"TS.ADD", b"t", at, v]);
15005        }
15006        assert_eq!(
15007            f.run(&[
15008                b"TS.RANGE",
15009                b"t",
15010                b"-",
15011                b"+",
15012                b"AGGREGATION",
15013                b"avg",
15014                b"200"
15015            ]),
15016            "*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"
15017        );
15018        // Three reductions is a row of four and not a row of two with a nested
15019        // three in it.
15020        assert_eq!(
15021            f.run(&[
15022                b"TS.RANGE",
15023                b"t",
15024                b"-",
15025                b"+",
15026                b"AGGREGATION",
15027                b"min,max,count",
15028                b"200"
15029            ]),
15030            "*3\r\n\
15031             *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15032             *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15033             *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15034        );
15035        // The timestamp a bucket is reported under.
15036        assert_eq!(
15037            f.run(&[
15038                b"TS.RANGE",
15039                b"t",
15040                b"-",
15041                b"+",
15042                b"AGGREGATION",
15043                b"avg",
15044                b"200",
15045                b"BUCKETTIMESTAMP",
15046                b"+"
15047            ]),
15048            "*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"
15049        );
15050        // An alignment moves where the bucket edges land.
15051        assert_eq!(
15052            f.run(&[
15053                b"TS.RANGE",
15054                b"t",
15055                b"100",
15056                b"400",
15057                b"ALIGN",
15058                b"100",
15059                b"AGGREGATION",
15060                b"sum",
15061                b"200"
15062            ]),
15063            "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15064        );
15065        // A `COUNT` sitting where the reduction name belongs is that name, and
15066        // the scan for a real one starts again two words later.
15067        assert_eq!(
15068            f.run(&[
15069                b"TS.RANGE",
15070                b"t",
15071                b"-",
15072                b"+",
15073                b"AGGREGATION",
15074                b"count",
15075                b"200"
15076            ]),
15077            "*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"
15078        );
15079        assert_eq!(
15080            f.run(&[
15081                b"TS.RANGE",
15082                b"t",
15083                b"-",
15084                b"+",
15085                b"AGGREGATION",
15086                b"count",
15087                b"200",
15088                b"COUNT",
15089                b"1"
15090            ]),
15091            "*1\r\n*2\r\n:0\r\n+1\r\n"
15092        );
15093    }
15094
15095    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
15096    /// carries two different things depending on which kind of empty it is.
15097    #[test]
15098    fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15099        let mut f = Fixture::new();
15100        for (at, v) in [
15101            (b"0".as_slice(), b"1".as_slice()),
15102            (b"100", b"2"),
15103            (b"500", b"nan"),
15104            (b"600", b"3"),
15105        ] {
15106            f.run(&[b"TS.ADD", b"g", at, v]);
15107        }
15108        // Without `EMPTY` the buckets with nothing in them are not there at all,
15109        // and neither is the one holding only a reading that is not a number.
15110        assert_eq!(
15111            f.run(&[
15112                b"TS.RANGE",
15113                b"g",
15114                b"-",
15115                b"+",
15116                b"AGGREGATION",
15117                b"avg",
15118                b"100"
15119            ]),
15120            "*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"
15121        );
15122        // The sum of nothing is zero rather than not a number.
15123        assert_eq!(
15124            f.run(&[
15125                b"TS.RANGE",
15126                b"g",
15127                b"-",
15128                b"+",
15129                b"AGGREGATION",
15130                b"sum",
15131                b"100",
15132                b"EMPTY"
15133            ]),
15134            "*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\
15135             *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\
15136             *2\r\n:600\r\n+3\r\n"
15137        );
15138        // Buckets 200 through 400 have no readings at all and carry the reading
15139        // before the gap either way round. Bucket 500 has a reading that is not
15140        // a number, so it carries whatever the bucket before it in the reading
15141        // direction answered, which is 2 forwards and 3 backwards.
15142        assert_eq!(
15143            f.run(&[
15144                b"TS.RANGE",
15145                b"g",
15146                b"-",
15147                b"+",
15148                b"AGGREGATION",
15149                b"last",
15150                b"100",
15151                b"EMPTY"
15152            ]),
15153            "*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\
15154             *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\
15155             *2\r\n:600\r\n+3\r\n"
15156        );
15157        assert_eq!(
15158            f.run(&[
15159                b"TS.REVRANGE",
15160                b"g",
15161                b"-",
15162                b"+",
15163                b"AGGREGATION",
15164                b"last",
15165                b"100",
15166                b"EMPTY"
15167            ]),
15168            "*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\
15169             *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\
15170             *2\r\n:0\r\n+1\r\n"
15171        );
15172        // And a window that opens on that bucket has nothing in range before it
15173        // to carry, so it answers not a number.
15174        assert_eq!(
15175            f.run(&[
15176                b"TS.RANGE",
15177                b"g",
15178                b"500",
15179                b"600",
15180                b"AGGREGATION",
15181                b"last",
15182                b"100",
15183                b"EMPTY"
15184            ]),
15185            "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15186        );
15187    }
15188
15189    /// The sentences a read answers when its options do not add up, which are
15190    /// the module's own word for word.
15191    #[test]
15192    fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15193        let mut f = Fixture::new();
15194        f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15195        f.run(&[b"SET", b"str", b"x"]);
15196        let cases: &[(&[&[u8]], &str)] = &[
15197            (
15198                &[b"TS.RANGE", b"t"],
15199                "-ERR wrong number of arguments for 'ts.range' command\r\n",
15200            ),
15201            // The key is resolved before a single option is read.
15202            (
15203                &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15204                "-ERR TSDB: the key does not exist\r\n",
15205            ),
15206            (
15207                &[b"TS.RANGE", b"str", b"-", b"+"],
15208                "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15209            ),
15210            (
15211                &[b"TS.RANGE", b"t", b"abc", b"+"],
15212                "-ERR TSDB: wrong fromTimestamp\r\n",
15213            ),
15214            (
15215                &[b"TS.RANGE", b"t", b"-", b"abc"],
15216                "-ERR TSDB: wrong toTimestamp\r\n",
15217            ),
15218            (
15219                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15220                "-ERR TSDB: COUNT argument is missing\r\n",
15221            ),
15222            (
15223                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15224                "-ERR TSDB: Couldn't parse COUNT\r\n",
15225            ),
15226            (
15227                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15228                "-ERR TSDB: Invalid COUNT value\r\n",
15229            ),
15230            (
15231                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15232                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15233            ),
15234            (
15235                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15236                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15237            ),
15238            (
15239                &[
15240                    b"TS.RANGE",
15241                    b"t",
15242                    b"-",
15243                    b"+",
15244                    b"AGGREGATION",
15245                    b"nope",
15246                    b"100",
15247                ],
15248                "-ERR TSDB: Unknown aggregation type\r\n",
15249            ),
15250            (
15251                &[
15252                    b"TS.RANGE",
15253                    b"t",
15254                    b"-",
15255                    b"+",
15256                    b"AGGREGATION",
15257                    b"avg,,min",
15258                    b"100",
15259                ],
15260                "-ERR TSDB: Empty aggregation type in list\r\n",
15261            ),
15262            // The list of names is read before the width is looked at.
15263            (
15264                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15265                "-ERR TSDB: Unknown aggregation type\r\n",
15266            ),
15267            (
15268                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15269                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15270            ),
15271            (
15272                &[
15273                    b"TS.RANGE",
15274                    b"t",
15275                    b"-",
15276                    b"+",
15277                    b"AGGREGATION",
15278                    b"avg",
15279                    b"100",
15280                    b"X",
15281                    b"EMPTY",
15282                ],
15283                "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15284            ),
15285            (
15286                &[
15287                    b"TS.RANGE",
15288                    b"t",
15289                    b"-",
15290                    b"+",
15291                    b"AGGREGATION",
15292                    b"avg",
15293                    b"100",
15294                    b"BUCKETTIMESTAMP",
15295                    b"z",
15296                ],
15297                "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15298            ),
15299            (
15300                &[
15301                    b"TS.RANGE",
15302                    b"t",
15303                    b"-",
15304                    b"+",
15305                    b"AGGREGATION",
15306                    b"avg",
15307                    b"100",
15308                    b"X",
15309                    b"Y",
15310                    b"BUCKETTIMESTAMP",
15311                    b"-",
15312                ],
15313                "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15314                 AGGREGATION flag\r\n",
15315            ),
15316            (
15317                &[
15318                    b"TS.RANGE",
15319                    b"t",
15320                    b"-",
15321                    b"+",
15322                    b"ALIGN",
15323                    b"z",
15324                    b"AGGREGATION",
15325                    b"avg",
15326                    b"100",
15327                ],
15328                "-ERR TSDB: unknown ALIGN parameter\r\n",
15329            ),
15330            (
15331                &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15332                "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15333            ),
15334            (
15335                &[
15336                    b"TS.RANGE",
15337                    b"t",
15338                    b"-",
15339                    b"+",
15340                    b"ALIGN",
15341                    b"-",
15342                    b"AGGREGATION",
15343                    b"avg",
15344                    b"100",
15345                ],
15346                "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15347            ),
15348            (
15349                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15350                "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15351            ),
15352            (
15353                &[
15354                    b"TS.RANGE",
15355                    b"t",
15356                    b"-",
15357                    b"+",
15358                    b"FILTER_BY_VALUE",
15359                    b"x",
15360                    b"2",
15361                ],
15362                "-ERR TSDB: Couldn't parse MIN\r\n",
15363            ),
15364            (
15365                &[
15366                    b"TS.RANGE",
15367                    b"t",
15368                    b"-",
15369                    b"+",
15370                    b"FILTER_BY_VALUE",
15371                    b"1",
15372                    b"y",
15373                ],
15374                "-ERR TSDB: Couldn't parse MAX\r\n",
15375            ),
15376            (
15377                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15378                "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15379            ),
15380        ];
15381        for (argv, want) in cases {
15382            let got = f.run(argv);
15383            assert_eq!(&got, want, "{:?}", argv.last());
15384        }
15385        // The one sentence here that is yo's own rather than the module's, which
15386        // is D-54. A read that would build more rows than yo will build is
15387        // refused instead of attempted.
15388        f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15389        f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15390        assert_eq!(
15391            f.run(&[
15392                b"TS.RANGE",
15393                b"wide",
15394                b"-",
15395                b"+",
15396                b"AGGREGATION",
15397                b"avg",
15398                b"1",
15399                b"EMPTY"
15400            ]),
15401            "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15402        );
15403    }
15404
15405    /// What RESP3 changes on a read, which is only how a number is written.
15406    #[test]
15407    fn resp3_writes_a_read_value_as_a_double() {
15408        let mut f = Fixture::new();
15409        f.out = Out::new(Proto::Resp3);
15410        for (at, v) in [
15411            (b"0".as_slice(), b"1".as_slice()),
15412            (b"100", b"2"),
15413            (b"500", b"nan"),
15414            (b"600", b"3"),
15415        ] {
15416            f.run(&[b"TS.ADD", b"g", at, v]);
15417        }
15418        assert_eq!(
15419            f.run(&[
15420                b"TS.RANGE",
15421                b"g",
15422                b"0",
15423                b"100",
15424                b"AGGREGATION",
15425                b"avg,min",
15426                b"200"
15427            ]),
15428            "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15429        );
15430        assert_eq!(
15431            f.run(&[
15432                b"TS.RANGE",
15433                b"g",
15434                b"500",
15435                b"600",
15436                b"AGGREGATION",
15437                b"last",
15438                b"100",
15439                b"EMPTY"
15440            ]),
15441            "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15442        );
15443    }
15444
15445    /// Two series with an overlap and a gap each, plus a third holding nothing,
15446    /// which is what the joined reads are measured against.
15447    fn joined() -> Fixture {
15448        let mut f = Fixture::new();
15449        f.run(&[b"TS.CREATE", b"z"]);
15450        for (at, v) in [
15451            (b"10".as_slice(), b"1".as_slice()),
15452            (b"20", b"2"),
15453            (b"40", b"4"),
15454            (b"50", b"5"),
15455        ] {
15456            f.run(&[b"TS.ADD", b"x", at, v]);
15457        }
15458        for (at, v) in [
15459            (b"20".as_slice(), b"20".as_slice()),
15460            (b"30", b"30"),
15461            (b"50", b"50"),
15462            (b"60", b"60"),
15463        ] {
15464            f.run(&[b"TS.ADD", b"y", at, v]);
15465        }
15466        f
15467    }
15468
15469    /// The joined read lines its keys up on the timestamp and writes a row as
15470    /// the timestamp and then a nested array of the columns, which is the one
15471    /// shape in the family that is not the flat pair.
15472    #[test]
15473    fn an_nrange_joins_its_keys_on_the_timestamp() {
15474        let mut f = joined();
15475        // One key still nests, so the shape does not depend on the count.
15476        assert_eq!(
15477            f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15478            "*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\
15479             *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15480        );
15481        // A key with no reading where another key has one writes NaN there.
15482        assert_eq!(
15483            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15484            "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15485             *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15486             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15487             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15488             *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15489             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15490        );
15491        // A series holding nothing is a column of NaN and never a row of its
15492        // own, and the same key twice answers twice.
15493        assert_eq!(
15494            f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15495            "*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"
15496        );
15497        assert_eq!(
15498            f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15499            "*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"
15500        );
15501        // COUNT is applied to the joined rows and not to each key, so backwards
15502        // it gives the newest joined row rather than the newest of each.
15503        assert_eq!(
15504            f.run(&[
15505                b"TS.NREVRANGE",
15506                b"2",
15507                b"x",
15508                b"y",
15509                b"-",
15510                b"+",
15511                b"COUNT",
15512                b"1"
15513            ]),
15514            "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15515        );
15516        assert_eq!(
15517            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
15518            "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
15519        );
15520        // The two sample filters are settled a key at a time, before the join.
15521        assert_eq!(
15522            f.run(&[
15523                b"TS.NRANGE",
15524                b"2",
15525                b"x",
15526                b"y",
15527                b"-",
15528                b"+",
15529                b"FILTER_BY_VALUE",
15530                b"2",
15531                b"30"
15532            ]),
15533            "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15534             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15535             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15536             *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
15537        );
15538    }
15539
15540    /// The aggregation on a joined read names one reduction a key and then the
15541    /// one bucket width, and each name may be a comma list, so a row can be
15542    /// wider than the key count.
15543    #[test]
15544    fn an_nrange_aggregation_names_one_reduction_a_key() {
15545        let mut f = joined();
15546        assert_eq!(
15547            f.run(&[
15548                b"TS.NRANGE",
15549                b"2",
15550                b"x",
15551                b"y",
15552                b"-",
15553                b"+",
15554                b"AGGREGATION",
15555                b"sum",
15556                b"sum",
15557                b"20"
15558            ]),
15559            "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
15560             *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
15561             *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
15562             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15563        );
15564        // A comma list on the first key widens the row to three columns.
15565        assert_eq!(
15566            f.run(&[
15567                b"TS.NRANGE",
15568                b"2",
15569                b"x",
15570                b"y",
15571                b"-",
15572                b"+",
15573                b"AGGREGATION",
15574                b"sum,count",
15575                b"avg",
15576                b"20"
15577            ]),
15578            "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
15579             *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
15580             *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
15581             *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
15582        );
15583        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
15584        // sits one or two past the width whatever the key count is.
15585        assert_eq!(
15586            f.run(&[
15587                b"TS.NRANGE",
15588                b"2",
15589                b"x",
15590                b"y",
15591                b"-",
15592                b"+",
15593                b"AGGREGATION",
15594                b"avg",
15595                b"sum",
15596                b"100",
15597                b"EMPTY",
15598                b"BUCKETTIMESTAMP",
15599                b"end"
15600            ]),
15601            "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
15602        );
15603        // A COUNT landing in one of the name slots is a reduction name and not
15604        // the keyword, and the read then has no count at all.
15605        assert_eq!(
15606            f.run(&[
15607                b"TS.NRANGE",
15608                b"2",
15609                b"x",
15610                b"y",
15611                b"-",
15612                b"+",
15613                b"AGGREGATION",
15614                b"avg",
15615                b"COUNT",
15616                b"100"
15617            ]),
15618            "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
15619        );
15620    }
15621
15622    /// The sentences a joined read answers when it does not add up, which are
15623    /// the module's own and come out in the module's own order.
15624    #[test]
15625    fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
15626        let mut f = joined();
15627        f.run(&[b"SET", b"str", b"hi"]);
15628        let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
15629        let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
15630                       must be equal to numkeys\r\n";
15631        let cases: &[(&[&[u8]], &str)] = &[
15632            (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
15633            (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
15634            (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
15635            // Not enough words behind the count for the keys and both ends of
15636            // the span, which is an arity error however many keys were named.
15637            (
15638                &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
15639                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15640            ),
15641            (
15642                &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
15643                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15644            ),
15645            // The reduction names are read before the two ends of the span,
15646            // which no other option is.
15647            (
15648                &[
15649                    b"TS.NRANGE",
15650                    b"2",
15651                    b"x",
15652                    b"y",
15653                    b"abc",
15654                    b"+",
15655                    b"AGGREGATION",
15656                    b"nope",
15657                    b"sum",
15658                    b"100",
15659                ],
15660                "-ERR TSDB: Unknown aggregation type\r\n",
15661            ),
15662            (
15663                &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
15664                "-ERR TSDB: wrong fromTimestamp\r\n",
15665            ),
15666            (
15667                &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
15668                "-ERR TSDB: wrong toTimestamp\r\n",
15669            ),
15670            // A name slot that is missing or holds a number is the count
15671            // sentence, and a width slot that is itself a reduction name is
15672            // that sentence as well.
15673            (
15674                &[
15675                    b"TS.NRANGE",
15676                    b"2",
15677                    b"x",
15678                    b"y",
15679                    b"-",
15680                    b"+",
15681                    b"AGGREGATION",
15682                    b"avg",
15683                ],
15684                numkeys,
15685            ),
15686            (
15687                &[
15688                    b"TS.NRANGE",
15689                    b"2",
15690                    b"x",
15691                    b"y",
15692                    b"-",
15693                    b"+",
15694                    b"AGGREGATION",
15695                    b"100",
15696                    b"sum",
15697                    b"100",
15698                ],
15699                numkeys,
15700            ),
15701            (
15702                &[
15703                    b"TS.NRANGE",
15704                    b"2",
15705                    b"x",
15706                    b"y",
15707                    b"-",
15708                    b"+",
15709                    b"AGGREGATION",
15710                    b"avg",
15711                    b"sum",
15712                    b"sum",
15713                    b"100",
15714                ],
15715                numkeys,
15716            ),
15717            (
15718                &[
15719                    b"TS.NRANGE",
15720                    b"2",
15721                    b"x",
15722                    b"y",
15723                    b"-",
15724                    b"+",
15725                    b"AGGREGATION",
15726                    b"avg",
15727                    b"sum",
15728                    b"abc",
15729                ],
15730                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15731            ),
15732            (
15733                &[
15734                    b"TS.NRANGE",
15735                    b"2",
15736                    b"x",
15737                    b"y",
15738                    b"-",
15739                    b"+",
15740                    b"AGGREGATION",
15741                    b"avg",
15742                    b"sum",
15743                    b"0",
15744                ],
15745                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15746            ),
15747            // With one key none of that applies and the plain parser runs, so a
15748            // lone width is a missing width rather than a count mismatch.
15749            (
15750                &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
15751                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15752            ),
15753            (
15754                &[
15755                    b"TS.NRANGE",
15756                    b"1",
15757                    b"x",
15758                    b"-",
15759                    b"+",
15760                    b"AGGREGATION",
15761                    b"100",
15762                    b"200",
15763                ],
15764                "-ERR TSDB: Unknown aggregation type\r\n",
15765            ),
15766            // The keys come last and in the order they were named.
15767            (
15768                &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
15769                "-ERR TSDB: the key does not exist\r\n",
15770            ),
15771            (
15772                &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
15773                "-ERR WRONGTYPE Operation against a key \
15774                 holding the wrong kind of value\r\n",
15775            ),
15776        ];
15777        for (argv, want) in cases {
15778            let got = f.run(argv);
15779            assert_eq!(&got, want, "{argv:?}");
15780        }
15781    }
15782
15783    /// `TS.READ`, which is a key, one timestamp and everything from there on.
15784    #[test]
15785    fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
15786        let mut f = joined();
15787        assert_eq!(
15788            f.run(&[b"TS.READ", b"x", b"-"]),
15789            "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
15790             *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15791        );
15792        // A plus is the last sample on its own, and a timestamp between two
15793        // samples starts at the one behind it.
15794        assert_eq!(
15795            f.run(&[b"TS.READ", b"x", b"+"]),
15796            "*1\r\n*2\r\n:50\r\n+5\r\n"
15797        );
15798        assert_eq!(
15799            f.run(&[b"TS.READ", b"x", b"25"]),
15800            "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15801        );
15802        // Past the end, a series holding nothing and a key that is not there
15803        // are all the empty array rather than an error.
15804        assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
15805        assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
15806        assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
15807        assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
15808        // The timestamp refusal goes out with nothing in front of it, and a key
15809        // holding something else answers the bare WRONGTYPE rather than the
15810        // module's prefixed one, both unlike the rest of the family.
15811        assert_eq!(
15812            f.run(&[b"TS.READ", b"x", b"abc"]),
15813            "-TSDB: invalid timestamp\r\n"
15814        );
15815        assert_eq!(
15816            f.run(&[b"TS.READ", b"x", b"-1"]),
15817            "-TSDB: invalid timestamp\r\n"
15818        );
15819        f.run(&[b"SET", b"str", b"hi"]);
15820        assert_eq!(
15821            f.run(&[b"TS.READ", b"str", b"-"]),
15822            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15823        );
15824        // Anything other than exactly three words is an arity error, so there
15825        // is nowhere to put an option even though the table says minus three.
15826        assert_eq!(
15827            f.run(&[b"TS.READ", b"x"]),
15828            "-ERR wrong number of arguments for 'ts.read' command\r\n"
15829        );
15830        assert_eq!(
15831            f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
15832            "-ERR wrong number of arguments for 'ts.read' command\r\n"
15833        );
15834    }
15835
15836    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
15837    /// to read the count to find them.
15838    #[test]
15839    fn getkeys_reads_the_count_of_a_joined_read() {
15840        let mut f = Fixture::new();
15841        assert_eq!(
15842            f.run(&[
15843                b"COMMAND",
15844                b"GETKEYS",
15845                b"TS.NRANGE",
15846                b"2",
15847                b"a",
15848                b"b",
15849                b"-",
15850                b"+"
15851            ]),
15852            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
15853        );
15854        assert_eq!(
15855            f.run(&[
15856                b"COMMAND",
15857                b"GETKEYS",
15858                b"TS.NREVRANGE",
15859                b"1",
15860                b"a",
15861                b"-",
15862                b"+"
15863            ]),
15864            "*1\r\n$1\r\na\r\n"
15865        );
15866        // A count of zero, or one too large for the words that follow it, is
15867        // the server's own refusal and not the module's.
15868        for n in [b"0".as_slice(), b"9", b"abc"] {
15869            assert_eq!(
15870                f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
15871                "-ERR Invalid arguments specified for command\r\n"
15872            );
15873        }
15874    }
15875
15876    /// The five series every test of the label surface works against.
15877    fn labelled() -> Fixture {
15878        let mut f = Fixture::new();
15879        f.run(&[
15880            b"TS.CREATE",
15881            b"a",
15882            b"LABELS",
15883            b"room",
15884            b"kitchen",
15885            b"x",
15886            b"1",
15887        ]);
15888        f.run(&[
15889            b"TS.CREATE",
15890            b"b",
15891            b"LABELS",
15892            b"room",
15893            b"bedroom",
15894            b"x",
15895            b"2",
15896        ]);
15897        f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
15898        f.run(&[b"TS.CREATE", b"d"]);
15899        f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
15900        f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
15901        f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
15902        f
15903    }
15904
15905    /// The filter grammar, which is four steps and a `strtok` rather than a
15906    /// grammar, and which every command that searches on labels shares.
15907    #[test]
15908    fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
15909        let mut f = labelled();
15910        let cases: &[(&[&[u8]], &str)] = &[
15911            // The plain forms, and the order the answer comes back in, which is
15912            // by key name and not by anything the series remembers.
15913            (
15914                &[b"TS.QUERYINDEX", b"room=kitchen"],
15915                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15916            ),
15917            (
15918                &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
15919                "*1\r\n$1\r\na\r\n",
15920            ),
15921            (
15922                &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
15923                "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
15924            ),
15925            // An empty list still counts as something that says which series to
15926            // take, it just never takes any.
15927            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
15928            // Absent and present, neither of which stands on its own.
15929            (
15930                &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
15931                "*1\r\n$1\r\nc\r\n",
15932            ),
15933            (
15934                &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
15935                "*1\r\n$1\r\na\r\n",
15936            ),
15937            (
15938                &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
15939                "-ERR TSDB: please provide at least one matcher\r\n",
15940            ),
15941            // A run of separators is one separator and everything past the
15942            // second field is dropped, so all three of these ask one question.
15943            (
15944                &[b"TS.QUERYINDEX", b"room==kitchen"],
15945                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15946            ),
15947            (
15948                &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
15949                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15950            ),
15951            (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
15952            // A bracket is only a list when it sits straight behind the
15953            // separator, and then the label in front of it has to be there.
15954            (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
15955            (
15956                &[b"TS.QUERYINDEX", b"=(1)"],
15957                "-ERR TSDB: failed parsing labels\r\n",
15958            ),
15959            (
15960                &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
15961                "-ERR TSDB: failed parsing labels\r\n",
15962            ),
15963            (
15964                &[b"TS.QUERYINDEX", b"room=(kitchen"],
15965                "-ERR TSDB: failed parsing labels\r\n",
15966            ),
15967            (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
15968            (
15969                &[b"TS.QUERYINDEX", b"nonsense"],
15970                "-ERR TSDB: failed parsing labels\r\n",
15971            ),
15972            // Nothing here says which series to take.
15973            (
15974                &[b"TS.QUERYINDEX", b"room!=kitchen"],
15975                "-ERR TSDB: please provide at least one matcher\r\n",
15976            ),
15977            // Names and values are both compared byte for byte.
15978            (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
15979            (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
15980            (
15981                &[b"TS.QUERYINDEX"],
15982                "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
15983            ),
15984        ];
15985        for (argv, want) in cases {
15986            let got = f.run(argv);
15987            assert_eq!(&got, want, "{:?}", argv.last());
15988        }
15989    }
15990
15991    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
15992    #[test]
15993    fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
15994        let mut f = labelled();
15995        let cases: &[(&[&[u8]], &str)] = &[
15996            (
15997                &[b"TS.QUERYLABELS", b"LABELS"],
15998                "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
15999            ),
16000            (
16001                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16002                "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16003            ),
16004            (
16005                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16006                "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16007            ),
16008            // The series wearing `r` twice contributes the smaller of the two
16009            // here, which is not the one it was written down as first.
16010            (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16011            (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16012            (
16013                &[b"TS.QUERYLABELS", b"VALUES"],
16014                "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16015            ),
16016            (
16017                &[b"TS.QUERYLABELS", b"ZZZ"],
16018                "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16019            ),
16020            (
16021                &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16022                "-ERR TSDB: unknown argument, expected FILTER\r\n",
16023            ),
16024            (
16025                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16026                "-ERR TSDB: FILTER given with no filter expressions\r\n",
16027            ),
16028            // With no filter at all every series is taken, which is why the
16029            // first case here answers about `r` as well. A filter that is there
16030            // still has to say which series to take.
16031            (
16032                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16033                "-ERR TSDB: please provide at least one matcher\r\n",
16034            ),
16035            (
16036                &[
16037                    b"TS.QUERYLABELS",
16038                    b"LABELS",
16039                    b"FILTER",
16040                    b"room=kitchen",
16041                    b"x=",
16042                ],
16043                "*1\r\n$4\r\nroom\r\n",
16044            ),
16045        ];
16046        for (argv, want) in cases {
16047            let got = f.run(argv);
16048            assert_eq!(&got, want, "{:?}", argv.last());
16049        }
16050    }
16051
16052    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
16053    /// ways of asking for the labels back alongside it.
16054    #[test]
16055    fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16056        let mut f = labelled();
16057        let cases: &[(&[&[u8]], &str)] = &[
16058            // A series with no samples writes an empty array where the sample
16059            // goes rather than dropping out of the reply.
16060            (
16061                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16062                "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16063                 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16064            ),
16065            (
16066                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16067                "*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\
16068                 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16069                 *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",
16070            ),
16071            // A selected label the series does not wear is a nil, not a gap.
16072            (
16073                &[
16074                    b"TS.MGET",
16075                    b"SELECTED_LABELS",
16076                    b"x",
16077                    b"FILTER",
16078                    b"room=kitchen",
16079                ],
16080                "*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\
16081                 *2\r\n:100\r\n+1.5\r\n\
16082                 *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",
16083            ),
16084            // The other half of the duplicated name rule. This one takes the
16085            // first written down where `TS.QUERYLABELS` takes the smallest.
16086            (
16087                &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16088                "*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",
16089            ),
16090            (
16091                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16092                "*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\
16093                 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16094            ),
16095            // A word that is not an option is ignored, but a missing `FILTER`
16096            // is an arity error whatever else was written.
16097            (
16098                &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16099                "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16100            ),
16101            (
16102                &[b"TS.MGET", b"a", b"b", b"c"],
16103                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16104            ),
16105            (
16106                &[b"TS.MGET", b"FILTER"],
16107                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16108            ),
16109            // Both keyword checks happen before the filter is read, and the two
16110            // sentences spell the second keyword without its `ED`.
16111            (
16112                &[
16113                    b"TS.MGET",
16114                    b"WITHLABELS",
16115                    b"SELECTED_LABELS",
16116                    b"x",
16117                    b"FILTER",
16118                    b"bad",
16119                ],
16120                "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16121            ),
16122            (
16123                &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16124                "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16125            ),
16126        ];
16127        for (argv, want) in cases {
16128            let got = f.run(argv);
16129            assert_eq!(&got, want, "{:?}", argv.last());
16130        }
16131    }
16132
16133    /// What RESP3 changes across the label surface, which is a set where there
16134    /// was an array and a map where there was a pair of them.
16135    #[test]
16136    fn resp3_writes_the_label_surface_as_sets_and_maps() {
16137        let mut f = labelled();
16138        f.out = Out::new(Proto::Resp3);
16139        let cases: &[(&[&[u8]], &str)] = &[
16140            (
16141                &[b"TS.QUERYINDEX", b"room=kitchen"],
16142                "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16143            ),
16144            (
16145                &[b"TS.QUERYLABELS", b"LABELS"],
16146                "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16147            ),
16148            (
16149                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16150                "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16151            ),
16152            // The key stops being the first of three and becomes the map key,
16153            // and the labels stop being pairs and become a map of their own.
16154            (
16155                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16156                "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16157                 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16158            ),
16159            (
16160                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16161                "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16162                 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16163                 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16164            ),
16165            (
16166                &[
16167                    b"TS.MGET",
16168                    b"SELECTED_LABELS",
16169                    b"x",
16170                    b"FILTER",
16171                    b"room=kitchen",
16172                ],
16173                "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16174                 *2\r\n:100\r\n,1.5\r\n\
16175                 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16176            ),
16177            // A map with a name in it twice, which is what a series wearing one
16178            // label name twice turns into.
16179            (
16180                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16181                "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16182                 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16183            ),
16184        ];
16185        for (argv, want) in cases {
16186            let got = f.run(argv);
16187            assert_eq!(&got, want, "{:?}", argv.last());
16188        }
16189    }
16190
16191    /// The same five series with enough samples in them for a group to have
16192    /// something to fold.
16193    fn spanned() -> Fixture {
16194        let mut f = labelled();
16195        f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16196        f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16197        f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16198        f
16199    }
16200
16201    /// A span read out of every series a filter takes, with and without a group
16202    /// over the top of it.
16203    #[test]
16204    fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16205        let mut f = spanned();
16206        let cases: &[(&[&[u8]], &str)] = &[
16207            (
16208                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16209                "*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\
16210                 *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",
16211            ),
16212            // Newest first is applied to each series before anything else sees
16213            // the rows.
16214            (
16215                &[
16216                    b"TS.MREVRANGE",
16217                    b"-",
16218                    b"+",
16219                    b"WITHLABELS",
16220                    b"FILTER",
16221                    b"room=kitchen",
16222                ],
16223                "*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\
16224                 *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\
16225                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16226                 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16227            ),
16228            // A label a series does not wear comes back against a nil rather
16229            // than being left out.
16230            (
16231                &[
16232                    b"TS.MRANGE",
16233                    b"-",
16234                    b"+",
16235                    b"SELECTED_LABELS",
16236                    b"x",
16237                    b"FILTER",
16238                    b"room=kitchen",
16239                ],
16240                "*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\
16241                 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16242                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16243                 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16244            ),
16245            // The fold: 100 is in both series and adds up, the other two are in
16246            // one each and are still rows.
16247            (
16248                &[
16249                    b"TS.MRANGE",
16250                    b"-",
16251                    b"+",
16252                    b"FILTER",
16253                    b"room=kitchen",
16254                    b"GROUPBY",
16255                    b"room",
16256                    b"REDUCE",
16257                    b"sum",
16258                ],
16259                "*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\
16260                 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16261            ),
16262            // RESP2 has nowhere to put the reducer and the member keys, so a
16263            // group wearing labels writes them as two more labels.
16264            (
16265                &[
16266                    b"TS.MRANGE",
16267                    b"-",
16268                    b"+",
16269                    b"WITHLABELS",
16270                    b"FILTER",
16271                    b"room=kitchen",
16272                    b"GROUPBY",
16273                    b"room",
16274                    b"REDUCE",
16275                    b"max",
16276                ],
16277                "*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\
16278                 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16279                 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16280                 *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",
16281            ),
16282            // A count is applied to each member and then again to the fold.
16283            (
16284                &[
16285                    b"TS.MREVRANGE",
16286                    b"-",
16287                    b"+",
16288                    b"COUNT",
16289                    b"1",
16290                    b"FILTER",
16291                    b"room=kitchen",
16292                    b"GROUPBY",
16293                    b"room",
16294                    b"REDUCE",
16295                    b"count",
16296                ],
16297                "*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",
16298            ),
16299            // Nothing wears the label, so nothing is in any group.
16300            (
16301                &[
16302                    b"TS.MRANGE",
16303                    b"-",
16304                    b"+",
16305                    b"FILTER",
16306                    b"room=kitchen",
16307                    b"GROUPBY",
16308                    b"nope",
16309                    b"REDUCE",
16310                    b"sum",
16311                ],
16312                "*0\r\n",
16313            ),
16314            (
16315                &[
16316                    b"TS.MRANGE",
16317                    b"-",
16318                    b"+",
16319                    b"AGGREGATION",
16320                    b"sum,avg",
16321                    b"100",
16322                    b"FILTER",
16323                    b"room=bedroom",
16324                ],
16325                "*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",
16326            ),
16327            // The errors, in the order they are looked for.
16328            (
16329                &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16330                "-ERR TSDB: missing FILTER argument\r\n",
16331            ),
16332            (
16333                &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16334                "-ERR TSDB: missing labels for filter argument\r\n",
16335            ),
16336            (
16337                &[
16338                    b"TS.MRANGE",
16339                    b"-",
16340                    b"+",
16341                    b"GROUPBY",
16342                    b"room",
16343                    b"REDUCE",
16344                    b"sum",
16345                    b"FILTER",
16346                    b"room=kitchen",
16347                ],
16348                "-ERR TSDB: GROUPBY should always come after filter\r\n",
16349            ),
16350            // The group is four words from the end here, so the length is what
16351            // is wrong with it.
16352            (
16353                &[
16354                    b"TS.MRANGE",
16355                    b"-",
16356                    b"+",
16357                    b"FILTER",
16358                    b"room=kitchen",
16359                    b"GROUPBY",
16360                    b"room",
16361                    b"REDUCE",
16362                    b"sum",
16363                    b"x",
16364                ],
16365                "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16366            ),
16367            // And here it is not, so its words are filters and answer first.
16368            (
16369                &[
16370                    b"TS.MRANGE",
16371                    b"-",
16372                    b"+",
16373                    b"FILTER",
16374                    b"nope",
16375                    b"GROUPBY",
16376                    b"room",
16377                    b"REDUCE",
16378                    b"sum",
16379                    b"x",
16380                ],
16381                "-ERR TSDB: failed parsing labels\r\n",
16382            ),
16383            (
16384                &[
16385                    b"TS.MRANGE",
16386                    b"-",
16387                    b"+",
16388                    b"FILTER",
16389                    b"room=kitchen",
16390                    b"GROUPBY",
16391                    b"room",
16392                    b"REDUCE",
16393                    b"twa",
16394                ],
16395                "-ERR TSDB: Invalid reducer type\r\n",
16396            ),
16397            (
16398                &[
16399                    b"TS.MRANGE",
16400                    b"-",
16401                    b"+",
16402                    b"AGGREGATION",
16403                    b"sum,avg",
16404                    b"100",
16405                    b"FILTER",
16406                    b"room=kitchen",
16407                    b"GROUPBY",
16408                    b"room",
16409                    b"REDUCE",
16410                    b"sum",
16411                ],
16412                "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16413            ),
16414            // The label list ends at a keyword, so this is a `COUNT` with a
16415            // `FILTER` where its number should be.
16416            (
16417                &[
16418                    b"TS.MRANGE",
16419                    b"-",
16420                    b"+",
16421                    b"SELECTED_LABELS",
16422                    b"COUNT",
16423                    b"FILTER",
16424                    b"room=kitchen",
16425                ],
16426                "-ERR TSDB: Couldn't parse COUNT\r\n",
16427            ),
16428        ];
16429        for (argv, want) in cases {
16430            let got = f.run(argv);
16431            assert_eq!(&got, want, "{argv:?}");
16432        }
16433    }
16434
16435    /// The multi key reads on RESP3, where the key becomes a map key and the
16436    /// reducer and the member keys become fields of their own.
16437    #[test]
16438    fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16439        let mut f = spanned();
16440        f.out = Out::new(Proto::Resp3);
16441        let cases: &[(&[&[u8]], &str)] = &[
16442            (
16443                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16444                "%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\
16445                 *1\r\n*2\r\n:200\r\n,2\r\n",
16446            ),
16447            // The reductions a read asked for, which RESP2 has no room for at
16448            // all and which is empty on a read that asked for none.
16449            (
16450                &[
16451                    b"TS.MRANGE",
16452                    b"-",
16453                    b"+",
16454                    b"AGGREGATION",
16455                    b"sum,avg",
16456                    b"100",
16457                    b"FILTER",
16458                    b"room=bedroom",
16459                ],
16460                "%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\
16461                 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16462            ),
16463            (
16464                &[
16465                    b"TS.MRANGE",
16466                    b"-",
16467                    b"+",
16468                    b"FILTER",
16469                    b"room=kitchen",
16470                    b"GROUPBY",
16471                    b"room",
16472                    b"REDUCE",
16473                    b"sum",
16474                ],
16475                "%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\
16476                 $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\
16477                 *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",
16478            ),
16479            // The labels hold only the pair the group was made on, because the
16480            // reducer and the sources have somewhere else to go.
16481            (
16482                &[
16483                    b"TS.MRANGE",
16484                    b"-",
16485                    b"+",
16486                    b"WITHLABELS",
16487                    b"FILTER",
16488                    b"room=kitchen",
16489                    b"GROUPBY",
16490                    b"room",
16491                    b"REDUCE",
16492                    b"max",
16493                ],
16494                "%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\
16495                 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16496                 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16497                 *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",
16498            ),
16499            (
16500                &[
16501                    b"TS.MRANGE",
16502                    b"-",
16503                    b"+",
16504                    b"FILTER",
16505                    b"room=kitchen",
16506                    b"GROUPBY",
16507                    b"nope",
16508                    b"REDUCE",
16509                    b"sum",
16510                ],
16511                "%0\r\n",
16512            ),
16513        ];
16514        for (argv, want) in cases {
16515            let got = f.run(argv);
16516            assert_eq!(&got, want, "{argv:?}");
16517        }
16518    }
16519
16520    /// `TS.CREATERULE`, whose refusals come in an order of their own.
16521    #[test]
16522    fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
16523        let mut f = Fixture::new();
16524        f.run(&[b"TS.CREATE", b"src"]);
16525        f.run(&[b"TS.CREATE", b"dst"]);
16526        f.run(&[b"SET", b"plain", b"v"]);
16527        let cases: &[(&[&[u8]], &str)] = &[
16528            // The width is read before the reduction, the reduction before the
16529            // width being above zero, and all three before either key is looked
16530            // at, so a command that is wrong twice complains about the first.
16531            (
16532                &[
16533                    b"TS.CREATERULE",
16534                    b"src",
16535                    b"dst",
16536                    b"AGGREGATION",
16537                    b"nope",
16538                    b"x",
16539                ],
16540                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16541            ),
16542            (
16543                &[
16544                    b"TS.CREATERULE",
16545                    b"src",
16546                    b"dst",
16547                    b"AGGREGATION",
16548                    b"nope",
16549                    b"10",
16550                ],
16551                "-ERR TSDB: Unknown aggregation type\r\n",
16552            ),
16553            (
16554                &[
16555                    b"TS.CREATERULE",
16556                    b"src",
16557                    b"dst",
16558                    b"AGGREGATION",
16559                    b"avg",
16560                    b"0",
16561                ],
16562                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16563            ),
16564            (
16565                &[
16566                    b"TS.CREATERULE",
16567                    b"src",
16568                    b"dst",
16569                    b"AGGREGATION",
16570                    b"avg",
16571                    b"10",
16572                    b"x",
16573                ],
16574                "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
16575            ),
16576            (
16577                &[
16578                    b"TS.CREATERULE",
16579                    b"src",
16580                    b"src",
16581                    b"AGGREGATION",
16582                    b"avg",
16583                    b"10",
16584                ],
16585                "-ERR TSDB: the source key and destination key should be different\r\n",
16586            ),
16587            // A key holding something else answers the same as a key that is not
16588            // there at all, because the source is looked up first and neither of
16589            // them is a series.
16590            (
16591                &[
16592                    b"TS.CREATERULE",
16593                    b"nope",
16594                    b"plain",
16595                    b"AGGREGATION",
16596                    b"avg",
16597                    b"10",
16598                ],
16599                "-ERR TSDB: the key does not exist\r\n",
16600            ),
16601            (
16602                &[
16603                    b"TS.CREATERULE",
16604                    b"src",
16605                    b"nope",
16606                    b"AGGREGATION",
16607                    b"avg",
16608                    b"10",
16609                ],
16610                "-ERR TSDB: the key does not exist\r\n",
16611            ),
16612            // A keyword other than AGGREGATION is an arity error rather than a
16613            // syntax one, because the arity is all that is checked.
16614            (
16615                &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
16616                "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
16617            ),
16618            (
16619                &[
16620                    b"TS.CREATERULE",
16621                    b"src",
16622                    b"dst",
16623                    b"AGGREGATION",
16624                    b"avg",
16625                    b"10",
16626                ],
16627                "+OK\r\n",
16628            ),
16629            // The link is now in place, so the same rule again is refused from
16630            // the destination's end.
16631            (
16632                &[
16633                    b"TS.CREATERULE",
16634                    b"src",
16635                    b"dst",
16636                    b"AGGREGATION",
16637                    b"avg",
16638                    b"10",
16639                ],
16640                "-ERR TSDB: the destination key already has a src rule\r\n",
16641            ),
16642            // A source that is already someone's destination, and a destination
16643            // that is already someone's source, are two different sentences.
16644            (
16645                &[
16646                    b"TS.CREATERULE",
16647                    b"dst",
16648                    b"src",
16649                    b"AGGREGATION",
16650                    b"avg",
16651                    b"10",
16652                ],
16653                "-ERR TSDB: the source key already has a source rule\r\n",
16654            ),
16655            (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
16656            (
16657                &[b"TS.DELETERULE", b"src", b"dst"],
16658                "-ERR TSDB: compaction rule does not exist\r\n",
16659            ),
16660            // The source is looked up and the destination is not, so a missing
16661            // destination is a missing rule and a missing source is a missing
16662            // key, which is the other way round from `TS.CREATERULE`.
16663            (
16664                &[b"TS.DELETERULE", b"src", b"nope"],
16665                "-ERR TSDB: compaction rule does not exist\r\n",
16666            ),
16667            (
16668                &[b"TS.DELETERULE", b"nope", b"dst"],
16669                "-ERR TSDB: the key does not exist\r\n",
16670            ),
16671        ];
16672        for (argv, want) in cases {
16673            let got = f.run(argv);
16674            assert_eq!(&got, want, "{argv:?}");
16675        }
16676    }
16677
16678    /// What a rule writes, which is every bucket but the one it is filling.
16679    #[test]
16680    fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
16681        let mut f = Fixture::new();
16682        f.run(&[b"TS.CREATE", b"src"]);
16683        f.run(&[b"TS.CREATE", b"dst"]);
16684        // The readings written before the rule was made are not folded, so the
16685        // destination is still empty after the first two.
16686        f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
16687        f.run(&[
16688            b"TS.CREATERULE",
16689            b"src",
16690            b"dst",
16691            b"AGGREGATION",
16692            b"sum",
16693            b"100",
16694        ]);
16695        f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
16696        assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
16697        // The bucket the rule is filling holds only what it was given, so it is
16698        // 2 rather than 3, and it is written when a reading lands past it.
16699        assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
16700        f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
16701        assert_eq!(
16702            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16703            "*1\r\n*2\r\n:0\r\n+2\r\n"
16704        );
16705        // A reading into a bucket that has already been written works that
16706        // bucket out again over everything the source now holds.
16707        f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
16708        assert_eq!(
16709            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16710            "*1\r\n*2\r\n:0\r\n+11\r\n"
16711        );
16712        // Deleting from the source works the buckets it touched out again and
16713        // reopens the newest one, so `LATEST` starts from the whole bucket.
16714        assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
16715        assert_eq!(
16716            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16717            "*1\r\n*2\r\n:0\r\n+8\r\n"
16718        );
16719        assert_eq!(
16720            f.run(&[b"TS.GET", b"dst", b"LATEST"]),
16721            "*2\r\n:100\r\n+4\r\n"
16722        );
16723        // The link shows on both ends, and dropping either key takes it down.
16724        assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
16725        f.run(&[b"DEL", b"dst"]);
16726        assert_eq!(
16727            f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
16728            "-ERR TSDB: compaction rule does not exist\r\n"
16729        );
16730    }
16731
16732    /// The three shapes an `XADD` id can take, and the one rule behind all of
16733    /// them.
16734    #[test]
16735    fn xadd_ids_only_ever_go_up() {
16736        let mut f = Fixture::new();
16737        // A bare millisecond is that millisecond and sequence zero.
16738        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
16739        // And `5-*` is the next free sequence inside it.
16740        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
16741        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
16742        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
16743        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16744
16745        assert!(
16746            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
16747                .contains("equal or smaller")
16748        );
16749        assert!(
16750            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
16751                .contains("must be greater than 0-0")
16752        );
16753        assert!(
16754            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
16755                .contains("Invalid stream ID")
16756        );
16757        // The pairs have to be pairs, and Redis calls an odd one an arity error
16758        // rather than a syntax error even though the table has already passed.
16759        assert!(
16760            f.run(&[b"XADD", b"s", b"*", b"a"])
16761                .contains("wrong number of arguments")
16762        );
16763
16764        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
16765        // producer can tell nobody is consuming this yet from the write landed.
16766        assert_eq!(
16767            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
16768            "$-1\r\n"
16769        );
16770        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
16771        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
16772        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
16773    }
16774
16775    /// The trim options, which are three keywords that disagree about how many
16776    /// arguments they take.
16777    #[test]
16778    fn trimming_reads_its_options_the_way_redis_does() {
16779        let mut f = Fixture::new();
16780        for i in 1..=10u32 {
16781            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
16782        }
16783        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
16784        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16785        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
16786        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16787
16788        // One argument after the keyword and the `~` is read as the threshold,
16789        // which is what a real server does and is the reason this is a number
16790        // complaint and not a syntax one.
16791        assert!(
16792            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
16793                .contains("not an integer")
16794        );
16795        assert!(
16796            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
16797                .contains("MAXLEN argument must be >= 0")
16798        );
16799        // The strategy check runs before the approximation check, so a LIMIT
16800        // with neither is told about the missing strategy.
16801        assert!(
16802            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
16803                .contains("without specifying a trimming strategy")
16804        );
16805        assert!(
16806            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
16807                .contains("without the special ~ option")
16808        );
16809        assert!(
16810            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
16811                .contains("at the same time are not compatible")
16812        );
16813        // NOMKSTREAM is XADD's and XTRIM does not take it.
16814        assert!(
16815            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
16816                .contains("syntax error")
16817        );
16818        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
16819    }
16820
16821    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
16822    #[test]
16823    fn xrange_looks_the_key_up_before_it_reads_the_count() {
16824        let mut f = Fixture::new();
16825        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
16826        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
16827
16828        assert_eq!(
16829            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
16830            "*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\
16831             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16832        );
16833        assert_eq!(
16834            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
16835            "*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"
16836        );
16837        // The exclusive bound is stepped after the missing sequence is filled
16838        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
16839        // `6-1` is still in the range.
16840        assert_eq!(
16841            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
16842            "*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\
16843             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16844        );
16845        assert_eq!(
16846            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
16847            "*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"
16848        );
16849        assert!(
16850            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
16851                .contains("Invalid stream ID")
16852        );
16853
16854        // The two kinds of nothing. A key that is not there is an empty array
16855        // and a key that is there with a count of zero is a null array, because
16856        // the lookup happens first.
16857        assert_eq!(
16858            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
16859            "*0\r\n"
16860        );
16861        assert_eq!(
16862            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
16863            "*-1\r\n"
16864        );
16865        f.run(&[b"SET", b"str", b"v"]);
16866        assert!(
16867            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
16868                .starts_with("-WRONGTYPE")
16869        );
16870        // The count is read in a loop, so the last one wins.
16871        assert_eq!(
16872            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
16873            "*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"
16874        );
16875    }
16876
16877    /// `XDEL` and `XACK` check every id before they touch any of them.
16878    #[test]
16879    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
16880        let mut f = Fixture::new();
16881        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16882        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16883        assert!(
16884            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
16885                .contains("Invalid stream ID")
16886        );
16887        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16888        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
16889        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
16890        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
16891        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
16892    }
16893
16894    /// `XGROUP`, and the two different complaints it makes about arguments.
16895    #[test]
16896    fn xgroup_has_an_arity_per_subcommand() {
16897        let mut f = Fixture::new();
16898        assert!(
16899            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16900                .contains("requires the key")
16901        );
16902        assert_eq!(
16903            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
16904            "+OK\r\n"
16905        );
16906        // A second CREATE is BUSYGROUP and not an ordinary error, because a
16907        // client racing another one to make a group branches on the prefix.
16908        assert!(
16909            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16910                .starts_with("-BUSYGROUP")
16911        );
16912        assert_eq!(
16913            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16914            ":1\r\n"
16915        );
16916        assert_eq!(
16917            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16918            ":0\r\n"
16919        );
16920        assert_eq!(
16921            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
16922            ":0\r\n"
16923        );
16924
16925        // Below the subcommand's own arity is an arity error naming the pair.
16926        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
16927        assert!(
16928            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
16929            "{short}"
16930        );
16931        // At or above it in a shape the handler will not take is the other one.
16932        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
16933        assert!(
16934            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
16935            "{odd}"
16936        );
16937        assert!(
16938            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
16939                .contains("Try XGROUP HELP")
16940        );
16941
16942        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
16943        assert!(
16944            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
16945                .starts_with("-NOGROUP")
16946        );
16947        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
16948        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
16949        assert!(
16950            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
16951                .contains("requires the key")
16952        );
16953    }
16954
16955    /// A group read, an acknowledgement, and what is left in between.
16956    #[test]
16957    fn xreadgroup_hands_out_and_xack_takes_back() {
16958        let mut f = Fixture::new();
16959        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16960        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16961        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
16962
16963        let first = f.run(&[
16964            b"XREADGROUP",
16965            b"GROUP",
16966            b"g",
16967            b"c1",
16968            b"COUNT",
16969            b"1",
16970            b"STREAMS",
16971            b"s",
16972            b">",
16973        ]);
16974        assert_eq!(
16975            first,
16976            "*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"
16977        );
16978        // A history read names its stream even with nothing to show, which is
16979        // the difference between it and a `>` read that found nothing.
16980        assert_eq!(
16981            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
16982            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
16983        );
16984        assert_eq!(
16985            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
16986            "*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"
16987        );
16988
16989        assert_eq!(
16990            f.run(&[b"XPENDING", b"s", b"g"]),
16991            "*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"
16992        );
16993        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
16994        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
16995        // Empty is four nulls and not a zero with three empty things.
16996        assert_eq!(
16997            f.run(&[b"XPENDING", b"s", b"g"]),
16998            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
16999        );
17000
17001        // A history read of an entry that has since been deleted is the id with
17002        // a null beside it, so the consumer can still acknowledge it.
17003        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17004        f.run(&[b"XDEL", b"s", b"2-1"]);
17005        assert_eq!(
17006            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17007            "*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"
17008        );
17009
17010        // The group lookup runs before the id parse, so a `+` at a stream with
17011        // no such group is told about the group and not about the id.
17012        assert!(
17013            f.run(&[
17014                b"XREADGROUP",
17015                b"GROUP",
17016                b"nope",
17017                b"c",
17018                b"STREAMS",
17019                b"s",
17020                b"+"
17021            ])
17022            .starts_with("-NOGROUP")
17023        );
17024        assert!(
17025            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17026                .contains("meaningless in the context of XREADGROUP")
17027        );
17028        assert!(
17029            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17030                .contains("only supported by XREADGROUP")
17031        );
17032        assert!(
17033            f.run(&[
17034                b"XREADGROUP",
17035                b"GROUP",
17036                b"g",
17037                b"c",
17038                b"STREAMS",
17039                b"s",
17040                b"a",
17041                b"b"
17042            ])
17043            .contains("Unbalanced 'xreadgroup' list of streams")
17044        );
17045    }
17046
17047    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
17048    /// answer.
17049    #[test]
17050    fn xread_with_no_block_writes_the_null_itself() {
17051        let mut f = Fixture::new();
17052        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17053        assert_eq!(
17054            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17055            "*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"
17056        );
17057        // Nothing new is a null array and not an empty one, and a stream with
17058        // nothing new is left out rather than sent with an empty list.
17059        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17060        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17061        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17062        assert_eq!(
17063            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17064            "*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"
17065        );
17066        // `$` is the last id, so nothing that is already there comes back.
17067        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17068        // And `+` is the last entry, whatever COUNT says.
17069        assert_eq!(
17070            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17071            "*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"
17072        );
17073        // A count of zero means unlimited here, which is the opposite of what it
17074        // means to XRANGE.
17075        assert_eq!(
17076            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17077            "*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"
17078        );
17079        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
17080        assert!(
17081            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17082                .contains("not an integer")
17083        );
17084        assert!(
17085            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17086                .contains("timeout is negative")
17087        );
17088        assert!(
17089            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17090                .contains("Unbalanced 'xread' list of streams")
17091        );
17092    }
17093
17094    /// A blocked reader, and the two ways it stops being blocked.
17095    #[test]
17096    fn a_blocked_xread_wakes_on_the_next_entry() {
17097        let mut f = Fixture::new();
17098        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17099        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17100        assert_eq!(flow, Flow::Block);
17101        assert!(reply.is_empty());
17102
17103        // Everybody parked on the stream gets the entry, because a read takes
17104        // nothing away. That is the difference between this and BLPOP.
17105        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17106        assert_eq!(flow, Flow::Block);
17107        assert_eq!(f.server.waiters().len(), 2);
17108
17109        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17110        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";
17111        for at in 0..2 {
17112            let mut out = Out::new(Proto::Resp2);
17113            assert!(f.server.serve_waiter(at, 0, &mut out));
17114            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17115        }
17116
17117        // And a deadline that runs out is a null array, the same as a plain
17118        // XREAD that found nothing.
17119        f.server.waiters_mut().forget(7);
17120        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17121        assert_eq!(flow, Flow::Block);
17122        let mut out = Out::new(Proto::Resp2);
17123        assert!(!f.server.serve_waiter(0, 0, &mut out));
17124        assert!(out.as_slice().is_empty());
17125        assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17126        assert_eq!(
17127            core::str::from_utf8(out.as_slice()).expect("ascii"),
17128            "*-1\r\n"
17129        );
17130    }
17131
17132    /// A blocked group reader whose group is destroyed under it.
17133    #[test]
17134    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17135        let mut f = Fixture::new();
17136        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17137        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17138        let (flow, _) = f.flow(&[
17139            b"XREADGROUP",
17140            b"GROUP",
17141            b"g",
17142            b"c",
17143            b"BLOCK",
17144            b"0",
17145            b"STREAMS",
17146            b"s",
17147            b">",
17148        ]);
17149        assert_eq!(flow, Flow::Block);
17150
17151        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17152        let mut out = Out::new(Proto::Resp2);
17153        assert!(f.server.serve_waiter(0, 0, &mut out));
17154        // The ordinary sentence and not a special one about having been parked,
17155        // which is what a running 8.10 sends.
17156        assert_eq!(
17157            core::str::from_utf8(out.as_slice()).expect("ascii"),
17158            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17159        );
17160    }
17161
17162    /// `XCLAIM`, whose argument shape is the odd one in the group.
17163    #[test]
17164    fn xclaim_reads_ids_until_one_will_not_parse() {
17165        let mut f = Fixture::new();
17166        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17167        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17168        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17169        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17170
17171        // Everything after the first argument that is not an id is an option, so
17172        // a `-` is an unrecognised option and not a bad id.
17173        assert!(
17174            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17175                .contains("Unrecognized XCLAIM option '-'")
17176        );
17177        assert_eq!(
17178            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17179            "*1\r\n$3\r\n1-1\r\n"
17180        );
17181        // An id that is pending but whose entry has gone is an empty answer, and
17182        // it leaves the pending list on the way past.
17183        f.run(&[b"XDEL", b"s", b"2-1"]);
17184        assert_eq!(
17185            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17186            "*0\r\n"
17187        );
17188        assert!(
17189            f.run(&[b"XPENDING", b"s", b"g"])
17190                .starts_with("*4\r\n:1\r\n")
17191        );
17192        assert!(
17193            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17194                .starts_with("-NOGROUP")
17195        );
17196        assert!(
17197            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17198                .contains("Invalid min-idle-time argument for XCLAIM")
17199        );
17200    }
17201
17202    /// `XAUTOCLAIM`, and the third value nobody expects.
17203    #[test]
17204    fn xautoclaim_reports_what_it_dropped() {
17205        let mut f = Fixture::new();
17206        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17207        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17208        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17209        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17210        f.run(&[b"XDEL", b"s", b"1-1"]);
17211
17212        // The cursor, what was claimed, and what was dropped for no longer being
17213        // in the stream. The third one is what makes a sweep converge.
17214        assert_eq!(
17215            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17216            "*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"
17217        );
17218        assert!(
17219            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17220                .contains("COUNT must be > 0")
17221        );
17222        assert!(
17223            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17224                .starts_with("-NOGROUP")
17225        );
17226    }
17227
17228    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
17229    #[test]
17230    fn xdelex_answers_one_integer_an_id() {
17231        let mut f = Fixture::new();
17232        for i in 1..=4 {
17233            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17234        }
17235        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17236        f.run(&[
17237            b"XREADGROUP",
17238            b"GROUP",
17239            b"g",
17240            b"c",
17241            b"COUNT",
17242            b"2",
17243            b"STREAMS",
17244            b"s",
17245            b">",
17246        ]);
17247
17248        // One means gone and minus one means it was not there to start with.
17249        assert_eq!(
17250            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17251            "*2\r\n:1\r\n:-1\r\n"
17252        );
17253        // `KEEPREF` leaves the pending entry behind, so the group still counts
17254        // the one it was handed even though the entry has gone.
17255        assert!(
17256            f.run(&[b"XPENDING", b"s", b"g"])
17257                .starts_with("*4\r\n:2\r\n")
17258        );
17259        // `DELREF` takes it out of every pending list on the way past.
17260        assert_eq!(
17261            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17262            "*1\r\n:1\r\n"
17263        );
17264        // `1-1` is still in the list, because the delete before it said KEEPREF.
17265        assert_eq!(
17266            f.run(&[b"XPENDING", b"s", b"g"]),
17267            "*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"
17268        );
17269
17270        // Two means somebody still wants it, and the question is wider than the
17271        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
17272        // refused even though no consumer has ever been handed it.
17273        assert_eq!(
17274            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17275            "*2\r\n:2\r\n:2\r\n"
17276        );
17277
17278        // A key that is not there answers minus ones without reading the IDs.
17279        assert_eq!(
17280            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17281            "*2\r\n:-1\r\n:-1\r\n"
17282        );
17283        // A key that is there validates every ID before deleting any of them.
17284        assert!(
17285            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17286                .starts_with("-ERR Invalid stream ID")
17287        );
17288        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17289
17290        assert!(
17291            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17292                .contains("Number of IDs must be a positive integer")
17293        );
17294        assert!(
17295            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17296                .contains("The `numids` parameter must match the number of arguments")
17297        );
17298        // The condition is one word, so a second one is a syntax error, and so
17299        // is one ID more than the count promised.
17300        assert!(
17301            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17302                .starts_with("-ERR syntax error")
17303        );
17304        assert!(
17305            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17306                .starts_with("-ERR syntax error")
17307        );
17308        // The key is looked up first, so the wrong type beats the syntax.
17309        f.run(&[b"SET", b"str", b"v"]);
17310        assert!(
17311            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17312                .starts_with("-WRONGTYPE")
17313        );
17314    }
17315
17316    /// `XACKDEL`, whose reply is about the pending list and not about the log.
17317    #[test]
17318    fn xackdel_reports_what_the_group_was_holding() {
17319        let mut f = Fixture::new();
17320        for i in 1..=3 {
17321            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17322        }
17323        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17324        f.run(&[
17325            b"XREADGROUP",
17326            b"GROUP",
17327            b"g",
17328            b"c",
17329            b"COUNT",
17330            b"1",
17331            b"STREAMS",
17332            b"s",
17333            b">",
17334        ]);
17335
17336        // Minus one is not about the stream: `2-1` is sitting there unread and
17337        // still answers minus one, because the group was not holding it. It also
17338        // stays, since only an ID that was acknowledged is deleted.
17339        assert_eq!(
17340            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17341            "*2\r\n:1\r\n:-1\r\n"
17342        );
17343        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17344
17345        // A missing group is minus one an ID and not a NOGROUP.
17346        assert_eq!(
17347            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17348            "*1\r\n:-1\r\n"
17349        );
17350        assert_eq!(
17351            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17352            "*1\r\n:-1\r\n"
17353        );
17354
17355        // The acknowledgement happens whatever the condition says, so an ACKED
17356        // that answers two has still emptied the pending list.
17357        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17358        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17359        assert_eq!(
17360            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17361            "*1\r\n:2\r\n"
17362        );
17363        assert_eq!(
17364            f.run(&[b"XPENDING", b"s", b"g"]),
17365            "*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"
17366        );
17367        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17368    }
17369
17370    /// `XNACK`, which hands an entry back to nobody.
17371    #[test]
17372    fn xnack_releases_an_entry_for_the_next_claim() {
17373        let mut f = Fixture::new();
17374        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17375        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17376        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17377        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17378        // Twice, so the delivery count is two and the words have something to
17379        // do with it.
17380        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17381
17382        assert_eq!(
17383            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17384            ":1\r\n"
17385        );
17386        // No owner, no idle time, and the count left where it was. A released
17387        // entry reads as idle for longer than any min-idle-time, which is what
17388        // puts it at the front of the next claim.
17389        assert_eq!(
17390            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17391            "*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"
17392        );
17393        // The consumer no longer holds it, so a filtered XPENDING skips it.
17394        assert_eq!(
17395            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17396            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17397        );
17398        // The bookmark did not move, so a `>` read will not hand it out again.
17399        assert_eq!(
17400            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17401            "*-1\r\n"
17402        );
17403        // A claim at any min-idle-time takes it.
17404        assert_eq!(
17405            f.run(&[
17406                b"XAUTOCLAIM",
17407                b"s",
17408                b"g",
17409                b"c2",
17410                b"99999999",
17411                b"-",
17412                b"JUSTID"
17413            ]),
17414            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17415        );
17416
17417        // `SILENT` takes one off the count rather than putting it back to zero,
17418        // which only shows on an entry that has been handed out more than once.
17419        // It was delivered and then claimed, so it is on two and goes to one.
17420        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17421        assert!(
17422            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17423                .contains(":-1\r\n:1\r\n")
17424        );
17425        // And it stops at zero rather than wrapping.
17426        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17427        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17428        assert!(
17429            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17430                .contains(":-1\r\n:0\r\n")
17431        );
17432        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
17433        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17434        assert!(
17435            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17436                .contains(":9223372036854775807\r\n")
17437        );
17438        f.run(&[
17439            b"XNACK",
17440            b"s",
17441            b"g",
17442            b"FATAL",
17443            b"IDS",
17444            b"1",
17445            b"1-1",
17446            b"RETRYCOUNT",
17447            b"3",
17448        ]);
17449        assert!(
17450            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17451                .contains(":-1\r\n:3\r\n")
17452        );
17453
17454        // Releasing something the group is not holding is zero, and `FORCE`
17455        // makes the pending entry rather than answering zero. A forced entry
17456        // starts at zero, since there was no earlier count to keep.
17457        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17458        assert_eq!(
17459            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17460            ":0\r\n"
17461        );
17462        assert_eq!(
17463            f.run(&[
17464                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17465            ]),
17466            ":1\r\n"
17467        );
17468        assert!(
17469            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17470                .contains(":-1\r\n:0\r\n")
17471        );
17472        // `FORCE` on an ID the stream does not have is still zero.
17473        assert_eq!(
17474            f.run(&[
17475                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17476            ]),
17477            ":0\r\n"
17478        );
17479
17480        // The group is looked up before the mode word, and it raises rather
17481        // than answering per ID the way the two delete commands do.
17482        assert_eq!(
17483            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17484            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17485        );
17486        assert!(
17487            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17488                .starts_with("-ERR")
17489        );
17490        // Its own sentences, which are not the ones XDELEX uses.
17491        assert!(
17492            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17493                .contains("numids must be a positive integer")
17494        );
17495        assert!(
17496            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17497                .contains("number of IDs doesn't match numids")
17498        );
17499        // Everything past the counted IDs is an option, so one too many is an
17500        // option nobody recognises and not a count that does not add up.
17501        assert!(
17502            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17503                .contains("Unrecognized XNACK option '2-1'")
17504        );
17505    }
17506
17507    /// `XINFO`, which is where the shape of the storage shows through.
17508    #[test]
17509    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
17510        let mut f = Fixture::new();
17511        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17512        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17513        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17514        f.run(&[
17515            b"XREADGROUP",
17516            b"GROUP",
17517            b"g",
17518            b"c1",
17519            b"COUNT",
17520            b"1",
17521            b"STREAMS",
17522            b"s",
17523            b">",
17524        ]);
17525
17526        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17527        // Ten pairs, since the six idempotency fields have nothing behind them
17528        // here and a zero would claim they had. That is D-27.
17529        assert!(info.starts_with("*20\r\n"), "{info}");
17530        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
17531        assert!(
17532            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
17533            "{info}"
17534        );
17535        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
17536        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
17537
17538        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
17539        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
17540        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
17541        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
17542        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
17543
17544        // A consumer that has never been given anything reports minus one for
17545        // inactive rather than the moment it turned up, which is what tells a
17546        // worker that is stuck from one that has nothing to do.
17547        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
17548        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
17549        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
17550        assert!(
17551            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
17552            "{consumers}"
17553        );
17554        // And in name order, which the storage does not hold them in.
17555        let c1 = consumers.find("c1").unwrap();
17556        let c2 = consumers.find("c2").unwrap();
17557        assert!(c1 < c2, "{consumers}");
17558
17559        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
17560        assert!(full.starts_with("*18\r\n"), "{full}");
17561        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
17562        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
17563
17564        assert!(
17565            f.run(&[b"XINFO", b"STREAM", b"missing"])
17566                .contains("no such key")
17567        );
17568        assert!(
17569            f.run(&[b"XINFO", b"GROUPS", b"missing"])
17570                .contains("no such key")
17571        );
17572        assert!(
17573            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
17574                .starts_with("-NOGROUP")
17575        );
17576        assert!(
17577            f.run(&[b"XINFO", b"NOSUCH", b"s"])
17578                .contains("Try XINFO HELP")
17579        );
17580        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
17581        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
17582    }
17583
17584    /// `XPENDING`'s long form, which reads its arguments by counting them.
17585    #[test]
17586    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
17587        let mut f = Fixture::new();
17588        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17589        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17590        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17591
17592        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
17593        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");
17594        assert_eq!(
17595            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17596            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
17597        );
17598        // A consumer nobody has heard of holds nothing rather than erroring.
17599        assert_eq!(
17600            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
17601            "*0\r\n"
17602        );
17603        assert_eq!(
17604            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
17605            list
17606        );
17607        // IDLE is only read at position three.
17608        assert!(
17609            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
17610                .contains("syntax error")
17611        );
17612        assert!(
17613            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
17614                .contains("syntax error")
17615        );
17616        assert_eq!(
17617            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
17618            "*0\r\n"
17619        );
17620        assert!(
17621            f.run(&[b"XPENDING", b"missing", b"g"])
17622                .starts_with("-NOGROUP")
17623        );
17624    }
17625
17626    /// `XSETID`, which is three counters and two refusals.
17627    #[test]
17628    fn xsetid_will_not_go_below_what_is_there() {
17629        let mut f = Fixture::new();
17630        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
17631        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
17632        assert_eq!(
17633            f.run(&[
17634                b"XSETID",
17635                b"s",
17636                b"10-1",
17637                b"ENTRIESADDED",
17638                b"7",
17639                b"MAXDELETEDID",
17640                b"9-1"
17641            ]),
17642            "+OK\r\n"
17643        );
17644        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17645        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
17646        assert!(
17647            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
17648            "{info}"
17649        );
17650
17651        assert!(
17652            f.run(&[b"XSETID", b"s", b"1-1"])
17653                .contains("smaller than the target stream top item")
17654        );
17655        assert!(
17656            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
17657                .contains("entries_added must be positive")
17658        );
17659        assert!(
17660            f.run(&[b"XSETID", b"missing", b"1-1"])
17661                .contains("no such key")
17662        );
17663    }
17664
17665    /// RESP3, where the two reads answer a map and the entries stay an array.
17666    #[test]
17667    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
17668        let mut f = Fixture::new();
17669        f.run(&[b"HELLO", b"3"]);
17670        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17671        // A map header and then the key and the entries side by side, with no
17672        // two element array wrapping the pair.
17673        assert_eq!(
17674            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17675            "%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"
17676        );
17677        // The fields are still one flat array and not a map, which is Redis's
17678        // shape and is what every consumer written before RESP3 expects.
17679        assert_eq!(
17680            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17681            "*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"
17682        );
17683        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
17684    }
17685
17686    /// A store to migrate values into, so a test can watch the inversion.
17687    ///
17688    /// A vector rather than a file for the same reason the tier's own tests use
17689    /// one: the file work has not attached a real store yet, and what this is
17690    /// checking is the policy above the store rather than the store.
17691    struct Mem {
17692        blobs: Vec<Vec<u8>>,
17693    }
17694
17695    impl yo_kv::cold::Blocks for Mem {
17696        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
17697            self.blobs.push(bytes.to_vec());
17698            Ok(yo_common::Addr::new(
17699                yo_common::Space::Log,
17700                (self.blobs.len() - 1) as u64,
17701            ))
17702        }
17703
17704        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
17705            self.blobs
17706                .get(at.offset() as usize)
17707                .map(Vec::as_slice)
17708                .ok_or_else(|| {
17709                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
17710                })
17711        }
17712
17713        fn bytes(&self) -> u64 {
17714            self.blobs.iter().map(|b| b.len() as u64).sum()
17715        }
17716    }
17717
17718    /// A server holding several segments of strings, with somewhere to put them.
17719    ///
17720    /// Answers the fixture and what it was holding when it stopped filling.
17721    fn filled(attach: bool) -> (Fixture, usize) {
17722        let mut f = Fixture::new();
17723        if attach {
17724            f.server
17725                .striped(0)
17726                .stripe_mut(0)
17727                .attach(Box::new(Mem { blobs: Vec::new() }));
17728        }
17729        let val = vec![b'v'; 256];
17730        for i in 0..24000u32 {
17731            let k = format!("key:{i:08}");
17732            f.run(&[b"SET", k.as_bytes(), &val]);
17733        }
17734        let full = f.server.memory_bytes();
17735        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
17736        (f, full)
17737    }
17738
17739    /// Write until the server is under `limit` or the writes run out.
17740    ///
17741    /// The same shape the eviction test uses. A memory limit is enforced in
17742    /// front of a command, so nothing happens until something is written, and
17743    /// the budget means one command does not do the whole job.
17744    fn press(f: &mut Fixture, limit: usize) {
17745        let val = vec![b'v'; 256];
17746        for i in 0..3000u32 {
17747            let k = format!("new:{i:08}");
17748            assert_eq!(
17749                f.run(&[b"SET", k.as_bytes(), &val]),
17750                "+OK\r\n",
17751                "write {i} was refused"
17752            );
17753            f.server.refresh_memory();
17754            if f.server.memory_bytes() <= limit {
17755                return;
17756            }
17757        }
17758        panic!(
17759            "it never got under: {} against {limit}",
17760            f.server.memory_bytes()
17761        );
17762    }
17763
17764    #[test]
17765    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
17766        let mut f = Fixture::new();
17767        assert_eq!(
17768            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17769            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
17770            "no limit is the default"
17771        );
17772        // The same memory value parser `maxmemory` uses, and the same trap in
17773        // it, plus the one spelling that means no limit at all.
17774        for (typed, bytes) in [
17775            (&b"0"[..], "0"),
17776            (b"1024", "1024"),
17777            (b"1k", "1000"),
17778            (b"1gb", "1073741824"),
17779            (b"-1", "-1"),
17780        ] {
17781            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
17782            assert_eq!(
17783                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17784                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
17785                "set {}",
17786                String::from_utf8_lossy(typed)
17787            );
17788        }
17789        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
17790            assert_eq!(
17791                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
17792                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
17793                "refused {}",
17794                String::from_utf8_lossy(bad)
17795            );
17796        }
17797        // Nothing is attached, so the answer to a memory limit is still Redis's.
17798        let info = f.run(&[b"INFO", b"memory"]);
17799        assert!(info.contains("maxstore:-1"), "{info}");
17800        assert!(info.contains("yo_memory_regime:evict"), "{info}");
17801        assert!(info.contains("yo_store_bytes:0"), "{info}");
17802    }
17803
17804    #[test]
17805    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
17806        // The inversion. The same pressure that makes a Redis server throw keys
17807        // away makes this one move values to the file, and afterwards every key
17808        // is still there and still answers with what was stored in it.
17809        let (mut f, full) = filled(true);
17810        let keys = f.run(&[b"DBSIZE"]);
17811        assert!(
17812            f.run(&[b"INFO", b"memory"])
17813                .contains("yo_memory_regime:migrate"),
17814            "a database with somewhere to put values migrates"
17815        );
17816
17817        let limit = full - 2 * 1024 * 1024;
17818        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17819        f.run(&[
17820            b"CONFIG",
17821            b"SET",
17822            b"maxmemory",
17823            limit.to_string().as_bytes(),
17824        ]);
17825        press(&mut f, limit);
17826
17827        assert!(
17828            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17829            "nothing was thrown away"
17830        );
17831        let after: usize = f.run(&[b"DBSIZE"])[1..]
17832            .trim_end()
17833            .parse()
17834            .expect("a count");
17835        let before: usize = keys[1..].trim_end().parse().expect("a count");
17836        assert!(after > before, "the keys that came in are all still here");
17837        assert!(
17838            f.server.store_bytes() > 0,
17839            "and what came out of memory went to the file"
17840        );
17841        // And the values read back, which is the part that makes it a migration
17842        // rather than a loss.
17843        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
17844        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
17845        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
17846    }
17847
17848    #[test]
17849    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
17850        // The documented setting for a drop in cache. A file that may hold
17851        // nothing cannot be migrated to, so eviction is all that is left, and
17852        // the server behaves exactly as it did before any of this existed.
17853        let (mut f, full) = filled(true);
17854        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
17855        assert!(
17856            f.run(&[b"INFO", b"memory"])
17857                .contains("yo_memory_regime:evict"),
17858            "nothing may go to the file"
17859        );
17860
17861        let limit = full - 2 * 1024 * 1024;
17862        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17863        f.run(&[
17864            b"CONFIG",
17865            b"SET",
17866            b"maxmemory",
17867            limit.to_string().as_bytes(),
17868        ]);
17869        press(&mut f, limit);
17870
17871        assert!(
17872            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17873            "keys were thrown away, which is what was asked for"
17874        );
17875        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
17876    }
17877
17878    #[test]
17879    fn a_full_file_goes_back_to_evicting() {
17880        // A storage limit reached is a storage limit, and eviction is the right
17881        // answer to one. The budget here is a few kilobytes, so the first round
17882        // of migration fills it and everything after that is evicted.
17883        let (mut f, full) = filled(true);
17884        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
17885        let limit = full - 2 * 1024 * 1024;
17886        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17887        f.run(&[
17888            b"CONFIG",
17889            b"SET",
17890            b"maxmemory",
17891            limit.to_string().as_bytes(),
17892        ]);
17893        press(&mut f, limit);
17894
17895        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
17896        assert!(
17897            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17898            "and then it started evicting"
17899        );
17900        assert!(
17901            f.run(&[b"INFO", b"memory"])
17902                .contains("yo_memory_regime:evict"),
17903            "and it says so"
17904        );
17905    }
17906    // ------------------------------------------------------------- stripes
17907
17908    /// Every string command, run twice: once on a database that is one keyspace
17909    /// and once on a database that is eight, with the same commands in the same
17910    /// order and the replies compared byte for byte.
17911    ///
17912    /// This is the whole claim the striping rests on. A key belongs to one
17913    /// stripe and to no other, so the answer to a command cannot depend on how
17914    /// many stripes there are, and the way to check that is to ask the same
17915    /// question of two servers that differ in nothing else.
17916    ///
17917    /// The keys are chosen to land on different stripes rather than to look
17918    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
17919    /// those three keys are not all on the same one, and at eight stripes three
17920    /// keys land together about one time in fifty.
17921    #[test]
17922    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
17923        let script: &[&[&[u8]]] = &[
17924            // The single key commands, which are the ones that get handed one
17925            // stripe at the dispatch site.
17926            &[b"SET", b"k1", b"v1"],
17927            &[b"SET", b"k2", b"v2"],
17928            &[b"GET", b"k1"],
17929            &[b"GET", b"nothing"],
17930            &[b"GETSET", b"k1", b"v1b"],
17931            &[b"SETNX", b"k1", b"no"],
17932            &[b"SETNX", b"k3", b"yes"],
17933            &[b"APPEND", b"k3", b"!"],
17934            &[b"STRLEN", b"k3"],
17935            &[b"SETRANGE", b"k3", b"1", b"XY"],
17936            &[b"GETRANGE", b"k3", b"0", b"-1"],
17937            &[b"INCR", b"n1"],
17938            &[b"INCRBY", b"n1", b"41"],
17939            &[b"DECRBY", b"n1", b"2"],
17940            &[b"INCRBYFLOAT", b"f1", b"1.5"],
17941            &[b"SETEX", b"e1", b"100", b"v"],
17942            &[b"PSETEX", b"e2", b"100000", b"v"],
17943            &[b"GETEX", b"e1", b"PERSIST"],
17944            &[b"GETDEL", b"k2"],
17945            &[b"GET", b"k2"],
17946            &[b"DIGEST", b"k1"],
17947            &[b"DELEX", b"k3"],
17948            // The five that name more than one key, which are the ones that
17949            // cannot be handed one stripe at all.
17950            &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
17951            &[b"MGET", b"a", b"b", b"c", b"missing"],
17952            &[b"MSETNX", b"d", b"4", b"e", b"5"],
17953            &[b"MSETNX", b"e", b"6", b"f", b"7"],
17954            &[b"MGET", b"d", b"e", b"f"],
17955            &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
17956            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
17957            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
17958            &[b"MGET", b"g", b"h"],
17959            &[b"SET", b"s1", b"ohmytext"],
17960            &[b"SET", b"s2", b"mynewtext"],
17961            &[b"LCS", b"s1", b"s2"],
17962            &[b"LCS", b"s1", b"s2", b"LEN"],
17963            &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
17964            &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
17965            &[b"LCS", b"s1", b"gone"],
17966            // And the errors, which have to be the same errors.
17967            &[b"MSET", b"odd"],
17968            &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
17969            &[b"MGET"],
17970        ];
17971
17972        let mut one = Fixture::new();
17973        let mut many = Fixture::striped(8);
17974        for parts in script {
17975            let a = one.run(parts);
17976            let b = many.run(parts);
17977            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
17978        }
17979    }
17980
17981    /// The keys of an `MSET` really do end up on different stripes.
17982    ///
17983    /// Without this the test above could pass on a server whose stripe number
17984    /// happened to be a constant, which is a striped database in name only.
17985    #[test]
17986    fn a_striped_database_spreads_the_keys_it_is_given() {
17987        let mut f = Fixture::striped(8);
17988        for i in 0..256 {
17989            let key = format!("key:{i}");
17990            f.run(&[b"SET", key.as_bytes(), b"v"]);
17991        }
17992        assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
17993    }
17994
17995    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
17996    /// that is not a string comes back nil and the rest of the reply is intact.
17997    #[test]
17998    fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
17999        let mut one = Fixture::new();
18000        let mut many = Fixture::striped(8);
18001        for f in [&mut one, &mut many] {
18002            f.run(&[b"SET", b"str", b"v"]);
18003            // Planted rather than pushed. `RPUSH` belongs to the list group,
18004            // which has not been taught about stripes yet and would refuse the
18005            // wide server. What is under test is what `MGET` does when it walks
18006            // onto a key that is not a string, and that does not care how the
18007            // key got there.
18008            f.server
18009                .striped(0)
18010                .at(b"list")
18011                .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18012                .expect("a new list");
18013        }
18014        assert_eq!(
18015            one.run(&[b"MGET", b"str", b"list", b"gone"]),
18016            many.run(&[b"MGET", b"str", b"list", b"gone"])
18017        );
18018    }
18019
18020    /// The same claim for the keyspace group, and the same way of checking it.
18021    ///
18022    /// `SORT` is not in the script because it is the one command in that file
18023    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
18024    /// `RANDOMKEY` are not in it either, because those three do not promise an
18025    /// order and comparing two replies byte for byte would be asserting one.
18026    /// They get tests of their own below.
18027    #[test]
18028    fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18029        let script: &[&[&[u8]]] = &[
18030            &[b"SET", b"k1", b"v1"],
18031            &[b"SET", b"k2", b"v2"],
18032            &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18033            &[b"TYPE", b"k1"],
18034            &[b"TYPE", b"gone"],
18035            &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18036            &[b"EXPIRE", b"k1", b"100"],
18037            &[b"TTL", b"k1"],
18038            &[b"EXPIRE", b"k1", b"200", b"NX"],
18039            &[b"PERSIST", b"k1"],
18040            &[b"TTL", b"k1"],
18041            &[b"PEXPIREAT", b"k2", b"1900000000000"],
18042            &[b"EXPIRETIME", b"k2"],
18043            &[b"PEXPIRETIME", b"k2"],
18044            &[b"PERSIST", b"k2"],
18045            &[b"OBJECT", b"ENCODING", b"k1"],
18046            &[b"OBJECT", b"REFCOUNT", b"k1"],
18047            &[b"OBJECT", b"IDLETIME", b"k1"],
18048            &[b"OBJECT", b"FREQ", b"k1"],
18049            &[b"OBJECT", b"ENCODING", b"gone"],
18050            &[b"OBJECT", b"HELP"],
18051            &[b"RENAME", b"k1", b"k9"],
18052            &[b"GET", b"k9"],
18053            &[b"RENAME", b"gone", b"x"],
18054            &[b"RENAMENX", b"k9", b"k2"],
18055            &[b"RENAMENX", b"k9", b"k8"],
18056            &[b"GET", b"k8"],
18057            &[b"COPY", b"k8", b"c1"],
18058            &[b"COPY", b"k8", b"c1"],
18059            &[b"COPY", b"k8", b"c1", b"REPLACE"],
18060            &[b"COPY", b"k8", b"k8"],
18061            &[b"COPY", b"gone", b"c2"],
18062            &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18063            &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18064            &[b"MOVE", b"c1", b"1"],
18065            &[b"MOVE", b"c1", b"1"],
18066            &[b"MOVE", b"k8", b"0"],
18067            &[b"DEL", b"k2", b"gone"],
18068            &[b"UNLINK", b"k8", b"k8"],
18069            &[b"DBSIZE"],
18070        ];
18071
18072        let mut one = Fixture::new();
18073        let mut many = Fixture::striped(8);
18074        for parts in script {
18075            let a = one.run(parts);
18076            let b = many.run(parts);
18077            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18078        }
18079
18080        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
18081        // payload is taken from the store rather than parsed back out of a
18082        // reply that is not text. Both servers dump the same key and the bytes
18083        // are the same bytes, which is the first half of what is being checked
18084        // here.
18085        for f in [&mut one, &mut many] {
18086            f.run(&[b"SET", b"d1", b"payload"]);
18087            let payload = f
18088                .server
18089                .striped(0)
18090                .at(b"d1")
18091                .dump(b"d1")
18092                .expect("a key that is there");
18093            assert!(
18094                f.run(&[b"DUMP", b"d1"])
18095                    .starts_with(&format!("${}", payload.len())),
18096                "a payload of the length the store gave"
18097            );
18098            assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18099            assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18100            assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18101            assert_eq!(
18102                f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18103                "-BUSYKEY Target key name already exists.\r\n"
18104            );
18105            assert_eq!(
18106                f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18107                "-ERR DUMP payload version or checksum are wrong\r\n"
18108            );
18109        }
18110    }
18111
18112    /// A `SCAN` of a database of eight stripes comes back with all of it.
18113    ///
18114    /// The cursor is the thing under test. It has to carry the stripe as well
18115    /// as the place in it, so a client that stops at one stripe and comes back
18116    /// carries on in that stripe and not at the top of the database, and the
18117    /// walk has to end once rather than eight times.
18118    #[test]
18119    fn a_scan_of_a_striped_database_walks_all_of_it() {
18120        let mut f = Fixture::striped(8);
18121        for i in 0..500 {
18122            let key = format!("key:{i}");
18123            f.run(&[b"SET", key.as_bytes(), b"v"]);
18124        }
18125
18126        let mut seen = Vec::new();
18127        let mut cursor = "0".to_owned();
18128        let mut calls = 0;
18129        loop {
18130            let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18131            let (next, keys) = scan_reply(&reply);
18132            seen.extend(keys);
18133            cursor = next;
18134            calls += 1;
18135            assert!(calls < 5_000, "a scan that will not finish");
18136            if cursor == "0" {
18137                break;
18138            }
18139        }
18140        seen.sort();
18141        assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18142        assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18143
18144        // And the options still work when the walk is over several stripes,
18145        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
18146        // applied by each stripe on the way.
18147        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18148        let (_, keys) = scan_reply(&reply);
18149        assert_eq!(keys.len(), 10, "key:40 through key:49");
18150        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18151        let (_, keys) = scan_reply(&reply);
18152        assert!(keys.is_empty(), "nothing here is a list");
18153    }
18154
18155    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
18156    ///
18157    /// The draw picks the stripe first, so the thing that can go wrong is that
18158    /// it always picks the same one, and two hundred draws over eight stripes
18159    /// would make that obvious.
18160    #[test]
18161    fn a_random_key_can_come_from_any_stripe() {
18162        let mut f = Fixture::striped(8);
18163        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18164        for i in 0..200 {
18165            let key = format!("key:{i}");
18166            f.run(&[b"SET", key.as_bytes(), b"v"]);
18167        }
18168        let mut homes = std::collections::HashSet::new();
18169        for _ in 0..200 {
18170            let got = f.run(&[b"RANDOMKEY"]);
18171            let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18172            assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18173            homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18174        }
18175        assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18176    }
18177
18178    /// Two keys that are not on the same stripe, which is what `RENAME` and
18179    /// `COPY` have to cope with and what a test has to arrange rather than
18180    /// hope for.
18181    fn apart(f: &mut Fixture, src: &str) -> String {
18182        let home = f.server.striped(0).stripe_of(src.as_bytes());
18183        for i in 0..1_000 {
18184            let dst = format!("dst:{i}");
18185            if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18186                return dst;
18187            }
18188        }
18189        panic!("eight stripes and a thousand keys all landed in one place");
18190    }
18191
18192    /// A rename whose two keys are on two stripes moves the value, the deadline
18193    /// and, for a collection, the body itself.
18194    #[test]
18195    fn a_rename_across_stripes_takes_everything_with_it() {
18196        let mut f = Fixture::striped(8);
18197        let dst = apart(&mut f, "src");
18198        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18199
18200        f.run(&[b"SET", src, b"v"]);
18201        f.run(&[b"EXPIRE", src, b"100"]);
18202        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18203        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18204        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18205        assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18206
18207        // A list, because a string lives in its record and a collection lives
18208        // in a slab, and the second of those is the one that can be left
18209        // behind. Planted through the store, since the list group has not been
18210        // taught about stripes yet.
18211        f.server
18212            .striped(0)
18213            .at(src)
18214            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18215            .expect("a new list");
18216        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18217        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18218        assert_eq!(
18219            f.server.striped(0).at(dst).llen(dst).expect("a list"),
18220            2,
18221            "the members are on the stripe the key moved to"
18222        );
18223
18224        // And `RENAMENX` still refuses a destination that is taken, which is
18225        // the one answer the cross stripe path has to work out for itself.
18226        f.run(&[b"SET", src, b"v"]);
18227        assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18228        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18229        assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18230    }
18231
18232    /// And a copy across two stripes leaves both keys behind it.
18233    #[test]
18234    fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18235        let mut f = Fixture::striped(8);
18236        let dst = apart(&mut f, "src");
18237        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18238
18239        f.run(&[b"SET", src, b"v"]);
18240        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18241        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18242        assert_eq!(
18243            f.run(&[b"COPY", src, dst]),
18244            ":0\r\n",
18245            "the destination is taken"
18246        );
18247        f.run(&[b"SET", src, b"w"]);
18248        assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18249        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18250
18251        // A collection is cloned rather than moved, so both keys have a body of
18252        // their own afterwards and writing to one does not show up in the
18253        // other.
18254        f.run(&[b"DEL", src, dst]);
18255        f.server
18256            .striped(0)
18257            .at(src)
18258            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18259            .expect("a new list");
18260        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18261        f.server
18262            .striped(0)
18263            .at(src)
18264            .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18265            .expect("a list that is there");
18266        assert_eq!(f.server.striped(0).at(src).llen(src).expect("a list"), 3);
18267        assert_eq!(f.server.striped(0).at(dst).llen(dst).expect("a list"), 2);
18268    }
18269
18270    /// Every bitmap command, on one stripe and on eight, replies compared byte
18271    /// for byte.
18272    ///
18273    /// `BITOP` is the one that names more than one key and it is where the work
18274    /// went. The rest are single key commands that now find their own stripe,
18275    /// and they are here because the cheapest way to be sure the routing is
18276    /// right is to ask.
18277    #[test]
18278    fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18279        let script: &[&[&[u8]]] = &[
18280            &[b"SET", b"k1", b"foobar"],
18281            &[b"SETBIT", b"b1", b"7", b"1"],
18282            &[b"SETBIT", b"b1", b"7", b"0"],
18283            &[b"GETBIT", b"k1", b"6"],
18284            &[b"GETBIT", b"k1", b"100"],
18285            &[b"BITCOUNT", b"k1"],
18286            &[b"BITCOUNT", b"k1", b"0", b"0"],
18287            &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18288            &[b"BITPOS", b"k1", b"1"],
18289            &[b"BITPOS", b"k1", b"0", b"2"],
18290            &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18291            &[
18292                b"BITFIELD",
18293                b"bf",
18294                b"SET",
18295                b"u8",
18296                b"0",
18297                b"255",
18298                b"GET",
18299                b"u8",
18300                b"0",
18301            ],
18302            &[
18303                b"BITFIELD",
18304                b"bf",
18305                b"OVERFLOW",
18306                b"SAT",
18307                b"INCRBY",
18308                b"u8",
18309                b"0",
18310                b"10",
18311            ],
18312            &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18313            // The multi key one, over sources that are not on one stripe unless
18314            // eight stripes have folded into one.
18315            &[b"SET", b"s1", b"abc"],
18316            &[b"SET", b"s2", b"abd"],
18317            &[b"SET", b"s3", b"a"],
18318            &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18319            &[b"GET", b"d1"],
18320            &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18321            &[b"GET", b"d2"],
18322            &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18323            &[b"STRLEN", b"d3"],
18324            &[b"BITOP", b"NOT", b"d4", b"s1"],
18325            &[b"STRLEN", b"d4"],
18326            &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18327            &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18328            &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18329            &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18330            // A source that is not there reads as empty, and a result with
18331            // nothing in it deletes the destination rather than writing one.
18332            &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18333            &[b"EXISTS", b"d1"],
18334            &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18335            &[b"GET", b"d9"],
18336            // And the errors, which have to be the same errors. The key that
18337            // is not a string is planted below rather than pushed here, since
18338            // the list group has not been taught about stripes yet.
18339            &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18340            &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18341            &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18342            &[b"BITOP", b"DIFF", b"d1", b"s1"],
18343            &[b"BITOP", b"NOPE", b"d1", b"s1"],
18344            &[b"BITCOUNT", b"list"],
18345            &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18346        ];
18347
18348        let mut one = Fixture::new();
18349        let mut many = Fixture::striped(8);
18350        for f in [&mut one, &mut many] {
18351            plant_list(f, b"list");
18352        }
18353        for parts in script {
18354            let a = one.run(parts);
18355            let b = many.run(parts);
18356            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18357        }
18358    }
18359
18360    /// A list under `key`, put there through the store.
18361    ///
18362    /// What a test does when it wants a key of the wrong type on a striped
18363    /// server, because the command that would make one is in a group that has
18364    /// not been taught about stripes yet.
18365    fn plant_list(f: &mut Fixture, key: &[u8]) {
18366        f.server
18367            .striped(0)
18368            .at(key)
18369            .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18370            .expect("a new list");
18371    }
18372
18373    /// A `BITOP` whose keys are on two stripes reads both of them.
18374    ///
18375    /// The test above spreads its keys by hashing and would still pass if one
18376    /// stripe were doing all the work, since the answers would be the same. This
18377    /// one puts the destination and the two sources where they are known not to
18378    /// share a stripe.
18379    #[test]
18380    fn a_bitop_across_stripes_reads_every_source() {
18381        let mut f = Fixture::striped(8);
18382        let other = apart(&mut f, "src");
18383        let (src, far) = (b"src".as_slice(), other.as_bytes());
18384        assert_ne!(
18385            f.server.striped(0).stripe_of(src),
18386            f.server.striped(0).stripe_of(far),
18387            "the two keys are the point of the test"
18388        );
18389
18390        f.run(&[b"SET", src, b"abc"]);
18391        f.run(&[b"SET", far, b"abd"]);
18392        assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18393        assert_eq!(
18394            f.run(&[b"GET", far]),
18395            "$3\r\nab`\r\n",
18396            "a destination that is also a source"
18397        );
18398        f.run(&[b"SET", far, b"abd"]);
18399        assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18400        assert_eq!(
18401            f.run(&[b"GET", src]),
18402            "$3\r\n\0\0\x07\r\n",
18403            "and the other way round"
18404        );
18405
18406        // A result of nothing deletes a destination on whatever stripe it is
18407        // on, and a source of the wrong type is refused before anything is
18408        // written.
18409        f.run(&[b"SET", src, b"abc"]);
18410        f.run(&[b"DEL", far]);
18411        assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18412        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18413        f.run(&[b"SET", src, b"abc"]);
18414        f.run(&[b"DEL", far]);
18415        plant_list(&mut f, far);
18416        assert_eq!(
18417            f.run(&[b"BITOP", b"OR", b"out", src, far]),
18418            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18419        );
18420        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18421    }
18422
18423    /// Every HyperLogLog command, on one stripe and on eight.
18424    #[test]
18425    fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18426        let script: &[&[&[u8]]] = &[
18427            &[b"PFADD", b"h1", b"a", b"b", b"c"],
18428            &[b"PFADD", b"h1", b"a"],
18429            &[b"PFADD", b"h2"],
18430            &[b"PFADD", b"h2", b"c", b"d", b"e"],
18431            &[b"PFCOUNT", b"h1"],
18432            &[b"PFCOUNT", b"h2"],
18433            &[b"PFCOUNT", b"missing"],
18434            // The two that name more than one key.
18435            &[b"PFCOUNT", b"h1", b"h2"],
18436            &[b"PFCOUNT", b"h1", b"missing"],
18437            &[b"PFMERGE", b"m", b"h1", b"h2"],
18438            &[b"PFCOUNT", b"m"],
18439            &[b"STRLEN", b"m"],
18440            &[b"PFMERGE", b"m"],
18441            &[b"PFCOUNT", b"m"],
18442            &[b"PFMERGE", b"m2", b"missing"],
18443            &[b"PFCOUNT", b"m2"],
18444            // The debugging ones, which are single key and change what they
18445            // look at.
18446            &[b"PFDEBUG", b"ENCODING", b"h1"],
18447            &[b"PFDEBUG", b"DECODE", b"h1"],
18448            &[b"PFDEBUG", b"TODENSE", b"h1"],
18449            &[b"PFDEBUG", b"ENCODING", b"h1"],
18450            &[b"PFDEBUG", b"TODENSE", b"h1"],
18451            &[b"PFCOUNT", b"h1", b"h2"],
18452            &[b"PFSELFTEST"],
18453            // And the errors.
18454            &[b"SET", b"plain", b"not a sketch at all"],
18455            &[b"PFADD", b"plain", b"a"],
18456            &[b"PFCOUNT", b"plain"],
18457            &[b"PFCOUNT", b"h1", b"plain"],
18458            &[b"PFMERGE", b"plain", b"h1"],
18459            &[b"PFMERGE", b"m", b"plain"],
18460            &[b"PFDEBUG", b"ENCODING", b"gone"],
18461            &[b"PFDEBUG", b"NOPE", b"h1"],
18462        ];
18463
18464        let mut one = Fixture::new();
18465        let mut many = Fixture::striped(8);
18466        for parts in script {
18467            let a = one.run(parts);
18468            let b = many.run(parts);
18469            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18470        }
18471    }
18472
18473    /// Every set command, on one stripe and on eight.
18474    ///
18475    /// The commands that answer members answer them in whatever order the set
18476    /// or the table they were built in holds them, so those replies are
18477    /// compared as sets. Everything else is compared byte for byte. Two servers
18478    /// agreeing on the order would be a fact about the tables and not about the
18479    /// answer, and asserting it would make this test fail for a reason nobody
18480    /// cares about.
18481    #[test]
18482    fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18483        const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18484        let script: &[&[&[u8]]] = &[
18485            &[b"SADD", b"s1", b"a", b"b", b"c"],
18486            &[b"SADD", b"s1", b"a"],
18487            &[b"SADD", b"s2", b"b", b"c", b"d"],
18488            &[b"SADD", b"ints", b"1", b"2", b"3"],
18489            &[b"SCARD", b"s1"],
18490            &[b"SISMEMBER", b"s1", b"a"],
18491            &[b"SISMEMBER", b"s1", b"z"],
18492            &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18493            &[b"SMEMBERS", b"s1"],
18494            &[b"SREM", b"s1", b"c"],
18495            &[b"SADD", b"s1", b"c"],
18496            &[b"SSCAN", b"s1", b"0"],
18497            &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18498            // The two draws, on a set of one member, which is the only shape
18499            // whose answer two servers have to agree on.
18500            &[b"SADD", b"one", b"m"],
18501            &[b"SRANDMEMBER", b"one"],
18502            &[b"SRANDMEMBER", b"one", b"-3"],
18503            &[b"SRANDMEMBER", b"gone"],
18504            &[b"SPOP", b"one"],
18505            &[b"SPOP", b"one"],
18506            &[b"SPOP", b"gone", b"2"],
18507            // The one that names two keys.
18508            &[b"SMOVE", b"s1", b"s2", b"a"],
18509            &[b"SMOVE", b"s1", b"s2", b"zzz"],
18510            &[b"SMOVE", b"gone", b"s2", b"a"],
18511            &[b"SMEMBERS", b"s1"],
18512            &[b"SMEMBERS", b"s2"],
18513            // The algebra.
18514            &[b"SINTER", b"s1", b"s2"],
18515            &[b"SUNION", b"s1", b"s2"],
18516            &[b"SDIFF", b"s2", b"s1"],
18517            &[b"SINTER", b"s1", b"gone"],
18518            &[b"SUNION", b"s1", b"gone"],
18519            &[b"SDIFF", b"gone", b"s1"],
18520            &[b"SINTER", b"ints", b"s1"],
18521            &[b"SINTERCARD", b"2", b"s1", b"s2"],
18522            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
18523            &[b"SUNIONCARD", b"2", b"s1", b"s2"],
18524            &[b"SDIFFCARD", b"2", b"s2", b"s1"],
18525            &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
18526            &[b"SMEMBERS", b"d1"],
18527            &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
18528            &[b"SCARD", b"d2"],
18529            &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
18530            &[b"SCARD", b"d3"],
18531            // An empty result deletes the destination rather than storing a
18532            // set with nothing in it.
18533            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18534            &[b"EXISTS", b"d4"],
18535            // And a destination that is also a source.
18536            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18537            &[b"SCARD", b"s2"],
18538            // The errors, which have to be the same errors.
18539            &[b"SET", b"str", b"v"],
18540            &[b"SADD", b"str", b"a"],
18541            &[b"SINTER", b"s1", b"str"],
18542            &[b"SINTERSTORE", b"d5", b"s1", b"str"],
18543            &[b"EXISTS", b"d5"],
18544            &[b"SMOVE", b"str", b"s2", b"a"],
18545            &[b"SMOVE", b"s1", b"str", b"b"],
18546            &[b"SMOVE", b"gone", b"str", b"b"],
18547            &[b"SINTERCARD", b"0", b"s1"],
18548            &[b"SINTERCARD", b"3", b"s1", b"s2"],
18549            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
18550            &[b"SPOP", b"s1", b"-1"],
18551        ];
18552
18553        let mut one = Fixture::new();
18554        let mut many = Fixture::striped(8);
18555        for parts in script {
18556            let a = one.run(parts);
18557            let b = many.run(parts);
18558            let name = String::from_utf8_lossy(parts[0]).to_uppercase();
18559            if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
18560                assert_eq!(sorted(&a), sorted(&b), "{name}");
18561            } else {
18562                assert_eq!(a, b, "{name}");
18563            }
18564        }
18565    }
18566
18567    /// The algebra over sets that are known to be on different stripes.
18568    #[test]
18569    fn a_set_operation_across_stripes_reads_every_set() {
18570        let mut f = Fixture::striped(8);
18571        let second = apart(&mut f, "s1");
18572        let third = apart(&mut f, &second);
18573        let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
18574
18575        f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
18576        f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
18577        assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
18578        assert_eq!(
18579            sorted(&f.run(&[b"SUNION", s1, s2])),
18580            ["a", "b", "c", "d"],
18581            "a union of two stripes is both of them"
18582        );
18583        assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
18584        assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
18585        assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
18586        assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
18587
18588        // A destination on a third stripe, and then one that is also a source.
18589        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
18590        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
18591        assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
18592        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
18593        assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
18594        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
18595
18596        // An empty result deletes a destination wherever it is, and a key of
18597        // the wrong type stops the command before the destination is touched.
18598        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
18599        assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
18600        f.run(&[b"SET", s3, b"v"]);
18601        assert_eq!(
18602            f.run(&[b"SINTER", s1, s3]),
18603            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18604        );
18605        assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
18606    }
18607
18608    /// An `SMOVE` whose two keys are on two stripes.
18609    #[test]
18610    fn a_move_across_stripes_takes_the_member_with_it() {
18611        let mut f = Fixture::striped(8);
18612        let other = apart(&mut f, "src");
18613        let (src, dst) = (b"src".as_slice(), other.as_bytes());
18614
18615        f.run(&[b"SADD", src, b"a", b"b"]);
18616        f.run(&[b"SADD", dst, b"c"]);
18617        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
18618        assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
18619        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
18620        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
18621
18622        // A destination that is not there is created on its own stripe, and a
18623        // source that loses its last member is deleted from its own.
18624        f.run(&[b"DEL", dst]);
18625        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
18626        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
18627        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
18628
18629        // And a source that is not there answers zero without ever asking what
18630        // the destination holds, which is Redis's order and not the obvious
18631        // one.
18632        f.run(&[b"SET", dst, b"v"]);
18633        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
18634        f.run(&[b"SADD", src, b"b"]);
18635        assert_eq!(
18636            f.run(&[b"SMOVE", src, dst, b"b"]),
18637            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18638        );
18639    }
18640
18641    /// A count and a merge over sketches that are known to be on two stripes.
18642    #[test]
18643    fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
18644        let mut f = Fixture::striped(8);
18645        let other = apart(&mut f, "src");
18646        let (src, far) = (b"src".as_slice(), other.as_bytes());
18647
18648        for i in 0..150 {
18649            let ele = format!("e:{i}");
18650            f.run(&[b"PFADD", src, ele.as_bytes()]);
18651        }
18652        for i in 150..200 {
18653            let ele = format!("e:{i}");
18654            f.run(&[b"PFADD", far, ele.as_bytes()]);
18655        }
18656        // The three numbers a real server gives for these elements, which are
18657        // the numbers the single stripe tests in the keyspace crate check too.
18658        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
18659        assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
18660        assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
18661
18662        // A merge whose destination is on a third stripe, and then one that
18663        // writes into a source.
18664        let dest = apart(&mut f, &other);
18665        assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
18666        assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
18667        assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
18668        assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
18669        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
18670    }
18671
18672    /// Every sorted set command, on one stripe and on eight.
18673    ///
18674    /// Every reply here is compared byte for byte, unlike the set group, because
18675    /// a sorted set answers in rank order and members sharing a score come out
18676    /// in the order of their bytes. There is nothing left for the table the
18677    /// answer was built in to decide.
18678    #[test]
18679    fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
18680        let script: &[&[&[u8]]] = &[
18681            &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
18682            &[b"ZADD", b"z1", b"NX", b"9", b"a"],
18683            &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
18684            &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
18685            &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
18686            &[b"ZINCRBY", b"z1", b"1.5", b"b"],
18687            &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
18688            &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
18689            &[b"ZADD", b"one", b"1", b"m"],
18690            &[b"ZCARD", b"z1"],
18691            &[b"ZCARD", b"gone"],
18692            &[b"ZSCORE", b"z1", b"a"],
18693            &[b"ZSCORE", b"z1", b"zz"],
18694            &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
18695            &[b"ZRANK", b"z1", b"c"],
18696            &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
18697            &[b"ZREVRANK", b"z1", b"c"],
18698            &[b"ZRANK", b"z1", b"gone"],
18699            &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
18700            &[b"ZCOUNT", b"z1", b"(1", b"3"],
18701            &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
18702            // The range commands, which are one parse and one walk.
18703            &[b"ZRANGE", b"z1", b"0", b"-1"],
18704            &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
18705            &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
18706            &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
18707            &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
18708            &[b"ZREVRANGE", b"z1", b"0", b"-1"],
18709            &[
18710                b"ZRANGEBYSCORE",
18711                b"z1",
18712                b"-inf",
18713                b"+inf",
18714                b"LIMIT",
18715                b"1",
18716                b"1",
18717            ],
18718            &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
18719            &[b"ZSCAN", b"z1", b"0"],
18720            &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
18721            // The draw, on a sorted set of one member, which is the only shape
18722            // whose answer two servers have to agree on.
18723            &[b"ZRANDMEMBER", b"one"],
18724            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
18725            &[b"ZRANDMEMBER", b"gone"],
18726            // The one that copies a window into another key.
18727            &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
18728            &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
18729            &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
18730            &[b"EXISTS", b"d0"],
18731            // The algebra, in both its shapes.
18732            &[b"ZUNION", b"2", b"z1", b"z2"],
18733            &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
18734            &[
18735                b"ZUNION",
18736                b"2",
18737                b"z1",
18738                b"z2",
18739                b"WEIGHTS",
18740                b"2",
18741                b"3",
18742                b"AGGREGATE",
18743                b"MAX",
18744                b"WITHSCORES",
18745            ],
18746            &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
18747            &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
18748            &[b"ZDIFF", b"2", b"gone", b"z1"],
18749            &[b"ZINTERCARD", b"2", b"z1", b"z2"],
18750            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
18751            &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
18752            &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
18753            &[
18754                b"ZINTERSTORE",
18755                b"d2",
18756                b"2",
18757                b"z1",
18758                b"z2",
18759                b"AGGREGATE",
18760                b"MIN",
18761            ],
18762            &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
18763            &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
18764            &[b"ZCARD", b"d3"],
18765            // An empty result deletes the destination rather than storing a
18766            // sorted set with nothing in it.
18767            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
18768            &[b"EXISTS", b"d4"],
18769            // A plain set is a sorted set where every score is one, so it is a
18770            // legal input to all of these.
18771            &[b"SADD", b"plain", b"a", b"x"],
18772            &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
18773            &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
18774            // And a destination that is also a source.
18775            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
18776            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
18777            // The three removals and the two pops.
18778            &[b"ZREM", b"d5", b"x", b"nothere"],
18779            &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
18780            &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
18781            &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
18782            &[b"ZPOPMIN", b"z1"],
18783            &[b"ZPOPMAX", b"z1", b"2"],
18784            &[b"ZPOPMIN", b"gone"],
18785            &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
18786            &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
18787            // The errors, which have to be the same errors.
18788            &[b"SET", b"str", b"v"],
18789            &[b"ZADD", b"str", b"1", b"a"],
18790            &[b"ZSCORE", b"str", b"a"],
18791            &[b"ZADD", b"z1", b"nan", b"a"],
18792            &[b"ZUNION", b"2", b"z1", b"str"],
18793            &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
18794            &[b"EXISTS", b"d6"],
18795            &[b"ZINTERCARD", b"0", b"z1"],
18796            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
18797            &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
18798            &[b"ZMPOP", b"1", b"str", b"MIN"],
18799            &[b"ZPOPMIN", b"z1", b"-1"],
18800        ];
18801
18802        let mut one = Fixture::new();
18803        let mut many = Fixture::striped(8);
18804        for parts in script {
18805            let a = one.run(parts);
18806            let b = many.run(parts);
18807            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18808        }
18809    }
18810
18811    /// The algebra over sorted sets that are known to be on different stripes.
18812    #[test]
18813    fn a_sorted_set_operation_across_stripes_reads_every_input() {
18814        let mut f = Fixture::striped(8);
18815        let second = apart(&mut f, "z1");
18816        let third = apart(&mut f, &second);
18817        let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
18818
18819        f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
18820        f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
18821        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
18822        // come out in and the answer that says both stripes were read.
18823        assert_eq!(
18824            f.run(&[b"ZUNION", b"2", z1, z2]),
18825            "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
18826        );
18827        assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
18828        assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
18829        assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
18830        assert_eq!(
18831            f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
18832            ":1\r\n"
18833        );
18834
18835        // A destination on a third stripe, and the weights and the aggregate
18836        // reaching every input.
18837        assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
18838        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
18839        assert_eq!(
18840            f.run(&[
18841                b"ZUNIONSTORE",
18842                z3,
18843                b"2",
18844                z1,
18845                z2,
18846                b"WEIGHTS",
18847                b"2",
18848                b"3",
18849                b"AGGREGATE",
18850                b"MAX"
18851            ]),
18852            ":3\r\n"
18853        );
18854        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
18855        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
18856        assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
18857        assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
18858        assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
18859
18860        // A pop over keys on several stripes takes from the first one that has
18861        // anything, which is what makes the order of the keys matter.
18862        let popped = format!(
18863            "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
18864            second.len()
18865        );
18866        assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
18867        f.run(&[b"ZADD", z2, b"3", b"b"]);
18868
18869        // An empty result deletes a destination wherever it is, and an input of
18870        // the wrong type stops the command before the destination is touched.
18871        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
18872        assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
18873        f.run(&[b"SET", z3, b"v"]);
18874        assert_eq!(
18875            f.run(&[b"ZUNION", b"2", z1, z3]),
18876            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18877        );
18878        assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
18879
18880        // And a destination that is also a source works across stripes for the
18881        // reason it works on one: the whole result is built before anything is
18882        // written.
18883        assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
18884        assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
18885        assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
18886    }
18887
18888    /// A `ZRANGESTORE` whose two keys are on two stripes.
18889    #[test]
18890    fn a_range_store_across_stripes_copies_the_window() {
18891        let mut f = Fixture::striped(8);
18892        let other = apart(&mut f, "src");
18893        let third = apart(&mut f, &other);
18894        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
18895
18896        f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
18897        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
18898        assert_eq!(
18899            f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
18900            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
18901        );
18902        assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
18903
18904        // A window walked backwards takes the other end of the sorted set and
18905        // still stores what it took in score order.
18906        assert_eq!(
18907            f.run(&[
18908                b"ZRANGESTORE",
18909                dst,
18910                src,
18911                b"+inf",
18912                b"-inf",
18913                b"BYSCORE",
18914                b"REV",
18915                b"LIMIT",
18916                b"0",
18917                b"2"
18918            ]),
18919            ":2\r\n"
18920        );
18921        assert_eq!(
18922            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
18923            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
18924        );
18925
18926        // An empty window deletes the destination on its own stripe, and a
18927        // source of the wrong type is refused before the destination is touched.
18928        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
18929        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
18930        f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
18931        f.run(&[b"SET", plain, b"v"]);
18932        assert_eq!(
18933            f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
18934            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18935        );
18936        assert_eq!(
18937            f.run(&[b"ZCARD", dst]),
18938            ":3\r\n",
18939            "and left the destination"
18940        );
18941    }
18942
18943    /// Every list command, on one stripe and on eight.
18944    ///
18945    /// The blocking six are in here too, both when they can be answered on the
18946    /// spot and when they cannot, since a command that parks its client writes
18947    /// nothing at all and two servers have to agree about that as much as they
18948    /// agree about a reply.
18949    #[test]
18950    fn the_list_group_answers_the_same_however_many_stripes_there_are() {
18951        let script: &[&[&[u8]]] = &[
18952            &[b"RPUSH", b"l1", b"a", b"b", b"c"],
18953            &[b"LPUSH", b"l1", b"z"],
18954            &[b"RPUSHX", b"l1", b"d"],
18955            &[b"LPUSHX", b"gone", b"x"],
18956            &[b"RPUSHX", b"gone", b"x"],
18957            &[b"LLEN", b"l1"],
18958            &[b"LLEN", b"gone"],
18959            &[b"LRANGE", b"l1", b"0", b"-1"],
18960            &[b"LRANGE", b"l1", b"1", b"2"],
18961            &[b"LRANGE", b"l1", b"5", b"9"],
18962            &[b"LINDEX", b"l1", b"0"],
18963            &[b"LINDEX", b"l1", b"-1"],
18964            &[b"LINDEX", b"l1", b"99"],
18965            &[b"LSET", b"l1", b"0", b"y"],
18966            &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
18967            &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
18968            &[b"LPOS", b"l1", b"b"],
18969            &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
18970            &[b"LPOS", b"l1", b"nothere"],
18971            &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
18972            &[b"LREM", b"l1", b"1", b"aa"],
18973            &[b"LTRIM", b"l1", b"0", b"3"],
18974            &[b"LRANGE", b"l1", b"0", b"-1"],
18975            &[b"LPOP", b"l1"],
18976            &[b"RPOP", b"l1"],
18977            &[b"LPOP", b"l1", b"2"],
18978            &[b"LPOP", b"gone"],
18979            &[b"LPOP", b"gone", b"2"],
18980            &[b"EXISTS", b"l1"],
18981            // The ones that name two keys, and the one that takes a block of
18982            // elements rather than the one on the end.
18983            &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
18984            &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
18985            &[b"RPOPLPUSH", b"src", b"dst"],
18986            &[b"LRANGE", b"dst", b"0", b"-1"],
18987            &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
18988            &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
18989            &[
18990                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
18991            ],
18992            &[
18993                b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
18994            ],
18995            &[b"LRANGE", b"dst", b"0", b"-1"],
18996            &[
18997                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
18998            ],
18999            &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19000            &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19001            &[b"LMPOP", b"1", b"gone", b"LEFT"],
19002            // The blocking ones, first with something there to answer them and
19003            // then with nothing, which parks the client and writes nothing.
19004            &[b"RPUSH", b"q", b"a", b"b", b"c"],
19005            &[b"BLPOP", b"gone", b"q", b"0"],
19006            &[b"BRPOP", b"q", b"0"],
19007            &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19008            &[b"RPUSH", b"q", b"x", b"y", b"z"],
19009            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19010            &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19011            &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19012            &[b"BLPOP", b"q", b"0"],
19013            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19014            // The errors, which have to be the same errors.
19015            &[b"SET", b"plain", b"v"],
19016            &[b"LPUSH", b"plain", b"a"],
19017            &[b"LLEN", b"plain"],
19018            &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19019            &[b"LRANGE", b"dst", b"0", b"-1"],
19020            &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19021            &[b"LSET", b"gone", b"0", b"v"],
19022            &[b"LSET", b"dst", b"99", b"v"],
19023            &[b"LPOP", b"dst", b"-1"],
19024            &[b"LMPOP", b"0", b"dst", b"LEFT"],
19025            &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19026        ];
19027
19028        let mut one = Fixture::new();
19029        let mut many = Fixture::striped(8);
19030        for parts in script {
19031            let a = one.run(parts);
19032            let b = many.run(parts);
19033            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19034        }
19035    }
19036
19037    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
19038    #[test]
19039    fn a_list_move_across_stripes_takes_the_elements_with_it() {
19040        let mut f = Fixture::striped(8);
19041        let other = apart(&mut f, "src");
19042        let third = apart(&mut f, &other);
19043        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19044
19045        f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19046        assert_eq!(
19047            f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19048            "$1\r\na\r\n"
19049        );
19050        assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19051        assert_eq!(
19052            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19053            "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19054            "one went on each end of the destination"
19055        );
19056        assert_eq!(
19057            f.run(&[b"LRANGE", src, b"0", b"-1"]),
19058            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19059        );
19060
19061        // A block of them, which under BULK arrives in the order it left.
19062        assert_eq!(
19063            f.run(&[
19064                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19065            ]),
19066            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19067        );
19068        assert_eq!(
19069            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19070            "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19071        );
19072        assert_eq!(
19073            f.run(&[b"EXISTS", src]),
19074            ":0\r\n",
19075            "and the source is gone with its last element"
19076        );
19077
19078        // An `EXACTLY` the source cannot fill moves nothing, and a source that
19079        // is not there at all is the two kinds of nothing the two commands have.
19080        f.run(&[b"RPUSH", src, b"e", b"f"]);
19081        assert_eq!(
19082            f.run(&[
19083                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19084            ]),
19085            "*-1\r\n"
19086        );
19087        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19088        assert_eq!(
19089            f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19090            "$-1\r\n"
19091        );
19092        assert_eq!(
19093            f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19094            "*-1\r\n"
19095        );
19096
19097        // A destination of the wrong type is refused before anything is taken,
19098        // which is the order that matters most here, since an element already
19099        // out of the source would have nowhere to go back to.
19100        f.run(&[b"SET", plain, b"v"]);
19101        assert_eq!(
19102            f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19103            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19104        );
19105        assert_eq!(
19106            f.run(&[b"LLEN", src]),
19107            ":2\r\n",
19108            "and left the source alone"
19109        );
19110        assert_eq!(
19111            f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19112            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19113        );
19114        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19115    }
19116
19117    /// A parked client served by a push that landed on another stripe.
19118    ///
19119    /// A waiter remembers the database and not the stripe, which is the point:
19120    /// serving it runs the same attempt the command ran, and the attempt finds
19121    /// the stripe each of its keys is on for itself.
19122    #[test]
19123    fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19124        let mut f = Fixture::striped(8);
19125        let other = apart(&mut f, "q");
19126        let (q, far) = (b"q".as_slice(), other.as_bytes());
19127
19128        assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19129        assert_eq!(f.server.waiters().len(), 1);
19130        f.run(&[b"RPUSH", far, b"v"]);
19131        let mut out = Out::new(Proto::Resp2);
19132        assert!(f.server.serve_waiter(0, 0, &mut out));
19133        let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19134        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19135        assert_eq!(
19136            f.run(&[b"EXISTS", far]),
19137            ":0\r\n",
19138            "and it took the element with it"
19139        );
19140
19141        // And a move across two stripes is served the same way, by the push
19142        // that fills its source.
19143        f.server.waiters_mut().forget(7);
19144        assert_eq!(
19145            f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19146            Flow::Block
19147        );
19148        f.run(&[b"RPUSH", q, b"w"]);
19149        let mut out = Out::new(Proto::Resp2);
19150        assert!(f.server.serve_waiter(0, 0, &mut out));
19151        assert_eq!(
19152            core::str::from_utf8(out.as_slice()).expect("ascii"),
19153            "$1\r\nw\r\n"
19154        );
19155        assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19156    }
19157
19158    /// Every stream command, on one stripe and on eight.
19159    ///
19160    /// Every ID is written out rather than left to the clock, so the two servers
19161    /// are being compared on what they store and not on how long the test took
19162    /// to get from one of them to the other.
19163    #[test]
19164    fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19165        let script: &[&[&[u8]]] = &[
19166            &[b"XADD", b"s", b"1-1", b"a", b"1"],
19167            &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19168            &[b"XADD", b"s", b"3-1", b"d", b"4"],
19169            &[b"XADD", b"s", b"1-1", b"e", b"5"],
19170            &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19171            &[b"XLEN", b"s"],
19172            &[b"XLEN", b"gone"],
19173            &[b"XRANGE", b"s", b"-", b"+"],
19174            &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19175            &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19176            &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19177            &[b"XREVRANGE", b"s", b"+", b"-"],
19178            &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19179            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19180            &[b"XREAD", b"STREAMS", b"s", b"$"],
19181            // The groups, which is where most of the state is.
19182            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19183            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19184            &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19185            &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19186            &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19187            &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19188            &[
19189                b"XREADGROUP",
19190                b"GROUP",
19191                b"g",
19192                b"c1",
19193                b"COUNT",
19194                b"1",
19195                b"STREAMS",
19196                b"s",
19197                b"0",
19198            ],
19199            &[
19200                b"XREADGROUP",
19201                b"GROUP",
19202                b"nope",
19203                b"c1",
19204                b"STREAMS",
19205                b"s",
19206                b">",
19207            ],
19208            &[b"XPENDING", b"s", b"g"],
19209            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19210            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19211            &[b"XPENDING", b"s", b"nope"],
19212            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19213            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19214            &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19215            &[b"XACK", b"s", b"g", b"1-1"],
19216            &[b"XACK", b"s", b"g", b"1-1"],
19217            &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19218            &[b"XPENDING", b"s", b"g"],
19219            &[b"XINFO", b"STREAM", b"s"],
19220            &[b"XINFO", b"GROUPS", b"s"],
19221            &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19222            &[b"XINFO", b"STREAM", b"gone"],
19223            // Deleting, trimming and moving the ID on.
19224            &[b"XDEL", b"s", b"3-1"],
19225            &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19226            &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19227            &[b"XADD", b"s", b"9-1", b"z", b"9"],
19228            &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19229            &[b"XTRIM", b"s", b"MINID", b"9"],
19230            &[b"XSETID", b"s", b"99-1"],
19231            &[b"XSETID", b"s", b"1-1"],
19232            &[b"XLEN", b"s"],
19233            &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19234            &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19235            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19236            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19237            // And the errors.
19238            &[b"SET", b"plain", b"v"],
19239            &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19240            &[b"XLEN", b"plain"],
19241            &[b"XREAD", b"STREAMS", b"plain", b"0"],
19242            &[b"XRANGE", b"s", b"bogus", b"+"],
19243            &[b"XADD", b"s", b"1-1", b"a"],
19244            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19245            &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19246        ];
19247
19248        let mut one = Fixture::new();
19249        let mut many = Fixture::striped(8);
19250        for parts in script {
19251            let a = one.run(parts);
19252            let b = many.run(parts);
19253            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19254        }
19255    }
19256
19257    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
19258    ///
19259    /// Nothing is shared between the two streams, so the only thing this can go
19260    /// wrong at is looking both of them up, which is exactly what a read that
19261    /// held one database and walked it would get wrong.
19262    #[test]
19263    fn a_stream_read_across_stripes_reads_every_key() {
19264        let mut f = Fixture::striped(8);
19265        let other = apart(&mut f, "s1");
19266        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19267
19268        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19269        f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19270        let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19271        assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19272        assert!(got.contains("1-1"), "the first one is in there: {got}");
19273        assert!(got.contains("2-1"), "and so is the second: {got}");
19274
19275        // A group read looks its group up on every key before it reads any of
19276        // them, so a group that is missing on the far key stops the near one.
19277        f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19278        let got = f.run(&[
19279            b"XREADGROUP",
19280            b"GROUP",
19281            b"g",
19282            b"c",
19283            b"STREAMS",
19284            s1,
19285            s2,
19286            b">",
19287            b">",
19288        ]);
19289        assert!(got.starts_with("-NOGROUP"), "{got}");
19290        assert_eq!(
19291            f.run(&[b"XPENDING", s1, b"g"]),
19292            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19293            "and read nothing from the key that did have the group"
19294        );
19295
19296        f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19297        let got = f.run(&[
19298            b"XREADGROUP",
19299            b"GROUP",
19300            b"g",
19301            b"c",
19302            b"STREAMS",
19303            s1,
19304            s2,
19305            b">",
19306            b">",
19307        ]);
19308        assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19309    }
19310
19311    /// A client parked on an `XREAD` woken by an entry on another stripe.
19312    #[test]
19313    fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19314        let mut f = Fixture::striped(8);
19315        let other = apart(&mut f, "s1");
19316        let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19317        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19318        f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19319
19320        assert_eq!(
19321            f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19322                .0,
19323            Flow::Block
19324        );
19325        f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19326        let mut out = Out::new(Proto::Resp2);
19327        assert!(f.server.serve_waiter(0, 0, &mut out));
19328        let want = format!(
19329            "*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",
19330            other.len()
19331        );
19332        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19333    }
19334
19335    /// Every JSON command, on one stripe and on eight.
19336    #[test]
19337    fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19338        let script: &[&[&[u8]]] = &[
19339            &[
19340                b"JSON.SET",
19341                b"d",
19342                b"$",
19343                br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19344            ],
19345            &[b"JSON.SET", b"d", b"$.a", b"2"],
19346            &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19347            &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19348            &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19349            &[b"JSON.GET", b"d"],
19350            &[b"JSON.GET", b"d", b"$.b"],
19351            &[b"JSON.GET", b"gone", b"$"],
19352            &[b"JSON.TYPE", b"d", b"$.b"],
19353            &[b"JSON.TYPE", b"d", b"$.s"],
19354            &[b"JSON.TOGGLE", b"d", b"$.t"],
19355            &[b"JSON.ARRLEN", b"d", b"$.b"],
19356            &[b"JSON.OBJLEN", b"d", b"$"],
19357            &[b"JSON.OBJKEYS", b"d", b"$"],
19358            &[b"JSON.STRLEN", b"d", b"$.s"],
19359            &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19360            &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19361            &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19362            &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19363            &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19364            &[b"JSON.ARRPOP", b"d", b"$.b"],
19365            &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19366            &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19367            &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19368            &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19369            &[b"JSON.RESP", b"d", b"$.b"],
19370            &[b"JSON.DEBUG", b"MEMORY", b"d"],
19371            &[b"JSON.CLEAR", b"d", b"$.b"],
19372            &[b"JSON.DEL", b"d", b"$.m"],
19373            &[b"JSON.FORGET", b"d", b"$.nothere"],
19374            // The two that name more than one key.
19375            &[
19376                b"JSON.MSET",
19377                b"m1",
19378                b"$",
19379                b"1",
19380                b"m2",
19381                b"$",
19382                b"2",
19383                b"m3",
19384                b"$",
19385                b"3",
19386            ],
19387            &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19388            &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19389            &[b"JSON.GET", b"m1", b"$"],
19390            &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19391            &[b"JSON.GET", b"m2", b"$"],
19392            // And the errors.
19393            &[b"SET", b"plain", b"v"],
19394            &[b"JSON.GET", b"plain", b"$"],
19395            &[b"JSON.SET", b"plain", b"$", b"1"],
19396            &[b"JSON.MGET", b"m1", b"plain", b"$"],
19397            &[b"JSON.SET", b"d", b"$.b", b"["],
19398            &[b"JSON.DEL", b"plain"],
19399        ];
19400
19401        let mut one = Fixture::new();
19402        let mut many = Fixture::striped(8);
19403        for parts in script {
19404            let a = one.run(parts);
19405            let b = many.run(parts);
19406            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19407        }
19408    }
19409
19410    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
19411    ///
19412    /// `JSON.MSET` works every triple out against the keyspace as it was before
19413    /// the command and writes nothing until all of them are known to work, so
19414    /// the thing to check is that a triple that cannot be written stops the
19415    /// ones on other stripes as well as the ones on its own.
19416    #[test]
19417    fn a_json_multi_write_across_stripes_reaches_every_key() {
19418        let mut f = Fixture::striped(8);
19419        let second = apart(&mut f, "m1");
19420        let third = apart(&mut f, &second);
19421        let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19422
19423        assert_eq!(
19424            f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19425            "+OK\r\n"
19426        );
19427        assert_eq!(
19428            f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19429            "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19430        );
19431
19432        // A value that is not JSON is refused before anything is written, and
19433        // the key on the far stripe keeps what it had.
19434        assert_eq!(
19435            f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19436            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19437        );
19438        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19439
19440        // A path that names nowhere is not an error. That triple is skipped,
19441        // the ones on the other stripes are still written, and the reply is a
19442        // nil rather than OK.
19443        assert_eq!(
19444            f.run(&[
19445                b"JSON.MSET",
19446                m1,
19447                b"$",
19448                b"9",
19449                m2,
19450                b"$.deep",
19451                b"9",
19452                m3,
19453                b"$",
19454                b"7"
19455            ]),
19456            "$-1\r\n"
19457        );
19458        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19459        assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19460        assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19461    }
19462
19463    /// Every geospatial command, on one stripe and on eight.
19464    #[test]
19465    fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19466        let script: &[&[&[u8]]] = &[
19467            &[
19468                b"GEOADD",
19469                b"g",
19470                b"13.361389",
19471                b"38.115556",
19472                b"palermo",
19473                b"15.087269",
19474                b"37.502669",
19475                b"catania",
19476            ],
19477            &[
19478                b"GEOADD",
19479                b"g",
19480                b"NX",
19481                b"13.361389",
19482                b"38.115556",
19483                b"palermo",
19484            ],
19485            &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19486            &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19487            &[b"GEOHASH", b"g", b"palermo", b"catania"],
19488            &[b"GEODIST", b"g", b"palermo", b"catania"],
19489            &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19490            &[b"GEODIST", b"g", b"palermo", b"nothere"],
19491            &[
19492                b"GEOSEARCH",
19493                b"g",
19494                b"FROMLONLAT",
19495                b"15",
19496                b"37",
19497                b"BYRADIUS",
19498                b"200",
19499                b"KM",
19500                b"ASC",
19501                b"WITHCOORD",
19502                b"WITHDIST",
19503                b"WITHHASH",
19504            ],
19505            &[
19506                b"GEOSEARCH",
19507                b"g",
19508                b"FROMMEMBER",
19509                b"palermo",
19510                b"BYBOX",
19511                b"400",
19512                b"400",
19513                b"KM",
19514                b"DESC",
19515            ],
19516            &[
19517                b"GEORADIUS",
19518                b"g",
19519                b"15",
19520                b"37",
19521                b"200",
19522                b"KM",
19523                b"COUNT",
19524                b"1",
19525            ],
19526            &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
19527            &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
19528            &[
19529                b"GEOSEARCHSTORE",
19530                b"dst",
19531                b"g",
19532                b"FROMLONLAT",
19533                b"15",
19534                b"37",
19535                b"BYRADIUS",
19536                b"200",
19537                b"KM",
19538            ],
19539            &[b"ZRANGE", b"dst", b"0", b"-1"],
19540            &[
19541                b"GEOSEARCHSTORE",
19542                b"dst",
19543                b"g",
19544                b"FROMLONLAT",
19545                b"15",
19546                b"37",
19547                b"BYRADIUS",
19548                b"1",
19549                b"M",
19550                b"STOREDIST",
19551            ],
19552            &[b"EXISTS", b"dst"],
19553            &[
19554                b"GEORADIUS",
19555                b"g",
19556                b"15",
19557                b"37",
19558                b"200",
19559                b"KM",
19560                b"STORE",
19561                b"dst",
19562            ],
19563            &[b"ZCARD", b"dst"],
19564            // And the errors.
19565            &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
19566            &[b"SET", b"plain", b"v"],
19567            &[b"GEOPOS", b"plain", b"a"],
19568            &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
19569            &[
19570                b"GEOSEARCHSTORE",
19571                b"dst",
19572                b"g",
19573                b"FROMLONLAT",
19574                b"15",
19575                b"37",
19576                b"BYRADIUS",
19577                b"200",
19578                b"KM",
19579                b"WITHCOORD",
19580            ],
19581        ];
19582
19583        let mut one = Fixture::new();
19584        let mut many = Fixture::striped(8);
19585        for parts in script {
19586            let a = one.run(parts);
19587            let b = many.run(parts);
19588            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19589        }
19590    }
19591
19592    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
19593    #[test]
19594    fn a_geo_search_store_across_stripes_writes_what_it_found() {
19595        let mut f = Fixture::striped(8);
19596        let other = apart(&mut f, "g");
19597        let third = apart(&mut f, &other);
19598        let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
19599
19600        f.run(&[
19601            b"GEOADD",
19602            g,
19603            b"13.361389",
19604            b"38.115556",
19605            b"palermo",
19606            b"15.087269",
19607            b"37.502669",
19608            b"catania",
19609        ]);
19610        assert_eq!(
19611            f.run(&[
19612                b"GEOSEARCHSTORE",
19613                dst,
19614                g,
19615                b"FROMLONLAT",
19616                b"15",
19617                b"37",
19618                b"BYRADIUS",
19619                b"200",
19620                b"KM",
19621                b"ASC",
19622            ]),
19623            ":2\r\n"
19624        );
19625        assert_eq!(
19626            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19627            "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
19628            "the geohash is the score, so the order is not the search order"
19629        );
19630        assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
19631
19632        // `STOREDIST` stores the distance in the unit the search was asked in,
19633        // which is the destination stripe's sorted set and not the source's.
19634        assert_eq!(
19635            f.run(&[
19636                b"GEOSEARCHSTORE",
19637                dst,
19638                g,
19639                b"FROMMEMBER",
19640                b"palermo",
19641                b"BYRADIUS",
19642                b"200",
19643                b"KM",
19644                b"STOREDIST",
19645            ]),
19646            ":2\r\n"
19647        );
19648        assert_eq!(
19649            f.run(&[b"ZSCORE", dst, b"palermo"]),
19650            "$1\r\n0\r\n",
19651            "the centre is nought away from itself"
19652        );
19653
19654        // A search that found nothing deletes the destination on its own
19655        // stripe, and a source of the wrong type is refused with the
19656        // destination left alone.
19657        assert_eq!(
19658            f.run(&[
19659                b"GEOSEARCHSTORE",
19660                dst,
19661                g,
19662                b"FROMLONLAT",
19663                b"0",
19664                b"0",
19665                b"BYRADIUS",
19666                b"1",
19667                b"M",
19668            ]),
19669            ":0\r\n"
19670        );
19671        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19672        f.run(&[
19673            b"GEOSEARCHSTORE",
19674            dst,
19675            g,
19676            b"FROMLONLAT",
19677            b"15",
19678            b"37",
19679            b"BYRADIUS",
19680            b"200",
19681            b"KM",
19682        ]);
19683        f.run(&[b"SET", plain, b"v"]);
19684        assert_eq!(
19685            f.run(&[
19686                b"GEOSEARCHSTORE",
19687                dst,
19688                plain,
19689                b"FROMLONLAT",
19690                b"15",
19691                b"37",
19692                b"BYRADIUS",
19693                b"200",
19694                b"KM",
19695            ]),
19696            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19697        );
19698        assert_eq!(
19699            f.run(&[b"ZCARD", dst]),
19700            ":2\r\n",
19701            "and left the destination"
19702        );
19703    }
19704
19705    /// Every time series command, on one stripe and on eight.
19706    ///
19707    /// Every timestamp is written out rather than left to the clock, so the two
19708    /// servers are compared on the samples they hold and not on how long the
19709    /// test took to get from one of them to the other.
19710    #[test]
19711    fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
19712        let script: &[&[&[u8]]] = &[
19713            &[
19714                b"TS.CREATE",
19715                b"ts:a",
19716                b"LABELS",
19717                b"sensor",
19718                b"a",
19719                b"room",
19720                b"1",
19721            ],
19722            &[b"TS.CREATE", b"ts:a"],
19723            &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
19724            &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
19725            &[
19726                b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
19727            ],
19728            &[
19729                b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
19730            ],
19731            &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
19732            &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
19733            &[b"TS.GET", b"ts:a"],
19734            &[b"TS.GET", b"gone"],
19735            &[b"TS.RANGE", b"ts:a", b"-", b"+"],
19736            &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
19737            &[
19738                b"TS.RANGE",
19739                b"ts:a",
19740                b"-",
19741                b"+",
19742                b"AGGREGATION",
19743                b"avg",
19744                b"2000",
19745            ],
19746            &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
19747            &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19748            &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19749            &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
19750            &[b"TS.READ", b"ts:a", b"0"],
19751            &[b"TS.READ", b"ts:a", b"+"],
19752            // The filters, which are the ones that have to walk every stripe.
19753            &[b"TS.QUERYINDEX", b"sensor=a"],
19754            &[b"TS.QUERYINDEX", b"room=1"],
19755            &[b"TS.QUERYINDEX", b"room=9"],
19756            &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
19757            &[
19758                b"TS.QUERYLABELS",
19759                b"VALUES",
19760                b"sensor",
19761                b"FILTER",
19762                b"room=1",
19763            ],
19764            &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
19765            &[
19766                b"TS.MGET",
19767                b"SELECTED_LABELS",
19768                b"sensor",
19769                b"FILTER",
19770                b"sensor=a",
19771            ],
19772            &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
19773            &[
19774                b"TS.MREVRANGE",
19775                b"-",
19776                b"+",
19777                b"WITHLABELS",
19778                b"FILTER",
19779                b"sensor=a",
19780            ],
19781            &[
19782                b"TS.MRANGE",
19783                b"-",
19784                b"+",
19785                b"FILTER",
19786                b"room=1",
19787                b"GROUPBY",
19788                b"room",
19789                b"REDUCE",
19790                b"max",
19791            ],
19792            &[b"TS.INFO", b"ts:a"],
19793            // And a rule, which is the one thing here that names two keys.
19794            &[
19795                b"TS.CREATERULE",
19796                b"ts:a",
19797                b"ts:down",
19798                b"AGGREGATION",
19799                b"avg",
19800                b"1000",
19801            ],
19802            &[b"TS.CREATE", b"ts:down"],
19803            &[
19804                b"TS.CREATERULE",
19805                b"ts:a",
19806                b"ts:down",
19807                b"AGGREGATION",
19808                b"avg",
19809                b"1000",
19810            ],
19811            &[b"TS.ADD", b"ts:a", b"5000", b"4"],
19812            &[b"TS.ADD", b"ts:a", b"6000", b"5"],
19813            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19814            &[b"TS.GET", b"ts:down", b"LATEST"],
19815            &[b"TS.INFO", b"ts:down"],
19816            &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
19817            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19818            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19819            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19820            &[b"TS.DEL", b"ts:a", b"0", b"1000"],
19821            // And the errors.
19822            &[b"SET", b"plain", b"v"],
19823            &[b"TS.ADD", b"plain", b"1", b"1"],
19824            &[b"TS.GET", b"plain"],
19825            &[b"TS.READ", b"plain", b"0"],
19826            &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
19827            &[b"TS.RANGE", b"gone", b"-", b"+"],
19828            &[b"TS.INFO", b"gone"],
19829        ];
19830
19831        let mut one = Fixture::new();
19832        let mut many = Fixture::striped(8);
19833        for parts in script {
19834            let a = one.run(parts);
19835            let b = many.run(parts);
19836            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19837        }
19838    }
19839
19840    /// A compaction rule whose two ends are on two stripes.
19841    ///
19842    /// This is the one thing in the family that walks from a key to another key,
19843    /// and it walks it in both directions: a sample on the source closes a
19844    /// bucket on the destination, a `LATEST` read on the destination folds the
19845    /// bucket the source is still filling, and a delete on the source rewrites
19846    /// what the destination already held. The same script is run against a
19847    /// server one stripe wide, where the two keys share a store, and against one
19848    /// eight stripes wide, where they do not.
19849    #[test]
19850    fn a_compaction_rule_across_stripes_reaches_both_ends() {
19851        let mut many = Fixture::striped(8);
19852        let other = apart(&mut many, "src");
19853        let (src, dst) = (b"src".as_slice(), other.as_bytes());
19854        let mut one = Fixture::new();
19855        let mut both = |parts: &[&[u8]]| {
19856            let a = one.run(parts);
19857            let b = many.run(parts);
19858            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19859            a
19860        };
19861
19862        both(&[b"TS.CREATE", src]);
19863        both(&[b"TS.CREATE", dst]);
19864        assert_eq!(
19865            both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
19866            "+OK\r\n"
19867        );
19868        both(&[b"TS.ADD", src, b"1000", b"1"]);
19869        both(&[b"TS.ADD", src, b"1500", b"3"]);
19870        // The bucket the source is filling is not written down yet, and asking
19871        // for it works it out off the source.
19872        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19873        let open = both(&[b"TS.GET", dst, b"LATEST"]);
19874        assert!(open.contains(":1000"), "the open bucket is folded: {open}");
19875
19876        // A sample past the bucket closes it, which is the write that has to
19877        // land on the other stripe.
19878        both(&[b"TS.ADD", src, b"2000", b"5"]);
19879        let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
19880        assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
19881        assert!(got.contains(":1000"), "{got}");
19882
19883        // And a delete on the source takes it away again.
19884        both(&[b"TS.DEL", src, b"1000", b"1999"]);
19885        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19886
19887        // Both ends still know about each other, and the link comes apart from
19888        // the source.
19889        assert!(
19890            both(&[b"TS.INFO", dst]).contains("src"),
19891            "the source is named"
19892        );
19893        assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
19894        assert_eq!(
19895            both(&[b"TS.DELETERULE", src, dst]),
19896            "-ERR TSDB: compaction rule does not exist\r\n"
19897        );
19898    }
19899
19900    /// A label filter takes the series it names wherever they landed.
19901    #[test]
19902    fn a_label_query_across_stripes_finds_every_series() {
19903        let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
19904        let mut many = Fixture::striped(8);
19905        let mut homes: Vec<usize> = names
19906            .iter()
19907            .map(|name| many.server.striped(0).stripe_of(name))
19908            .collect();
19909        homes.sort_unstable();
19910        homes.dedup();
19911        assert!(homes.len() > 1, "the six keys are not all on one stripe");
19912
19913        let mut one = Fixture::new();
19914        let mut both = |parts: &[&[u8]]| {
19915            let a = one.run(parts);
19916            let b = many.run(parts);
19917            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19918            a
19919        };
19920        for name in &names {
19921            both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
19922            both(&[b"TS.ADD", name, b"1000", b"1"]);
19923        }
19924
19925        let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
19926        assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
19927        assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19928        assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19929        assert_eq!(
19930            both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
19931            "*1\r\n$4\r\nroom\r\n"
19932        );
19933    }
19934
19935    /// Every hash command, and the field import beside it, on one stripe and on
19936    /// eight.
19937    ///
19938    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
19939    /// stripes do not draw the same numbers, so the only draw here is off a hash
19940    /// holding one field, where every generator gives the same answer.
19941    #[test]
19942    fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
19943        let script: &[&[&[u8]]] = &[
19944            &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
19945            &[b"HMSET", b"h", b"c", b"3"],
19946            &[b"HSETNX", b"h", b"a", b"9"],
19947            &[b"HSETNX", b"h", b"d", b"4"],
19948            &[b"HGET", b"h", b"a"],
19949            &[b"HGET", b"h", b"nope"],
19950            &[b"HMGET", b"h", b"a", b"nope"],
19951            &[b"HLEN", b"h"],
19952            &[b"HEXISTS", b"h", b"a"],
19953            &[b"HSTRLEN", b"h", b"a"],
19954            &[b"HGETALL", b"h"],
19955            &[b"HKEYS", b"h"],
19956            &[b"HVALS", b"h"],
19957            &[b"HINCRBY", b"h", b"a", b"5"],
19958            &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
19959            &[b"HSCAN", b"h", b"0"],
19960            &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
19961            &[b"HSCAN", b"h", b"0", b"NOVALUES"],
19962            &[b"HDEL", b"h", b"d"],
19963            &[b"HSET", b"one", b"f", b"v"],
19964            &[b"HRANDFIELD", b"one"],
19965            &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
19966            // The field deadlines.
19967            &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
19968            &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
19969            &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
19970            &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19971            &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19972            &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
19973            &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
19974            &[b"HGET", b"h", b"b"],
19975            // The three that came later and word everything their own way.
19976            &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
19977            &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
19978            &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
19979            &[b"HGET", b"h", b"e"],
19980            // And the import, whose key is the third word.
19981            &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
19982            &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
19983            &[b"HGETALL", b"imp"],
19984            &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
19985            &[b"HIMPORT", b"DISCARD", b"fs"],
19986            // And the errors.
19987            &[b"SET", b"plain", b"v"],
19988            &[b"HSET", b"plain", b"a", b"1"],
19989            &[b"HGETALL", b"plain"],
19990            &[b"HGET", b"gone", b"a"],
19991            &[b"HINCRBY", b"h", b"a", b"nan"],
19992        ];
19993
19994        let mut one = Fixture::new();
19995        let mut many = Fixture::striped(8);
19996        // The field deadlines are absolute milliseconds worked out from the
19997        // clock, so both servers are put on the same one rather than left to
19998        // read the wall a moment apart.
19999        one.server.set_clock_ms(1_700_000_000_000);
20000        many.server.set_clock_ms(1_700_000_000_000);
20001        for parts in script {
20002            let a = one.run(parts);
20003            let b = many.run(parts);
20004            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20005        }
20006    }
20007
20008    /// Every array command, on one stripe and on eight.
20009    #[test]
20010    fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20011        let script: &[&[&[u8]]] = &[
20012            &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20013            &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20014            &[b"ARGET", b"a", b"1"],
20015            &[b"ARGET", b"a", b"99"],
20016            &[b"ARMGET", b"a", b"0", b"5", b"99"],
20017            &[b"ARGETRANGE", b"a", b"0", b"7"],
20018            &[b"ARLEN", b"a"],
20019            &[b"ARCOUNT", b"a"],
20020            &[b"ARINSERT", b"a", b"m", b"n"],
20021            &[b"ARSCAN", b"a", b"0", b"20"],
20022            &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20023            &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20024            &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20025            &[b"ARLASTITEMS", b"a", b"2"],
20026            &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20027            &[b"ARNEXT", b"a"],
20028            &[b"ARSEEK", b"a", b"3"],
20029            &[b"AROP", b"a", b"0", b"20", b"USED"],
20030            &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20031            &[b"ARINFO", b"a"],
20032            &[b"ARINFO", b"a", b"FULL"],
20033            &[b"ARDEL", b"a", b"0"],
20034            &[b"ARDELRANGE", b"a", b"1", b"2"],
20035            &[b"ARCOUNT", b"a"],
20036            &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20037            &[b"ARGETRANGE", b"r", b"0", b"9"],
20038            // And the errors.
20039            &[b"SET", b"plain", b"v"],
20040            &[b"ARGET", b"plain", b"0"],
20041            &[b"ARSET", b"plain", b"0", b"v"],
20042            &[b"ARGET", b"gone", b"0"],
20043            &[b"ARSET", b"a", b"bad", b"v"],
20044        ];
20045
20046        let mut one = Fixture::new();
20047        let mut many = Fixture::striped(8);
20048        for parts in script {
20049            let a = one.run(parts);
20050            let b = many.run(parts);
20051            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20052        }
20053    }
20054
20055    /// Every graph and vector set command, on one stripe and on eight.
20056    ///
20057    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
20058    /// not: it draws from the stripe's generator, and the stripes do not share
20059    /// one.
20060    #[test]
20061    fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20062        let script: &[&[&[u8]]] = &[
20063            &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20064            &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20065            &[b"G.NADD", b"g", b"n3"],
20066            &[b"G.NGET", b"g", b"n1"],
20067            &[b"G.NGET", b"g", b"gone"],
20068            &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20069            &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20070            &[b"G.OUT", b"g", b"n1", b"knows"],
20071            &[b"G.IN", b"g", b"n2", b"knows"],
20072            &[b"G.DEG", b"g", b"n1", b"knows"],
20073            &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20074            &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20075            &[b"G.PATH", b"g", b"n1", b"n3"],
20076            &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20077            &[b"G.NDEL", b"g", b"n3"],
20078            &[b"G.NGET", b"g", b"n3"],
20079            // The vector set, which is one index under one key.
20080            &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20081            &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20082            &[b"VCARD", b"v"],
20083            &[b"VDIM", b"v"],
20084            &[b"VEMB", b"v", b"e1"],
20085            &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20086            &[b"VSIM", b"v", b"ELE", b"e1"],
20087            &[b"VISMEMBER", b"v", b"e1"],
20088            &[b"VISMEMBER", b"v", b"gone"],
20089            &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20090            &[b"VGETATTR", b"v", b"e1"],
20091            &[b"VRANGE", b"v", b"-", b"+"],
20092            &[b"VLINKS", b"v", b"e1"],
20093            &[b"VINFO", b"v"],
20094            &[b"VREM", b"v", b"e2"],
20095            &[b"VCARD", b"v"],
20096            // And the errors.
20097            &[b"SET", b"plain", b"v"],
20098            &[b"G.NGET", b"plain", b"n1"],
20099            &[b"VCARD", b"plain"],
20100            &[b"G.NADD", b"gone2", b"n"],
20101            &[b"VEMB", b"gone3", b"e"],
20102        ];
20103
20104        let mut one = Fixture::new();
20105        let mut many = Fixture::striped(8);
20106        for parts in script {
20107            let a = one.run(parts);
20108            let b = many.run(parts);
20109            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20110        }
20111    }
20112
20113    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
20114    /// command, on one stripe and on eight.
20115    #[test]
20116    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20117        let script: &[&[&[u8]]] = &[
20118            // The bloom filter.
20119            &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20120            &[b"BF.ADD", b"bf", b"a"],
20121            &[b"BF.ADD", b"bf", b"a"],
20122            &[b"BF.MADD", b"bf", b"b", b"c"],
20123            &[b"BF.EXISTS", b"bf", b"a"],
20124            &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20125            &[b"BF.CARD", b"bf"],
20126            &[b"BF.INFO", b"bf"],
20127            &[b"BF.INFO", b"bf", b"CAPACITY"],
20128            &[b"BF.DEBUG", b"bf"],
20129            &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20130            &[b"BF.EXISTS", b"made", b"x"],
20131            &[b"BF.SCANDUMP", b"bf", b"0"],
20132            // The cuckoo filter.
20133            &[b"CF.RESERVE", b"cf", b"100"],
20134            &[b"CF.ADD", b"cf", b"a"],
20135            &[b"CF.ADDNX", b"cf", b"a"],
20136            &[b"CF.COUNT", b"cf", b"a"],
20137            &[b"CF.EXISTS", b"cf", b"a"],
20138            &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20139            &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20140            &[b"CF.DEL", b"cf", b"a"],
20141            &[b"CF.COMPACT", b"cf"],
20142            &[b"CF.INFO", b"cf"],
20143            &[b"CF.DEBUG", b"cf"],
20144            &[b"CF.SCANDUMP", b"cf", b"0"],
20145            // The count min sketch.
20146            &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20147            &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20148            &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20149            &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20150            &[b"CMS.INFO", b"cms"],
20151            // The top k sketch.
20152            &[b"TOPK.RESERVE", b"tk", b"3"],
20153            &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20154            &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20155            &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20156            &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20157            &[b"TOPK.LIST", b"tk"],
20158            &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20159            &[b"TOPK.INFO", b"tk"],
20160            // The t digest.
20161            &[b"TDIGEST.CREATE", b"td"],
20162            &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20163            &[b"TDIGEST.MIN", b"td"],
20164            &[b"TDIGEST.MAX", b"td"],
20165            &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20166            &[b"TDIGEST.CDF", b"td", b"3"],
20167            &[b"TDIGEST.RANK", b"td", b"3"],
20168            &[b"TDIGEST.REVRANK", b"td", b"3"],
20169            &[b"TDIGEST.BYRANK", b"td", b"0"],
20170            &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20171            &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20172            &[b"TDIGEST.INFO", b"td"],
20173            &[b"TDIGEST.RESET", b"td"],
20174            &[b"TDIGEST.MIN", b"td"],
20175            // And the errors.
20176            &[b"SET", b"plain", b"v"],
20177            &[b"BF.ADD", b"plain", b"a"],
20178            &[b"CF.ADD", b"plain", b"a"],
20179            &[b"CMS.QUERY", b"plain", b"a"],
20180            &[b"TOPK.ADD", b"plain", b"a"],
20181            &[b"TDIGEST.ADD", b"plain", b"1"],
20182            &[b"CMS.INFO", b"gone"],
20183            &[b"TOPK.INFO", b"gone"],
20184            &[b"TDIGEST.INFO", b"gone"],
20185        ];
20186
20187        let mut one = Fixture::new();
20188        let mut many = Fixture::striped(8);
20189        for parts in script {
20190            let a = one.run(parts);
20191            let b = many.run(parts);
20192            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20193        }
20194    }
20195
20196    /// The two sketch merges, with their sources on stripes of their own.
20197    ///
20198    /// These are the only two commands in the ten groups that name more than one
20199    /// key, and both read a run of sources and write a destination, so both go
20200    /// wrong in the same way if a merge holds one store and looks every source up
20201    /// in it.
20202    #[test]
20203    fn a_sketch_merge_across_stripes_reads_every_source() {
20204        let mut many = Fixture::striped(8);
20205        let other = apart(&mut many, "s1");
20206        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20207        let mut one = Fixture::new();
20208        let mut both = |parts: &[&[u8]]| {
20209            let a = one.run(parts);
20210            let b = many.run(parts);
20211            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20212            a
20213        };
20214
20215        // The count min sketch. The destination has to be the sources' shape,
20216        // and it is named first, so all three keys are read before anything is
20217        // written.
20218        for key in [b"cd".as_slice(), s1, s2] {
20219            both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20220        }
20221        both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20222        both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20223        assert_eq!(
20224            both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20225            "+OK\r\n",
20226            "the merge took both sources"
20227        );
20228        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20229        // And with weights, which are read against the sources in order.
20230        both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20231        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20232        // A source that is not a sketch is answered before anything is written.
20233        both(&[b"SET", b"plain", b"v"]);
20234        assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20235        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20236
20237        // The t digest, which builds its destination and then puts it in place.
20238        // The two source keys are used again here, so what they held goes first.
20239        both(&[b"FLUSHALL"]);
20240        both(&[b"TDIGEST.CREATE", b"td"]);
20241        both(&[b"TDIGEST.CREATE", s1]);
20242        both(&[b"TDIGEST.CREATE", s2]);
20243        both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20244        both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20245        assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20246        assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20247        assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20248    }
20249
20250    /// Every shape of `SORT`, on one stripe and on eight.
20251    ///
20252    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
20253    /// destination are four different names and nothing lines them up, so on
20254    /// eight stripes this script is reading and writing all over the database
20255    /// while on one it is doing what it always did.
20256    #[test]
20257    fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20258        let script: &[&[&[u8]]] = &[
20259            &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20260            &[b"SORT", b"l"],
20261            &[b"SORT", b"l", b"DESC"],
20262            &[b"SORT", b"l", b"ALPHA"],
20263            &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20264            &[b"SORT_RO", b"l"],
20265            // A weight per element, so the order comes off keys the command
20266            // never named.
20267            &[
20268                b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20269            ],
20270            &[b"SORT", b"l", b"BY", b"w_*"],
20271            &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20272            &[b"DEL", b"w_2"],
20273            &[b"SORT", b"l", b"BY", b"w_*"],
20274            // And the answer off another set of keys again, with `#` mixed in
20275            // so the rows are not all lookups.
20276            &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20277            &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20278            // A pattern that reaches into a hash, which is another key again.
20279            &[b"HSET", b"h_1", b"f", b"9"],
20280            &[b"HSET", b"h_2", b"f", b"8"],
20281            &[b"HSET", b"h_3", b"f", b"7"],
20282            &[b"HSET", b"h_10", b"f", b"6"],
20283            &[b"SORT", b"l", b"BY", b"h_*->f"],
20284            &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20285            // The destination, which is a fourth place to land.
20286            &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20287            &[b"LRANGE", b"out", b"0", b"-1"],
20288            &[b"SORT", b"l", b"STORE", b"l"],
20289            &[b"LRANGE", b"l", b"0", b"-1"],
20290            // An empty result takes the destination away rather than leaving a
20291            // list of nothing behind.
20292            &[b"SORT", b"missing", b"STORE", b"out"],
20293            &[b"EXISTS", b"out"],
20294            // A set and a sorted set sort the same way a list does, and a set
20295            // written to a destination is sorted even when nothing asked.
20296            &[b"SADD", b"s", b"c", b"a", b"b"],
20297            &[b"SORT", b"s", b"ALPHA"],
20298            &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20299            &[b"LRANGE", b"out", b"0", b"-1"],
20300            &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20301            &[b"SORT", b"z", b"BY", b"nosort"],
20302            &[b"SORT", b"z", b"ALPHA", b"DESC"],
20303            // And the two ways it refuses: a key of the wrong type, and an
20304            // element that is not a number under a numeric sort.
20305            &[b"SET", b"str", b"v"],
20306            &[b"SORT", b"str"],
20307            &[b"RPUSH", b"words", b"one", b"two"],
20308            &[b"SORT", b"words"],
20309            &[b"SORT_RO", b"l", b"STORE", b"out"],
20310        ];
20311
20312        let mut one = Fixture::new();
20313        let mut many = Fixture::striped(8);
20314        for parts in script {
20315            let a = one.run(parts);
20316            let b = many.run(parts);
20317            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20318        }
20319    }
20320
20321    /// One `SORT` whose four kinds of key are on stripes of their own.
20322    ///
20323    /// The script above spreads keys around by writing enough of them, and this
20324    /// one checks the spread rather than trusting it: the list, the weight key
20325    /// for one of its elements and the destination are asserted to be in three
20326    /// places before the command runs.
20327    #[test]
20328    fn a_sort_across_stripes_reads_every_pattern_key() {
20329        let mut f = Fixture::striped(8);
20330        let out = apart(&mut f, "l");
20331        let (list, dest) = (b"l".as_slice(), out.as_bytes());
20332
20333        f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20334        f.run(&[
20335            b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20336        ]);
20337        f.run(&[
20338            b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20339        ]);
20340
20341        // The weights are four keys and they are not all in one place, which is
20342        // the thing that would go unnoticed if the command held a stripe.
20343        let db = f.server.striped(0);
20344        let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20345            .iter()
20346            .map(|k| db.stripe_of(k.as_slice()))
20347            .collect();
20348        assert!(
20349            weights.iter().any(|s| *s != weights[0]),
20350            "the four weight keys all landed on one stripe, so this proves nothing"
20351        );
20352
20353        assert_eq!(
20354            f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
20355            "*4\r\n$1\r\nD\r\n$1\r\nC\r\n$1\r\nB\r\n$1\r\nA\r\n",
20356            "the order came off the weights and the answer off the data keys"
20357        );
20358        assert_eq!(
20359            f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20360            ":4\r\n"
20361        );
20362        assert_eq!(
20363            f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20364            "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20365            "the destination is on a stripe of its own and got the whole answer"
20366        );
20367    }
20368
20369    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
20370    /// decide what shape it is stored in.
20371    ///
20372    /// This is the setting that would go wrong quietly. A stripe that kept the
20373    /// old ladder would hold the same hash in a different encoding from the
20374    /// stripe next to it, and the only thing that would ever say so is
20375    /// `OBJECT ENCODING`, which is why the check is on that.
20376    #[test]
20377    fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20378        let mut f = Fixture::striped(8);
20379        let other = apart(&mut f, "h");
20380        let (first, second) = (b"h".as_slice(), other.as_bytes());
20381
20382        assert_eq!(
20383            f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20384            "+OK\r\n"
20385        );
20386        assert_eq!(
20387            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20388            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20389            "the read comes off one stripe and has to answer for all of them"
20390        );
20391        for key in [first, second] {
20392            f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20393            assert_eq!(
20394                f.run(&[b"OBJECT", b"ENCODING", key]),
20395                "$8\r\nlistpack\r\n",
20396                "two fields is still under the ladder"
20397            );
20398            f.run(&[b"HSET", key, b"c", b"3"]);
20399            assert_eq!(
20400                f.run(&[b"OBJECT", b"ENCODING", key]),
20401                "$9\r\nhashtable\r\n",
20402                "three fields is over it, on whichever stripe the key is on"
20403            );
20404        }
20405
20406        // And the policy, which every stripe has to agree about for the same
20407        // reason: an eviction draws from one stripe at a time.
20408        assert_eq!(
20409            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20410            "+OK\r\n"
20411        );
20412        let db = f.server.striped(0);
20413        assert!(
20414            db.stripes()
20415                .iter()
20416                .all(|s| s.policy().name() == "allkeys-lru"),
20417            "a stripe kept the old policy"
20418        );
20419    }
20420}