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::lock::Held;
95use yo_common::{Code, Error};
96use yo_kv::cold::Blocks;
97use yo_kv::{Clock, Db, Keyspace};
98use yo_search::Registry;
99
100/// How many databases a server has.
101///
102/// Redis's default is sixteen and its `databases` setting can change it. Ours
103/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
104/// constant. Nothing in the design needs the number to be fixed; nothing yet
105/// needs it not to be.
106pub const DATABASES: usize = 16;
107
108/// Every database's bit in [`Server::dirty`], which is what a fresh server
109/// starts on so that the first maintenance turn asks all of them.
110///
111/// A `u64` holds sixteen bits with room to spare, and the assertion below is
112/// what turns raising [`DATABASES`] past sixty four into a build failure rather
113/// than a shift that silently drops the databases past the end.
114const ALL_DATABASES: u64 = if DATABASES == 64 {
115    u64::MAX
116} else {
117    (1u64 << DATABASES) - 1
118};
119const _: () = assert!(DATABASES <= 64);
120
121/// How many keys one command throws away before it leaves the rest to the next.
122///
123/// A bound and not a loop to the end, because this runs in front of a client
124/// that is waiting for its reply, and a server a long way over its limit would
125/// otherwise hold that client for as long as it took to walk all the way back
126/// under. Sixty four is a batch's worth of commands, so a server that went over
127/// by what one batch allocated comes back under in one command, and a server
128/// whose limit was just cut in half works through it over the next few thousand
129/// rather than in one long stall. Redis bounds the same loop by a time slice
130/// instead of a count and hands the rest to a timer; there is no timer here, so
131/// the rest goes to the next command that runs.
132const EVICT_BUDGET: usize = 64;
133
134/// What a server says to a command that would allocate when it has no room.
135///
136/// Redis's `shared.oomerr`, word for word including the full stop, because
137/// clients match on the `OOM` prefix and people match on the sentence.
138const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
139
140/// What the connection should do after a command.
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub enum Flow {
143    /// Read the next command.
144    Continue,
145    /// Write what is buffered and then close, which is what `QUIT` asks for.
146    Close,
147    /// Nothing was written and nothing is owed yet.
148    ///
149    /// The client is on the waiter list and its reply comes when a key it named
150    /// has something in it or when its deadline passes, whichever happens first.
151    /// Until then the connection stops reading commands, because a client that
152    /// is waiting for an answer is not a client that has sent another question.
153    Block,
154}
155
156/// The numbers `INFO` reports that this layer cannot see for itself.
157///
158/// The reactor owns the sockets, so the reactor is what knows how many clients
159/// there are. It writes these directly and nothing here does anything with them
160/// except report them.
161#[derive(Debug, Clone, Copy, Default)]
162pub struct Stats {
163    /// Connections open right now.
164    pub clients: u64,
165    /// Connections accepted since the server started.
166    pub connections: u64,
167    /// Commands run since the server started, which this layer counts itself.
168    pub commands: u64,
169}
170
171/// Where the process was started, which is what `dir` defaults to.
172///
173/// A dot if the working directory cannot be read, which happens when it has
174/// been deleted out from under a running process. That is not a reason to
175/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
176/// from the filesystem if anybody asks for one.
177fn working_dir() -> PathBuf {
178    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
179}
180
181/// One command's counters, for `INFO commandstats`.
182///
183/// Three of Redis's five. `usec` and `usec_per_call` are not here because
184/// nothing times a command, and timing one means two clock reads around a call
185/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
186/// has room for it; this does not, and a zero under a name that says microseconds
187/// is worse than an absent field, which is the same rule the rest of `INFO`
188/// follows.
189#[derive(Debug, Clone, Copy, Default)]
190pub struct CommandStat {
191    /// Times the command ran, whatever it answered.
192    pub calls: u64,
193    /// Times it was turned away before it ran, which is the wrong number of
194    /// arguments or no room under `maxmemory`.
195    pub rejected: u64,
196    /// Times it ran and answered with an error.
197    pub failed: u64,
198}
199
200impl CommandStat {
201    /// Whether this command has ever been seen.
202    ///
203    /// A row that has not is left out of the reply, which is what Redis does and
204    /// is why the section is a handful of lines on a working server rather than
205    /// one line per command in the table.
206    const fn seen(&self) -> bool {
207        self.calls != 0 || self.rejected != 0 || self.failed != 0
208    }
209}
210
211/// A counter per command, indexed the way [`table::index_of`] says.
212///
213/// A flat array and not a map, because the dispatcher is already holding the
214/// spec and the spec's position in the table is two addresses subtracted. That
215/// makes the counting a load, an add and a store on a row the previous command
216/// of the same name has already pulled into cache.
217struct CommandStats(Box<[CommandStat]>);
218
219impl Default for CommandStats {
220    fn default() -> CommandStats {
221        CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
222    }
223}
224
225impl CommandStats {
226    /// The row for one command.
227    fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
228        &mut self.0[table::index_of(spec)]
229    }
230}
231
232/// Where a database gets its store from, asked by database number.
233///
234/// `None` means that database cannot have one. The caller owns whatever the
235/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
236/// database, and this crate never learns what any of that is.
237pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
238
239/// Everything a server holds.
240///
241/// One of these per shard thread, not one per process: the databases inside are
242/// not `Sync` and are reached by sending their thread a command. What makes
243/// this a server rather than a shard is that it is the whole of what a
244/// connection can address.
245pub struct Server {
246    dbs: Vec<Db>,
247    /// How many stripes each database is cut into, the same for all of them.
248    ///
249    /// Kept here as well as in each database so that the flat slot arithmetic
250    /// below is a multiply and a divide against a field on the server rather
251    /// than a walk asking each database how wide it is.
252    width: usize,
253    clock: Clock,
254    started_ms: u64,
255    /// Where the next maintenance turn starts looking, so that a database
256    /// under constant write load cannot hold the other fifteen's space.
257    next_db: usize,
258    /// One bit per database, set when a command ran against it.
259    ///
260    /// The maintenance turn after every batch used to ask all sixteen
261    /// databases whether they had anything to collect, and asking costs a load
262    /// and a store in each one. Fifteen of those are cold lines on a server
263    /// where every client is on database zero, which is every server, and the
264    /// answer is no every time. This is the cheap half of the question: a
265    /// database nobody has touched since it last said no cannot have started
266    /// saying yes.
267    dirty: u64,
268    /// What the connections are holding, kept by the engine.
269    conn_bytes: usize,
270    /// The `maxmemory` limit in bytes, zero when there is not one.
271    ///
272    /// Zero is the default and it is the whole reason the check in front of
273    /// every write is one comparison against a field that is already warm.
274    maxmemory: u64,
275    /// Where a database gets a store from the first time it needs one.
276    ///
277    /// A closure and not a store, because there are sixteen databases and a
278    /// server that fills memory on database zero should not have opened
279    /// anything for the other fifteen. Nothing is asked of this until a memory
280    /// limit is actually reached, so a server that never fills memory never
281    /// opens a file, and a server that has no file never has one of these.
282    ///
283    /// `None` from the closure means that database cannot have one, which is
284    /// how the caller says the file it opened has no more room for logs.
285    store: Option<Box<StoreSource>>,
286    /// The `maxstore` limit in bytes, `None` when there is not one.
287    ///
288    /// The storage limit, and the other half of the inversion `14` section 4.1
289    /// describes. `maxmemory` is a limit on memory and the right answer to a
290    /// memory limit on a system with a file under it is to move data to the
291    /// file, not to delete it. Deleting is the right answer to a limit on the
292    /// file, and this is that limit.
293    ///
294    /// Zero is not "no limit" here, which is the one place this reads
295    /// differently from `maxmemory` and is the difference that makes a drop in
296    /// cache possible. A storage budget of zero bytes means nothing may live on
297    /// the file, so migration cannot make room and eviction is the only thing
298    /// left, which is Redis exactly. `None` is no limit and is the default,
299    /// which with `noeviction` means the database grows until the disk is full
300    /// and then writes fail, which is what a database does.
301    maxstore: Option<u64>,
302    /// What [`Server::memory_bytes`] said at the last maintenance turn.
303    ///
304    /// The reading is a walk over every collection in every database and cannot
305    /// go on a command path, so the command path reads this instead and is at
306    /// most one batch behind. What that costs is overshoot: a server can end a
307    /// batch holding one batch's worth of allocation more than its limit before
308    /// anything notices. A batch is 64 commands, so that is bounded by what 64
309    /// commands can allocate and not by how long the server runs.
310    ///
311    /// Only kept up to date when there is a limit to judge it against. A server
312    /// with no `maxmemory` never reads it and never pays for it.
313    used: usize,
314    /// Which database the next eviction draws from.
315    ///
316    /// Its own cursor and not [`Server::next_db`], because eviction and
317    /// compaction move at different rates and sharing one would make the
318    /// database that gets compacted depend on how many keys were evicted.
319    evict_db: usize,
320    /// Which database the next active expiry sweep starts at.
321    ///
322    /// A third cursor for the same reason there is a second one. A sweep runs on
323    /// every turn of the loop and compaction runs when there is dead space, so
324    /// sharing a cursor would make which database gets swept depend on which one
325    /// was last collected.
326    expire_db: usize,
327    /// The millisecond the last active expiry sweep ran on, so the next one on
328    /// the same millisecond does not bother.
329    expire_ms: u64,
330    /// Clients parked on a blocking command.
331    waiters: Waiters,
332    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
333    ///
334    /// Empty on a server nobody has migrated a key out of, which is nearly all
335    /// of them, and it costs a vector's three words to be empty.
336    peers: migrate::Peers,
337    /// The numbers the reactor keeps for `INFO`.
338    pub stats: Stats,
339    /// A counter per command, for `INFO commandstats`.
340    cmdstats: CommandStats,
341    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
342    ///
343    /// Absolute, and resolved once when the server is built rather than every
344    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
345    /// entitled to hand one of them to a copy tool, so a relative path that
346    /// meant something different after a `chdir` would be a path that stops
347    /// working for reasons nobody could see.
348    dir: PathBuf,
349    /// What backup is running, if one is.
350    ///
351    /// On the server and not on a session, because a backup outlives the
352    /// connection that asked for it and any other connection can seal it.
353    backup: backup::State,
354    /// The search indexes and the names pointing at them.
355    ///
356    /// On the server and not on a database, which is the one collection in this
357    /// build that is. A real server keeps its indexes in the search module, the
358    /// module has one table, and `SELECT 1` followed by `FT._LIST` lists the
359    /// indexes made on database zero. `search.rs` has the rest of why.
360    ///
361    /// A server nobody has made an index on holds two empty vectors here, which
362    /// is six words and no allocation.
363    search: Registry,
364    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
365    ///
366    /// A flag rather than an exit, because the command layer is not what owns
367    /// the process. It runs inside a batch that has other commands behind it
368    /// and inside a driver that has a socket file to take away and a file to
369    /// close, and a server that calls `exit` from a command handler skips all
370    /// of that. So the command says stop and the driver stops, on the same turn
371    /// and through the same door a signal uses.
372    stopping: bool,
373}
374
375impl Server {
376    /// A server with [`DATABASES`] empty databases on the system clock.
377    #[must_use]
378    pub fn new() -> Server {
379        let clock = Clock::system();
380        Server {
381            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
382            width: 1,
383            clock,
384            started_ms: clock.now_ms(),
385            next_db: 0,
386            dirty: ALL_DATABASES,
387            conn_bytes: 0,
388            maxmemory: 0,
389            store: None,
390            maxstore: None,
391            used: 0,
392            evict_db: 0,
393            expire_db: 0,
394            expire_ms: 0,
395            waiters: Waiters::default(),
396            peers: migrate::Peers::default(),
397            stats: Stats::default(),
398            cmdstats: CommandStats::default(),
399            dir: working_dir(),
400            backup: backup::State::default(),
401            search: Registry::new(),
402            stopping: false,
403        }
404    }
405
406    /// A server whose databases are cut into `width` stripes each.
407    ///
408    /// Not reachable from the command line yet. Every command group answers on
409    /// a server of any width now and so does everything that walks a whole
410    /// database, and the tests run each group at a width of one and a width of
411    /// eight and check the two agree.
412    ///
413    /// What is left before this is what `--threads` sets is the engine. A
414    /// database being several objects is what makes more than one thread
415    /// possible, and it is not what makes more than one thread happen.
416    #[must_use]
417    pub fn with_width(width: usize) -> Server {
418        let clock = Clock::system();
419        let mut server = Server::new();
420        server.dbs = (0..DATABASES)
421            .map(|_| Db::with_clock(clock, width))
422            .collect();
423        server.width = server.dbs[0].width();
424        server
425    }
426
427    /// A server on a clock the caller moves by hand, for tests.
428    #[must_use]
429    pub fn with_clock(clock: Clock) -> Server {
430        Server {
431            dbs: (0..DATABASES).map(|_| Db::with_clock(clock, 1)).collect(),
432            width: 1,
433            clock,
434            started_ms: clock.now_ms(),
435            next_db: 0,
436            dirty: ALL_DATABASES,
437            conn_bytes: 0,
438            maxmemory: 0,
439            store: None,
440            maxstore: None,
441            used: 0,
442            evict_db: 0,
443            expire_db: 0,
444            expire_ms: 0,
445            waiters: Waiters::default(),
446            peers: migrate::Peers::default(),
447            stats: Stats::default(),
448            cmdstats: CommandStats::default(),
449            dir: working_dir(),
450            backup: backup::State::default(),
451            search: Registry::new(),
452            stopping: false,
453        }
454    }
455
456    /// One database, by index.
457    ///
458    /// A caller that knows which key it wants names the one stripe the key is
459    /// on rather than working over the whole thing, which is what `at` and its
460    /// neighbours on [`Db`] are for. A caller that is about a database rather
461    /// than about a key, which is the snapshot walk and a setting, works over
462    /// all of them.
463    ///
464    /// The borrow is mutable, so the database is marked as having had something
465    /// run against it. Anything that only reads has [`Server::striped_ref`] and
466    /// does not come through here.
467    ///
468    /// # Panics
469    ///
470    /// If `i` is not a database. `SELECT` is the only way a client changes the
471    /// index and it checks, so an index that is out of range here is a bug in
472    /// the caller and not something a client can ask for.
473    pub fn striped(&mut self, i: usize) -> &mut Db {
474        self.dirty |= 1u64 << i;
475        &mut self.dbs[i]
476    }
477
478    /// Every keyspace on the server, which is every stripe of every database.
479    ///
480    /// What the aggregates walk. A total over the whole server is a total over
481    /// all of these and the stripe boundaries do not appear in it, which is
482    /// what makes the numbers `INFO` reports the same numbers whatever the
483    /// server was cut into.
484    fn keyspaces(&self) -> impl Iterator<Item = Held<'_, Keyspace>> {
485        self.dbs
486            .iter()
487            .flat_map(|db| (0..db.width()).map(|i| db.hold_stripe(i)))
488    }
489
490    /// The same, mutably.
491    fn keyspaces_mut(&mut self) -> impl Iterator<Item = &mut Keyspace> {
492        self.dbs.iter_mut().flat_map(Db::stripes_mut)
493    }
494
495    /// How many keyspaces there are, counting every stripe of every database.
496    ///
497    /// The maintenance turns walk these rather than the databases, because a
498    /// stripe is the thing that holds an arena and a deadline heap and so it is
499    /// the thing that has anything to collect.
500    const fn slots(&self) -> usize {
501        DATABASES * self.width
502    }
503
504    /// Which database slot `i` belongs to.
505    const fn slot_db(&self, i: usize) -> usize {
506        i / self.width
507    }
508
509    /// Keyspace `i` of [`Server::slots`].
510    fn slot_mut(&mut self, i: usize) -> &mut Keyspace {
511        let (db, stripe) = (i / self.width, i % self.width);
512        self.dbs[db].stripe_mut(stripe)
513    }
514
515    /// The same, without taking it mutably.
516    fn slot(&self, i: usize) -> Held<'_, Keyspace> {
517        let (db, stripe) = (i / self.width, i % self.width);
518        self.dbs[db].hold_stripe(stripe)
519    }
520
521    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
522    #[must_use]
523    pub fn dir(&self) -> &Path {
524        &self.dir
525    }
526
527    /// Point the server at a different directory, which `yodb serve --dir` does.
528    ///
529    /// Only before it is serving. There is no `CONFIG SET dir` here and there
530    /// is none on a real server either without turning protected configs on,
531    /// for the good reason that moving it out from under a running backup would
532    /// leave files nothing can find again.
533    pub fn set_dir(&mut self, dir: PathBuf) {
534        self.dir = dir;
535    }
536
537    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
538    ///
539    /// Once per batch, from the same maintenance turn that collects the arena.
540    /// It reads two fields and returns on a server that has never taken a
541    /// backup, which is nearly all of them.
542    pub fn backup_expire(&mut self) {
543        backup::expire(self);
544    }
545
546    /// Ask for the server to stop, which is what `SHUTDOWN` does.
547    ///
548    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
549    /// or ends the process, because none of those belong to this layer, and a
550    /// batch that is halfway through still has to finish and be written out.
551    pub fn stop(&mut self) {
552        self.stopping = true;
553    }
554
555    /// Whether somebody has asked the server to stop.
556    ///
557    /// Read once per turn by the loop, next to the flag a signal sets. The two
558    /// mean the same thing and are separate only because one arrives from the
559    /// operating system and the other from a client.
560    #[must_use]
561    pub fn stopping(&self) -> bool {
562        self.stopping
563    }
564
565    /// One database, by index, without taking it mutably.
566    ///
567    /// What the prefetch stage needs. It runs for all 64 commands in a batch
568    /// before any of them executes, so it cannot hold the mutable borrow `run`
569    /// is about to want, and it does not need one: warming a cache line reads
570    /// nothing and changes nothing.
571    #[must_use]
572    pub fn striped_ref(&self, i: usize) -> &Db {
573        &self.dbs[i]
574    }
575
576    /// The stripe that answers for a database when a setting is read back.
577    ///
578    /// A ladder setting and an eviction policy are one number on a real server,
579    /// and the fact that every stripe of every database carries a copy of it is
580    /// ours rather than the client's problem. A write puts the same value on
581    /// every one of them, so any stripe answers for all of them and this is the
582    /// first one.
583    fn settings(&self) -> Held<'_, Keyspace> {
584        self.dbs[0].hold_stripe(0)
585    }
586
587    /// Take a new clock reading and give it to every database.
588    ///
589    /// Once per turn of the event loop, which is the only place time moves. A
590    /// command asking what the time is gets the answer the whole batch got, so
591    /// two keys written by the same batch expire together (`04` section 3).
592    pub fn refresh_clock(&mut self) {
593        self.clock.refresh();
594        let now = self.clock.now_ms();
595        for db in &mut self.dbs {
596            db.set_clock_ms(now);
597        }
598    }
599
600    /// Move every clock here on by `ms`, for tests about expiry.
601    ///
602    /// The same thing [`Server::set_clock_ms`] does and by the same argument,
603    /// except that it moves from wherever the clock is rather than to a stated
604    /// moment, which is what a test that wants a key to have expired asks for.
605    pub fn advance_clock_ms(&mut self, ms: u64) {
606        let now = self.clock.now_ms() + ms;
607        self.set_clock_ms(now);
608    }
609
610    /// Move every clock here to `ms` by hand, for tests about expiry.
611    ///
612    /// A test cannot wait a hundred seconds and a test that waits a hundred
613    /// milliseconds is a test that fails on a loaded machine, so time moves on
614    /// request. The system clock underneath will overwrite this on the next
615    /// [`Server::refresh_clock`], which is why this is only useful in a test
616    /// that drives commands directly rather than through the event loop.
617    pub fn set_clock_ms(&mut self, ms: u64) {
618        self.clock.set(ms);
619        for db in &mut self.dbs {
620            db.set_clock_ms(ms);
621        }
622    }
623
624    /// Seconds since this server was built.
625    #[must_use]
626    pub fn uptime_secs(&self) -> u64 {
627        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
628    }
629
630    /// Bytes held by every database's index and arena, plus the read and reply
631    /// buffers of every connection.
632    ///
633    /// The buffers are in here because they are real and because Redis counts
634    /// its own, so leaving them out would make the one number people compare
635    /// flattering rather than true. They are not a database, so nothing in the
636    /// keyspace can change them and the engine has to say when they move.
637    #[must_use]
638    pub fn memory_bytes(&self) -> usize {
639        self.keyspaces().map(|db| db.memory_bytes()).sum::<usize>() + self.conn_bytes
640    }
641
642    /// What the keyspace itself is holding, live records only.
643    ///
644    /// `used_memory` minus this is what the store costs to run: the index, the
645    /// space dead records are sitting in until compaction gets to them, and the
646    /// connections' buffers.
647    #[must_use]
648    pub fn dataset_bytes(&self) -> usize {
649        self.keyspaces()
650            .map(|db| db.map().arena().live_bytes() as usize)
651            .sum()
652    }
653
654    /// Bytes the arenas are holding, live and dead together.
655    #[must_use]
656    pub fn arena_bytes(&self) -> usize {
657        self.keyspaces()
658            .map(|db| db.map().arena().reserved_bytes() as usize)
659            .sum()
660    }
661
662    /// Bytes the indexes are holding.
663    #[must_use]
664    pub fn index_bytes(&self) -> usize {
665        self.keyspaces()
666            .map(|db| db.map().index().memory_bytes())
667            .sum()
668    }
669
670    /// What arena compaction has cost, across every database.
671    ///
672    /// The write amplification of value separation, which is invisible from the
673    /// outside otherwise: a client that writes a megabyte can leave the store
674    /// copying several more, and the only sign of it without these is that the
675    /// writes got slower.
676    #[must_use]
677    pub fn compaction(&self) -> yo_kv::Compaction {
678        self.keyspaces().map(|db| db.map().compaction()).fold(
679            yo_kv::Compaction::default(),
680            |a, b| yo_kv::Compaction {
681                walked: a.walked + b.walked,
682                moved: a.moved + b.moved,
683                bytes: a.bytes + b.bytes,
684            },
685        )
686    }
687
688    /// Arena segments whose pages are real, across every database.
689    #[must_use]
690    pub fn segment_count(&self) -> usize {
691        self.keyspaces()
692            .map(|db| db.map().arena().resident_segments())
693            .sum()
694    }
695
696    /// What the connections' read and reply buffers are holding.
697    #[must_use]
698    pub const fn conn_bytes(&self) -> usize {
699        self.conn_bytes
700    }
701
702    /// Note that the connections are holding `delta` bytes more than they were,
703    /// or fewer when it is negative.
704    ///
705    /// A delta and not a total because the alternative is a walk over every
706    /// connection, and the walk would have to happen on a turn of the loop
707    /// rather than when `INFO` asks, which puts the cost of a report on the
708    /// command path of a server nobody is asking.
709    pub fn note_conn_bytes(&mut self, delta: isize) {
710        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
711    }
712
713    /// Keys reclaimed by running into them after their deadline.
714    #[must_use]
715    pub fn expired_keys(&self) -> u64 {
716        self.keyspaces().map(|db| db.expired_keys()).sum()
717    }
718
719    /// Keys thrown away to make room, which is the other number entirely.
720    #[must_use]
721    pub fn evicted_keys(&self) -> u64 {
722        self.keyspaces().map(|db| db.evicted_keys()).sum()
723    }
724
725    /// Every command that has been seen, with its counters.
726    ///
727    /// Only the ones that have. A server reports a handful of lines rather than
728    /// one per command in the table, which is what Redis does and is the
729    /// difference between a section a person can read and one they cannot.
730    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
731        self.cmdstats
732            .0
733            .iter()
734            .enumerate()
735            .filter(|(_, row)| row.seen())
736            .map(|(at, row)| (table::name_at(at), *row))
737    }
738
739    /// The `maxmemory` limit in bytes, zero when there is not one.
740    #[must_use]
741    pub const fn maxmemory(&self) -> u64 {
742        self.maxmemory
743    }
744
745    /// Set the limit, and take a reading straight away.
746    ///
747    /// The reading is here rather than left to the next maintenance turn because
748    /// a client that sets the limit and sends a write in the same batch expects
749    /// the write to be judged against the limit it just set, and because the
750    /// cached number is meaningless until the first time there is a limit to
751    /// compare it with.
752    ///
753    /// Turning the limit on also turns on the running total every slab keeps of
754    /// what its collections hold, and turning it off turns that back off, so a
755    /// server with no limit is not paying to count something nobody reads. The
756    /// first reading after switching it on is the walk that the total starts
757    /// from, and it is the only walk.
758    pub fn set_maxmemory(&mut self, bytes: u64) {
759        self.maxmemory = bytes;
760        for db in &mut self.dbs {
761            db.track_memory(bytes != 0);
762        }
763        self.used = self.settled_memory();
764    }
765
766    /// Say where a database should get its store from when it needs one.
767    ///
768    /// This is what turns the eviction inversion on. Until it is called every
769    /// database answers a memory limit by evicting, which is Redis, and after it
770    /// is called a database under memory pressure moves values to whatever the
771    /// closure hands back instead of throwing keys away.
772    ///
773    /// Called at most once per database and only under pressure, so a server
774    /// that is given a file and never fills memory never touches it.
775    pub fn set_store_source(
776        &mut self,
777        source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
778    ) {
779        self.store = Some(Box::new(source));
780    }
781
782    /// Whether this server has been given somewhere to put cold values.
783    #[must_use]
784    pub const fn has_store_source(&self) -> bool {
785        self.store.is_some()
786    }
787
788    /// Open database `at`'s store, if it has not got one and there is one to be
789    /// had.
790    ///
791    /// A store that will not open leaves the database where it was, which is
792    /// evicting, because a memory limit that cannot be answered by moving data
793    /// still has to be answered.
794    fn attach_store(&mut self, at: usize) {
795        if self.slot(at).store_bytes().is_some() {
796            return;
797        }
798        let Some(source) = self.store.as_mut() else {
799            return;
800        };
801        if let Some(blocks) = source(at) {
802            self.slot_mut(at).attach(blocks);
803        }
804    }
805
806    /// The `maxstore` limit in bytes, `None` when there is not one.
807    #[must_use]
808    pub const fn maxstore(&self) -> Option<u64> {
809        self.maxstore
810    }
811
812    /// Set the storage limit, or clear it with `None`.
813    ///
814    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
815    /// total, because this limit is compared against a number the store keeps
816    /// and answers on demand, not against a walk.
817    pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
818        self.maxstore = bytes;
819    }
820
821    /// What every attached store is holding, for `INFO memory`.
822    ///
823    /// Zero on a server with nothing attached, which is not the same as a server
824    /// whose file is empty, and [`Server::regime`] is the field that tells those
825    /// two apart.
826    #[must_use]
827    pub fn store_bytes(&self) -> u64 {
828        self.keyspaces().filter_map(|db| db.store_bytes()).sum()
829    }
830
831    /// What the file has been asked to do, added up over every database.
832    ///
833    /// Counters and not levels, so they only ever go up and a run is the
834    /// difference between two readings. G9 is a ratio over these: the faults a
835    /// run took, divided by the point reads it issued, has to come out at 1.05
836    /// or less with a working set ten times memory. There is no way to work that
837    /// out from outside the server, so it is reported rather than inferred.
838    ///
839    /// A fault is a read that went to the store. Whether it also went to the
840    /// device depends on the store: a log serves a read out of a resident page
841    /// without touching anything. At ten times memory almost every fault is a
842    /// real read, which is why the gate is written against this number, but the
843    /// two are not the same thing and a run tight against the bar should be
844    /// checked against what the operating system says.
845    #[must_use]
846    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
847        let mut total = yo_kv::tier::Stats::default();
848        for db in self.keyspaces() {
849            let Some(tier) = db.tier() else { continue };
850            let s = tier.stats();
851            total.demoted += s.demoted;
852            total.promoted += s.promoted;
853            total.faults += s.faults;
854            total.served += s.served;
855            total.bytes_out += s.bytes_out;
856            total.bytes_in += s.bytes_in;
857        }
858        total
859    }
860
861    /// Which way this server answers a memory limit, in one word for `INFO`.
862    ///
863    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
864    /// inversion: a memory limit moves values to the file and nothing stored is
865    /// lost. A server reports one word rather than leaving an operator to work
866    /// it out from a limit, a setting and whether a file happens to be open.
867    #[must_use]
868    pub fn regime(&self) -> &'static str {
869        if (0..self.slots()).any(|at| self.migrates(at)) {
870            "migrate"
871        } else {
872            "evict"
873        }
874    }
875
876    /// Whether database `at` answers a memory limit by moving values to the
877    /// file rather than by throwing keys away.
878    ///
879    /// Three things have to hold. There has to be somewhere to move them, which
880    /// is a store attached to that database or a source that can open one, and
881    /// on a server that was never given a file this is false everywhere and
882    /// every database behaves exactly as it did.
883    /// The storage budget has to be more than nothing, which is what
884    /// `maxstore 0` says it is not. And the file has to be under that budget,
885    /// because a full file is a storage limit reached and eviction is the right
886    /// answer to a storage limit.
887    fn migrates(&self, at: usize) -> bool {
888        if self.maxstore == Some(0) {
889            return false;
890        }
891        // Out of the stripe first. A match keeps whatever it is looking at
892        // alive for the whole of itself, and that would be this stripe held
893        // across the arms for no reason.
894        let bytes = self.slot(at).store_bytes();
895        match bytes {
896            Some(held) => self.maxstore.is_none_or(|cap| held < cap),
897            // Nothing attached, but somewhere to get one from the moment this
898            // database needs it, which is what makes the answer yes rather than
899            // no. Opening it here would mean `INFO` opened files.
900            None => self.store.is_some(),
901        }
902    }
903
904    /// Take a fresh memory reading, which the maintenance turn does once a batch.
905    ///
906    /// Nothing at all when there is no limit, which is the default and is every
907    /// server that has not asked for one.
908    pub fn refresh_memory(&mut self) {
909        if self.maxmemory != 0 {
910            self.used = self.settled_memory();
911        }
912    }
913
914    /// [`Server::memory_bytes`], asked the cheap way.
915    ///
916    /// The same number. The difference is that this asks each database only
917    /// about the collections that could have moved since the last time, which is
918    /// what a batch touched rather than what the server holds, so it can be
919    /// asked once a batch and again on every command that is over the limit.
920    fn settled_memory(&mut self) -> usize {
921        self.keyspaces_mut()
922            .map(Keyspace::settled_memory_bytes)
923            .sum::<usize>()
924            + self.conn_bytes
925    }
926
927    /// Make room under the `maxmemory` limit, throwing keys away if that is what
928    /// it takes. Answers whether there is anything left it could throw away.
929    ///
930    /// Redis runs the same thing from `processCommand` before every command and
931    /// so does this: a client that writes has to be judged at the moment it
932    /// writes, not a batch later, or the limit is a suggestion.
933    ///
934    /// Three things happen in the loop and all three are needed. Eviction picks
935    /// a key and drops it. Compaction gives the pages back, because dropping a
936    /// key marks its record dead and returns nothing on its own, so a loop that
937    /// only evicted would throw the whole keyspace away and watch the number
938    /// stay where it was. The reading is taken again each time round, because
939    /// the two of them together are the only thing that moves it.
940    ///
941    /// # Why running out of budget is not a no
942    ///
943    /// `false` means there was nothing left to evict, which is `noeviction`, or
944    /// a `volatile` policy on a database where nothing has a deadline, or a
945    /// keyspace that is already empty. It does not mean the server is still over
946    /// its limit, and that difference is Redis's: `performEvictions` answers
947    /// `EVICT_FAIL` only when it has run out of things to delete, and
948    /// `processCommand` refuses the client on that and on nothing else. Running
949    /// out of time part way through a job it is doing well comes back as
950    /// `EVICT_RUNNING` and the command goes through, because a server that is
951    /// evicting steadily and refusing every write while it does it is worse for
952    /// the client than a little overshoot.
953    ///
954    /// # What the limit is worth
955    ///
956    /// Space comes back a segment at a time and a segment is two megabytes, so
957    /// this holds a server to its limit give or take a segment. A `maxmemory` of
958    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
959    /// megabytes is asking for a precision this store does not have.
960    pub fn make_room(&mut self) -> bool {
961        if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
962            return true;
963        }
964        // The cached reading is a batch old and the batch may have compacted
965        // since, so take a fresh one before throwing anything away. It is the
966        // settled reading and not the walk, so what this costs is the handful of
967        // collections the last batch touched and not the whole database.
968        self.used = self.settled_memory();
969        let mut budget = EVICT_BUDGET;
970        while self.used as u64 > self.maxmemory {
971            let over = self.used - self.maxmemory as usize;
972            if !self.relieve_step(over) {
973                return false;
974            }
975            self.compact_hard_step();
976            self.used = self.settled_memory();
977            budget -= 1;
978            if budget == 0 {
979                break;
980            }
981        }
982        true
983    }
984
985    /// Give back `over` bytes from whichever database can, by moving values to
986    /// the file where there is one and by throwing keys away where there is not.
987    ///
988    /// The two answers are the eviction inversion and which one a database gets
989    /// is [`Server::migrates`]. Answers whether anything was given back at all,
990    /// and `false` is what refuses the client's write.
991    ///
992    /// A store that will not take the bytes counts as nothing given back, so the
993    /// write is refused rather than turned into a deletion. A disk that is
994    /// misbehaving is a reason to stop accepting writes and it is not a reason
995    /// to start losing data that was accepted already.
996    ///
997    /// Round robin from a cursor rather than always starting at database zero,
998    /// so a server using more than one of them does not empty the first before
999    /// touching the second. Almost every server is on database zero only, where
1000    /// this is one call that answers and fifteen that say the map is empty.
1001    fn relieve_step(&mut self, over: usize) -> bool {
1002        for turn in 0..self.slots() {
1003            let i = (self.evict_db + turn) % self.slots();
1004            // An empty keyspace has nothing to move and opening a log for one
1005            // would cost a resident page window to find that out.
1006            let used = !self.slot(i).is_empty();
1007            let gave = if used && self.migrates(i) {
1008                self.attach_store(i);
1009                // Whether it made room and not whether it moved a key. A round
1010                // that demoted nothing and handed back a segment is a round
1011                // that made room, and reading only the count refuses the write
1012                // that provoked it.
1013                self.slot_mut(i)
1014                    .relieve(over)
1015                    .is_ok_and(yo_kv::tier::Relief::made_room)
1016            } else {
1017                self.slot_mut(i).evict_one()
1018            };
1019            if gave {
1020                self.evict_db = (i + 1) % self.slots();
1021                self.dirty |= 1u64 << self.slot_db(i);
1022                return true;
1023            }
1024        }
1025        false
1026    }
1027
1028    /// The sweep the shard loop calls, at most once a millisecond.
1029    ///
1030    /// The gate is the whole difference between this and [`Server::expire_step`].
1031    /// A maintenance slice runs on every turn of the loop and a turn is a
1032    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
1033    /// thousand times per millisecond and spend a real share of the shard on
1034    /// looking for keys that cannot have died since the last look. Nothing in a
1035    /// database changes fast enough to be worth asking about more often than the
1036    /// clock can tell the difference, and the clock here is milliseconds.
1037    ///
1038    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
1039    /// hertz, so this is not the thing that decides how promptly memory comes
1040    /// back. What it decides is that an idle server sweeps a thousand times a
1041    /// second rather than a million.
1042    pub fn expire_slice(&mut self, budget: usize) -> usize {
1043        let now = self.clock.now_ms();
1044        if now == self.expire_ms {
1045            return 0;
1046        }
1047        self.expire_ms = now;
1048        self.expire_step(budget)
1049    }
1050
1051    /// Sweep dead keys out of the databases, spending at most `budget` looks.
1052    ///
1053    /// Answers what it spent, so the caller can charge its maintenance slice for
1054    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
1055    ///
1056    /// Round robin from its own cursor, and every database gets offered whatever
1057    /// is left of the budget rather than a sixteenth of it each, so a server on
1058    /// database zero only, which is nearly every server, spends the whole slice
1059    /// where the keys are. The fifteen empty ones cost a comparison apiece
1060    /// because a database with no key carrying a deadline says so without
1061    /// drawing anything.
1062    ///
1063    /// The cursor moves to the database after whichever one did the work, so two
1064    /// busy databases take turns instead of the lower numbered one starving the
1065    /// other.
1066    pub fn expire_step(&mut self, budget: usize) -> usize {
1067        let mut spent = 0;
1068        for turn in 0..self.slots() {
1069            if spent >= budget {
1070                break;
1071            }
1072            let i = (self.expire_db + turn) % self.slots();
1073            let c = self.slot_mut(i).expire_cycle(budget - spent);
1074            spent += c.examined;
1075            if c.expired > 0 {
1076                self.expire_db = (i + 1) % self.slots();
1077                self.dirty |= 1u64 << self.slot_db(i);
1078            }
1079        }
1080        spent
1081    }
1082
1083    /// One slice of compaction for a server that is over its limit.
1084    ///
1085    /// Takes the databases in the same order [`Server::compact_step`] does and
1086    /// stops at the first one that had something to move, and it asks with the
1087    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
1088    fn compact_hard_step(&mut self) -> Option<usize> {
1089        for turn in 0..self.slots() {
1090            let i = (self.next_db + turn) % self.slots();
1091            if let Some(moved) = self.slot_mut(i).compact_hard() {
1092                self.next_db = (i + 1) % self.slots();
1093                return Some(moved);
1094            }
1095        }
1096        None
1097    }
1098
1099    /// Give one database's dead space back, if any database has enough of it to
1100    /// be worth the move. `None` when no database had a candidate.
1101    ///
1102    /// Once per batch, next to the clock. Overwriting a key writes a new record
1103    /// and counts the old one dead, so without this a server holds everything
1104    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
1105    /// a key against Redis at 144 for the same load, and the whole difference
1106    /// was dead records nothing ever came back for.
1107    ///
1108    /// At most one segment moves per call and the search starts one database
1109    /// further along each time, so the cost of asking is a comparison per
1110    /// database and the cost of acting is bounded by a segment.
1111    pub fn compact_step(&mut self) -> Option<usize> {
1112        for turn in 0..self.slots() {
1113            let i = (self.next_db + turn) % self.slots();
1114            // Nothing has run against this database since it last said it had
1115            // nothing to collect, so it still has nothing to collect and the
1116            // line it lives on stays where it is.
1117            let at = self.slot_db(i);
1118            if self.dirty & (1 << at) == 0 {
1119                continue;
1120            }
1121            if let Some(moved) = self.slot_mut(i).compact_step() {
1122                self.next_db = (i + 1) % self.slots();
1123                return Some(moved);
1124            }
1125            // Only once every stripe of the database has said it has nothing,
1126            // since the bit is per database and one stripe answering for all of
1127            // them would stop the others being asked at all.
1128            if i % self.width == self.width - 1 {
1129                self.dirty &= !(1u64 << at);
1130            }
1131        }
1132        None
1133    }
1134}
1135
1136impl Default for Server {
1137    fn default() -> Server {
1138        Server::new()
1139    }
1140}
1141
1142/// What one connection has chosen.
1143pub struct Session {
1144    db: usize,
1145    id: u64,
1146    name: Vec<u8>,
1147    /// The `HIMPORT` fieldsets this connection has prepared.
1148    ///
1149    /// Connection state and not keyspace state, which is the reference's design
1150    /// and not a shortcut: a fieldset is invisible to every other connection and
1151    /// the keys built from one outlive it.
1152    sets: himport::Fieldsets,
1153}
1154
1155impl Session {
1156    /// A new connection, on database zero with no name.
1157    #[must_use]
1158    pub fn new(id: u64) -> Session {
1159        Session {
1160            db: 0,
1161            id,
1162            name: Vec::new(),
1163            sets: himport::Fieldsets::default(),
1164        }
1165    }
1166
1167    /// The connection id, which `HELLO` reports and `CLIENT` will.
1168    #[must_use]
1169    pub const fn id(&self) -> u64 {
1170        self.id
1171    }
1172
1173    /// Which database this connection is working in.
1174    #[must_use]
1175    pub const fn db(&self) -> usize {
1176        self.db
1177    }
1178
1179    /// The name the client gave itself, empty if it gave none.
1180    #[must_use]
1181    pub fn name(&self) -> &[u8] {
1182        &self.name
1183    }
1184
1185    /// Put everything back the way it was when the connection was opened.
1186    ///
1187    /// The protocol is not here because it is not here: it lives in the reply
1188    /// buffer, and `RESET` sets it back there.
1189    pub fn reset(&mut self) {
1190        self.db = 0;
1191        self.name.clear();
1192        // `SELECT` leaves these alone and `RESET` does not, both checked
1193        // against 8.10.1, which is the one pair of answers you could not guess
1194        // from what the command is for.
1195        self.sets.clear();
1196    }
1197
1198    /// Record the name from `HELLO ... SETNAME`.
1199    fn set_name(&mut self, name: &[u8]) {
1200        yo_alloc::allow(|| {
1201            self.name.clear();
1202            self.name.extend_from_slice(name);
1203        });
1204    }
1205}
1206
1207/// Run one command and write its reply.
1208///
1209/// The name is looked up and the arity is checked here, once, so that no body
1210/// has to. Everything after that is the command's own.
1211pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1212    // The decoder never produces a command with no name. If one ever arrives,
1213    // it is not something to answer.
1214    if args.is_empty() {
1215        return Flow::Continue;
1216    }
1217    resolved(server, session, lookup(args.name()), args, out)
1218}
1219
1220/// The same, for a caller that has already found the command.
1221///
1222/// The engine frames a command before it runs it, and between those two it also
1223/// asks which key the command touches so the record can be prefetched. That is
1224/// two more chances to look the name up, and looking it up three times to run it
1225/// once is three times the cost of the cheapest thing in the path. So the engine
1226/// resolves the name where it frames the command, carries the answer on the
1227/// framed command, and both the other two take it from there.
1228///
1229/// `spec` is `None` for a name that is not a command, which is the same thing
1230/// [`lookup`] says and lands in the same reply.
1231pub fn resolved(
1232    server: &mut Server,
1233    session: &mut Session,
1234    spec: Option<&'static Spec>,
1235    args: Args<'_>,
1236    out: &mut Out,
1237) -> Flow {
1238    if args.is_empty() {
1239        return Flow::Continue;
1240    }
1241    server.stats.commands += 1;
1242
1243    let Some(spec) = spec else {
1244        write_error(out, &args::unknown_command(args));
1245        return Flow::Continue;
1246    };
1247    if !arity_ok(spec, args.len()) {
1248        server.cmdstats.at(spec).rejected += 1;
1249        write_error(out, &args::wrong_arity(spec.name));
1250        return Flow::Continue;
1251    }
1252
1253    // The limit first, so a server with no `maxmemory`, which is the default and
1254    // is nearly all of them, pays one comparison against a field that is already
1255    // warm. Every command and not only the writes, because that is where Redis
1256    // puts it: making room is the server's job whatever the client asked for,
1257    // and the flag only decides who gets told no when there is no room to make.
1258    //
1259    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1260    // Redis's list, so a command that only frees is let through with nothing
1261    // left, which is what lets a client dig itself out with `DEL`.
1262    if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1263        server.cmdstats.at(spec).rejected += 1;
1264        out.error_line(b"OOM ", OOM);
1265        return Flow::Continue;
1266    }
1267
1268    // Which databases the maintenance turn after this batch has to ask. Marked
1269    // for every command and not only for the writes, because a read can make
1270    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1271    // record it dropped is exactly the kind of thing the collector is for.
1272    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1273    // two groups that hold them mark all of them rather than the session's.
1274    server.dirty |= match spec.group {
1275        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1276        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" | "ts" => {
1277            1u64 << session.db
1278        }
1279        _ => ALL_DATABASES,
1280    };
1281
1282    let mark = out.len();
1283    // Before the group, because the five that block are list commands and would
1284    // otherwise land in `lists`, which is handed one database and nothing that
1285    // could park a client. The flag is the right thing to branch on rather than
1286    // a list of names: it is what `COMMAND INFO` reports about exactly these
1287    // commands, and the sorted set and stream ones that arrive later carry it
1288    // too.
1289    let done = if spec.flags.contains(&"blocking") {
1290        blocking::execute(server, session, spec, args, out)
1291    } else {
1292        match spec.group {
1293            "string" => {
1294                let db = session.db;
1295                strings::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1296            }
1297            // Its own group and its own file, and the same values underneath:
1298            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1299            // something a `SET` left behind works.
1300            "bitmap" => {
1301                let db = session.db;
1302                bits::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1303            }
1304            // The same again: a sketch is a string with a documented layout, so
1305            // `GET` hands one to a client and `SET` takes it back.
1306            "hyperloglog" => {
1307                let db = session.db;
1308                hll::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1309            }
1310            "set" => {
1311                let db = session.db;
1312                sets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1313            }
1314            // The one hash command whose state is not in the keyspace. A
1315            // fieldset belongs to the connection, so this is handed the session
1316            // as well as the database, the same exception `MIGRATE` gets in the
1317            // keyspace group for the socket it keeps.
1318            "hash" if spec.name == "himport" => {
1319                let db = session.db;
1320                himport::execute(&server.dbs[db], &mut session.sets, args, out)
1321                    .map(|()| Flow::Continue)
1322            }
1323            "hash" => {
1324                let db = session.db;
1325                hashes::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1326            }
1327            "list" => {
1328                let db = session.db;
1329                lists::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1330            }
1331            "zset" => {
1332                let db = session.db;
1333                zsets::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1334            }
1335            // A geo key is a sorted set and these are sorted set commands with
1336            // arithmetic on the way in and on the way out, so a client can ZREM
1337            // a place out of one and ZCARD it to count them.
1338            "geo" => {
1339                let db = session.db;
1340                geo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1341            }
1342            "array" => {
1343                let db = session.db;
1344                arrays::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1345            }
1346            "graph" => {
1347                let db = session.db;
1348                graph::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1349            }
1350            // A document under a key, reached by a path. The group is Redis's
1351            // module surface and the storage is ours, the same trade the vector
1352            // set group makes.
1353            "json" => {
1354                let db = session.db;
1355                json::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1356            }
1357            "vector" => {
1358                let db = session.db;
1359                vectors::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1360            }
1361            "bloom" => {
1362                let db = session.db;
1363                bloom::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1364            }
1365            "cuckoo" => {
1366                let db = session.db;
1367                cuckoo::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1368            }
1369            "cms" => {
1370                let db = session.db;
1371                cms::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1372            }
1373            "topk" => {
1374                let db = session.db;
1375                topk::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1376            }
1377            "tdigest" => {
1378                let db = session.db;
1379                tdigest::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1380            }
1381            "ts" => {
1382                let db = session.db;
1383                ts::execute(&server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1384            }
1385            // The clock is read before the database is borrowed, because every
1386            // stream command needs the time and it lives on the server. An
1387            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1388            // `XINFO` reporting it all have to agree about what moment this is.
1389            "stream" => {
1390                let db = session.db;
1391                let now = server.now_ms();
1392                streams::execute(&server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1393            }
1394            // The one keyspace command that needs more than the databases,
1395            // because the socket it talks down is held on the server between
1396            // commands and not opened again for each one.
1397            "keyspace" if spec.name == "migrate" => {
1398                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1399            }
1400            // Every database and not the one the session is on, because `COPY` takes
1401            // a `DB n` and writes into a database nobody selected.
1402            "keyspace" => {
1403                keyspace::execute(&server.dbs, session.db, spec, args, out).map(|()| Flow::Continue)
1404            }
1405            // No database at all, because an index is not a key. The registry
1406            // is the whole of what these sixteen commands touch.
1407            "search" => {
1408                search::execute(&mut server.search, spec, args, out).map(|()| Flow::Continue)
1409            }
1410            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1411            _ => server::execute(server, session, spec, args, out),
1412        }
1413    };
1414    let flow = match done {
1415        Ok(flow) => flow,
1416        Err(e) => {
1417            out.truncate(mark);
1418            write_error(out, &e);
1419            Flow::Continue
1420        }
1421    };
1422
1423    // Counted here and not before the call, which is where Redis counts it, so
1424    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1425    // same way theirs does.
1426    //
1427    // Failure is read off the reply rather than off the `Result`, because the
1428    // two are not the same set. A command that ran out of arguments comes back
1429    // as an `Err` and a command that was sent the wrong password writes its own
1430    // error line and comes back `Ok`, and both of those are a call that failed.
1431    // The first byte at the mark is what a client would branch on, and it is `-`
1432    // for an error on either protocol and `!` for RESP3's long form.
1433    let row = server.cmdstats.at(spec);
1434    row.calls += 1;
1435    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1436        row.failed += 1;
1437    }
1438    flow
1439}
1440
1441/// The error line for an error value.
1442///
1443/// The prefix is what a client branches on, and there are three of them:
1444/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1445/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1446/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1447/// than routed through here. `OOM` is not a [`Code`] of its own because
1448/// [`Code::Full`] already covers the string that is too long for
1449/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1450fn write_error(out: &mut Out, e: &Error) {
1451    let prefix: &[u8] = match e.code() {
1452        Code::WrongType => b"WRONGTYPE ",
1453        // Only the HyperLogLog commands answer this one, and the prefix is the
1454        // sentence a client branches on to tell a sketch it cannot read from a
1455        // sketch it sent wrong.
1456        Code::Corrupt => b"INVALIDOBJ ",
1457        _ => b"ERR ",
1458    };
1459    out.error_line(prefix, e.message().as_bytes());
1460}
1461
1462#[cfg(test)]
1463mod tests {
1464    use super::*;
1465    use crate::proto::{Limits, Proto};
1466    use crate::request::Argv;
1467
1468    /// Build the wire bytes for a command.
1469    ///
1470    /// Tests go through the codec rather than around it, so an argument in a
1471    /// test is the same borrowed slice a connection produces.
1472    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1473        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1474        for p in parts {
1475            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1476            wire.extend_from_slice(p);
1477            wire.extend_from_slice(b"\r\n");
1478        }
1479        wire
1480    }
1481
1482    /// A server, a connection and a buffer, driven the way the reactor will.
1483    struct Fixture {
1484        server: Server,
1485        session: Session,
1486        argv: Argv,
1487        out: Out,
1488    }
1489
1490    impl Fixture {
1491        fn new() -> Fixture {
1492            Fixture::on(Server::new())
1493        }
1494
1495        /// The same, on a server whose databases are cut into `width` stripes.
1496        fn striped(width: usize) -> Fixture {
1497            Fixture::on(Server::with_width(width))
1498        }
1499
1500        fn on(server: Server) -> Fixture {
1501            Fixture {
1502                server,
1503                session: Session::new(7),
1504                argv: Argv::new(),
1505                out: Out::new(Proto::Resp2),
1506            }
1507        }
1508
1509        /// Run one command and answer with the bytes it wrote.
1510        fn run(&mut self, parts: &[&[u8]]) -> String {
1511            self.flow(parts).1
1512        }
1513
1514        /// Run one command and answer with the bytes exactly as written.
1515        ///
1516        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
1517        /// every reply that is text and destroys a `DUMP` payload, since a
1518        /// payload is arbitrary bytes and a checksum on the end of them.
1519        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1520            let wire = encode(parts);
1521            self.argv.decode(&wire, &Limits::default()).unwrap();
1522            self.out.clear();
1523            execute(
1524                &mut self.server,
1525                &mut self.session,
1526                Args::new(&self.argv, &wire),
1527                &mut self.out,
1528            );
1529            self.out.as_slice().to_vec()
1530        }
1531
1532        /// Move every clock in the server on by `ms`.
1533        fn advance(&mut self, ms: u64) {
1534            self.server.advance_clock_ms(ms);
1535        }
1536
1537        /// The same, with what the connection should do next.
1538        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1539            let wire = encode(parts);
1540            self.argv.decode(&wire, &Limits::default()).unwrap();
1541            self.out.clear();
1542            let flow = execute(
1543                &mut self.server,
1544                &mut self.session,
1545                Args::new(&self.argv, &wire),
1546                &mut self.out,
1547            );
1548            (
1549                flow,
1550                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1551            )
1552        }
1553    }
1554
1555    /// What a client does all day: write the same keys again and again. Every
1556    /// one of those writes leaves the previous record behind, so a server that
1557    /// never compacts holds every version of every key it has ever been sent.
1558    #[test]
1559    fn rewriting_the_same_keys_does_not_grow_the_server() {
1560        let mut f = Fixture::new();
1561        let val = vec![b'v'; 1024];
1562        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1563
1564        for k in &keys {
1565            f.run(&[b"SET", k, &val]);
1566        }
1567        f.server.compact_step();
1568        let after_first = f.server.memory_bytes();
1569
1570        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
1571        // of it. Thirty two megabytes written to hold sixty four kilobytes,
1572        // which is the shape of a real workload and is enough churn to fill
1573        // sixteen segments if nothing ever comes back.
1574        for _ in 0..500 {
1575            for k in &keys {
1576                f.run(&[b"SET", k, &val]);
1577            }
1578            f.server.compact_step();
1579        }
1580
1581        assert!(
1582            f.server.memory_bytes() <= after_first * 2,
1583            "held {} after five hundred passes against {after_first} after one",
1584            f.server.memory_bytes()
1585        );
1586        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1587        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1588    }
1589
1590    /// The same churn on a database nobody starts on, either side of a quiet
1591    /// spell long enough for the maintenance turn to stop asking about it.
1592    ///
1593    /// The turn after each batch skips a database that has already said it has
1594    /// nothing to collect and has not been touched since, which is what keeps a
1595    /// server whose clients are all on database zero from loading and storing
1596    /// in the other fifteen every batch to be told no. Two things could go
1597    /// wrong with that. A database might never be marked at all, so this uses
1598    /// database nine, which nothing marks by accident. And a database whose
1599    /// mark was cleared might never get it back, so this drains the collector
1600    /// until it says there is nothing left, checks the mark really is gone, and
1601    /// then writes another thirty two megabytes through the same sixty four
1602    /// keys. If either went wrong the server would hold all of it.
1603    #[test]
1604    fn a_database_nobody_started_on_is_still_collected() {
1605        let mut f = Fixture::new();
1606        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1607        let val = vec![b'v'; 1024];
1608        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1609
1610        for k in &keys {
1611            f.run(&[b"SET", k, &val]);
1612        }
1613        while f.server.compact_step().is_some() {}
1614        assert_eq!(
1615            f.server.dirty & (1 << 9),
1616            0,
1617            "database nine was drained and should not be asked again until it is written to"
1618        );
1619        let after_first = f.server.memory_bytes();
1620
1621        for _ in 0..500 {
1622            for k in &keys {
1623                f.run(&[b"SET", k, &val]);
1624            }
1625            f.server.compact_step();
1626        }
1627
1628        assert!(
1629            f.server.memory_bytes() <= after_first * 2,
1630            "held {} after five hundred passes against {after_first} after one",
1631            f.server.memory_bytes()
1632        );
1633        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1634        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1635        // And nothing landed anywhere else on the way.
1636        f.run(&[b"SELECT", b"0"]);
1637        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1638    }
1639
1640    #[test]
1641    fn a_command_goes_from_bytes_to_bytes() {
1642        let mut f = Fixture::new();
1643        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1644        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1645        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1646        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1647        // The name is matched whatever case it came in, and so are the options.
1648        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1649        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1650    }
1651
1652    #[test]
1653    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1654        let mut f = Fixture::new();
1655        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1656        // A key named twice exists twice and can only be deleted once, and both
1657        // of those are Redis's answers rather than tidier ones.
1658        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1659        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1660        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1661        // UNLINK is the same body and reports the same way.
1662        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1663        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1664    }
1665
1666    #[test]
1667    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1668        let mut f = Fixture::new();
1669        f.run(&[b"SET", b"k", b"v"]);
1670        // A simple string on both protocols, which is unusual: most replies
1671        // that carry a word are bulk strings.
1672        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1673        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1674    }
1675
1676    #[test]
1677    fn touch_counts_the_way_exists_counts() {
1678        let mut f = Fixture::new();
1679        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1680        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1681        assert_eq!(
1682            f.run(&[b"TOUCH", b"a", b"a"]),
1683            ":2\r\n",
1684            "twice counts twice"
1685        );
1686        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1687        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1688    }
1689
1690    #[test]
1691    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1692        let mut f = Fixture::new();
1693        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1694        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1695
1696        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1697        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1698        assert_eq!(
1699            f.run(&[b"TTL", b"b"]),
1700            ":100\r\n",
1701            "the source's and not b's"
1702        );
1703        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1704    }
1705
1706    #[test]
1707    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1708        let mut f = Fixture::new();
1709        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1710        // The source is checked before the destination, so this is the error
1711        // and not the zero RENAMENX would otherwise answer for a taken name.
1712        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1713    }
1714
1715    #[test]
1716    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1717        let mut f = Fixture::new();
1718        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1719
1720        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1721        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1722        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
1723        // one call the two disagree about and neither does any work for.
1724        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1725        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1726        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1727        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1728    }
1729
1730    #[test]
1731    fn renaming_a_set_does_not_touch_a_member() {
1732        let mut f = Fixture::new();
1733        for i in 0..300 {
1734            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1735        }
1736        let before = f.server.memory_bytes();
1737
1738        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1739        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1740        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1741        assert!(
1742            f.server.memory_bytes().abs_diff(before) < 256,
1743            "the members were copied: {} against {before}",
1744            f.server.memory_bytes()
1745        );
1746    }
1747
1748    #[test]
1749    fn a_copy_is_a_second_value_and_not_a_second_name() {
1750        let mut f = Fixture::new();
1751        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1752
1753        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1754        f.run(&[b"SADD", b"t", b"m3"]);
1755        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1756        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1757    }
1758
1759    /// Every type a key can hold, copied, because two of them used to panic.
1760    ///
1761    /// `COPY` reads the value out of the source through one match on the type
1762    /// tag, and that match had a catch all at the bottom from back when a set
1763    /// and a hash were the only bodies. The list and the sorted set landed after
1764    /// it and nobody came back, so `COPY mylist other` took the shard down. It
1765    /// is an ordinary command against a type the server supports everywhere
1766    /// else, so this walks all five rather than the two that were broken: the
1767    /// point is that the next type cannot land the same way.
1768    #[test]
1769    fn every_type_can_be_copied() {
1770        let mut f = Fixture::new();
1771        f.run(&[b"SET", b"str", b"v1"]);
1772        f.run(&[b"SADD", b"set", b"m1"]);
1773        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1774        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1775        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1776
1777        for name in [
1778            &b"str"[..],
1779            &b"set"[..],
1780            &b"hash"[..],
1781            &b"list"[..],
1782            &b"zset"[..],
1783        ] {
1784            let dst = [name, b":copy"].concat();
1785            assert_eq!(
1786                f.run(&[b"COPY", name, &dst]),
1787                ":1\r\n",
1788                "copying {}",
1789                String::from_utf8_lossy(name)
1790            );
1791            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1792        }
1793
1794        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1795            let mut want = String::from("*2\r\n");
1796            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1797            want
1798        });
1799        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1800
1801        // And the copy is its own value, not a second name for the source.
1802        f.run(&[b"RPUSH", b"list:copy", b"c"]);
1803        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1804        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1805    }
1806
1807    #[test]
1808    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1809        let mut f = Fixture::new();
1810        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1811        f.run(&[b"SET", b"b", b"v2"]);
1812
1813        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1814        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1815        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1816        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1817        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1818        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1819    }
1820
1821    #[test]
1822    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1823        let mut f = Fixture::new();
1824        f.run(&[b"SET", b"a", b"v1"]);
1825
1826        // Same key, different database, so this is not the same object and is
1827        // an ordinary copy. Same key in the same database is the error below.
1828        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1829        f.run(&[b"SELECT", b"1"]);
1830        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1831        assert_eq!(
1832            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1833            ":0\r\n",
1834            "taken"
1835        );
1836        assert_eq!(
1837            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1838            ":1\r\n"
1839        );
1840    }
1841
1842    #[test]
1843    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1844        let mut f = Fixture::new();
1845        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1846        assert_eq!(
1847            f.run(&[b"SORT", b"l"]),
1848            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1849        );
1850        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
1851        assert_eq!(
1852            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1853            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1854        );
1855        assert_eq!(
1856            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1857            "*1\r\n$1\r\n2\r\n"
1858        );
1859    }
1860
1861    #[test]
1862    fn sort_reads_a_key_per_element_for_by_and_for_get() {
1863        let mut f = Fixture::new();
1864        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1865        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1866        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
1867        // misses, which is a nil in the middle of the array and not a short one.
1868        assert_eq!(
1869            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1870            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1871        );
1872    }
1873
1874    #[test]
1875    fn sort_store_writes_a_list_and_answers_its_length() {
1876        let mut f = Fixture::new();
1877        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1878        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1879        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1880        assert_eq!(
1881            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1882            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1883        );
1884        // An empty result takes the destination with it rather than leaving a
1885        // list that holds nothing.
1886        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1887        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1888    }
1889
1890    #[test]
1891    fn sort_ro_does_not_know_the_word_store() {
1892        let mut f = Fixture::new();
1893        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1894        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1895        assert_eq!(
1896            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1897            "-ERR syntax error\r\n"
1898        );
1899        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1900    }
1901
1902    #[test]
1903    fn sort_refuses_what_it_cannot_sort() {
1904        let mut f = Fixture::new();
1905        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1906        f.run(&[b"SET", b"s", b"x"]);
1907        assert_eq!(
1908            f.run(&[b"SORT", b"s"]),
1909            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1910        );
1911        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1912        assert_eq!(
1913            f.run(&[b"SORT", b"words"]),
1914            "-ERR One or more scores can't be converted into double\r\n"
1915        );
1916        assert_eq!(
1917            f.run(&[b"SORT", b"words", b"ALPHA"]),
1918            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1919        );
1920        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1921    }
1922
1923    #[test]
1924    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1925        let mut f = Fixture::new();
1926        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1927        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1928        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1929        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1930        assert_eq!(
1931            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1932            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1933        );
1934        // And back, which proves the body survived the trip rather than being
1935        // rebuilt from a copy that happened to look the same.
1936        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1937        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1938    }
1939
1940    #[test]
1941    fn move_answers_zero_when_either_end_says_no() {
1942        let mut f = Fixture::new();
1943        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1944        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1945        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1946        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1947        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1948        // The destination is taken, so nothing moves and the source is still
1949        // there with what it had.
1950        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1951        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1952        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1953        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1954    }
1955
1956    #[test]
1957    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1958        let mut f = Fixture::new();
1959        assert_eq!(
1960            f.run(&[b"MOVE", b"a", b"0"]),
1961            "-ERR source and destination objects are the same\r\n"
1962        );
1963        assert_eq!(
1964            f.run(&[b"MOVE", b"a", b"99"]),
1965            "-ERR DB index is out of range\r\n"
1966        );
1967        assert_eq!(
1968            f.run(&[b"MOVE", b"a", b"-1"]),
1969            "-ERR DB index is out of range\r\n"
1970        );
1971        assert_eq!(
1972            f.run(&[b"MOVE", b"a", b"x"]),
1973            "-ERR value is not an integer or out of range\r\n"
1974        );
1975    }
1976
1977    #[test]
1978    fn swapdb_swaps_what_two_connections_would_see() {
1979        let mut f = Fixture::new();
1980        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1981        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1982        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1983        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1984
1985        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1986        // Still on database zero, and database zero is a different database.
1987        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1988        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1989        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1990        // A database swapped with itself is fine and changes nothing.
1991        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1992        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1993    }
1994
1995    #[test]
1996    fn swapdb_says_which_index_it_could_not_read() {
1997        let mut f = Fixture::new();
1998        assert_eq!(
1999            f.run(&[b"SWAPDB", b"x", b"1"]),
2000            "-ERR invalid first DB index\r\n"
2001        );
2002        assert_eq!(
2003            f.run(&[b"SWAPDB", b"0", b"y"]),
2004            "-ERR invalid second DB index\r\n"
2005        );
2006        // A number too big to be an index on a server that keeps one in an int
2007        // is the same complaint, and a plausible one that is not ours is the
2008        // range complaint instead. The split is Redis's.
2009        assert_eq!(
2010            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
2011            "-ERR invalid first DB index\r\n"
2012        );
2013        assert_eq!(
2014            f.run(&[b"SWAPDB", b"0", b"99"]),
2015            "-ERR DB index is out of range\r\n"
2016        );
2017        assert_eq!(
2018            f.run(&[b"SWAPDB", b"-1", b"0"]),
2019            "-ERR DB index is out of range\r\n"
2020        );
2021    }
2022
2023    #[test]
2024    fn wait_answers_zero_replicas_without_waiting() {
2025        let mut f = Fixture::new();
2026        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
2027        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
2028        // A replica that is never going to arrive, and a timeout that would be
2029        // a real wait on a server that had one.
2030        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
2031        // Negative replicas is not an error, because zero is already more than
2032        // it asked for.
2033        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
2034        assert_eq!(
2035            f.run(&[b"WAIT", b"x", b"0"]),
2036            "-ERR value is not an integer or out of range\r\n"
2037        );
2038        assert_eq!(
2039            f.run(&[b"WAIT", b"0", b"-1"]),
2040            "-ERR timeout is negative\r\n"
2041        );
2042        assert_eq!(
2043            f.run(&[b"WAIT", b"0", b"1.5"]),
2044            "-ERR timeout is not an integer or out of range\r\n"
2045        );
2046    }
2047
2048    #[test]
2049    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
2050        let mut f = Fixture::new();
2051        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
2052        assert_eq!(
2053            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
2054            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
2055        );
2056        assert_eq!(
2057            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
2058            "-ERR value is out of range, value must between 0 and 1\r\n"
2059        );
2060        assert_eq!(
2061            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
2062            "-ERR value is out of range, must be positive\r\n"
2063        );
2064        // The arguments are all read before the server looks at itself, so a
2065        // bad timeout beats the append only complaint even with numlocal set.
2066        assert_eq!(
2067            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
2068            "-ERR timeout is negative\r\n"
2069        );
2070    }
2071
2072    /// The bytes inside a bulk reply, with the header and the trailing break
2073    /// taken off. Every `DUMP` test needs this and none of them care how the
2074    /// length was written.
2075    fn payload(reply: &[u8]) -> Vec<u8> {
2076        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
2077        reply[head + 2..reply.len() - 2].to_vec()
2078    }
2079
2080    #[test]
2081    fn a_value_survives_a_dump_and_a_restore() {
2082        let mut f = Fixture::new();
2083        f.run(&[b"SET", b"s", b"hello"]);
2084        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
2085        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
2086        f.run(&[b"SADD", b"u", b"x", b"y"]);
2087        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
2088        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
2089
2090        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
2091            let mut copy = key.to_vec();
2092            copy.push(b'2');
2093            let bytes = payload(&f.raw(&[b"DUMP", key]));
2094            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
2095            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
2096        }
2097
2098        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
2099        assert_eq!(
2100            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
2101            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
2102        );
2103        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
2104        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
2105        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
2106        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
2107        // The encoding survives too, since the payload names the plainest legal
2108        // type and the loader puts the value back on the rung it belongs on.
2109        assert_eq!(
2110            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
2111            f.run(&[b"OBJECT", b"ENCODING", b"t"])
2112        );
2113    }
2114
2115    #[test]
2116    fn a_dumped_hash_keeps_its_field_deadlines() {
2117        let mut f = Fixture::new();
2118        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
2119        assert_eq!(
2120            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
2121            "*1\r\n:1\r\n"
2122        );
2123        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
2124        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
2125        assert_eq!(
2126            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
2127            "*2\r\n:-1\r\n:100\r\n"
2128        );
2129    }
2130
2131    #[test]
2132    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2133        let mut f = Fixture::new();
2134        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2135        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2136        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2137        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2138        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2139        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2140        // An absolute deadline that has already gone is not an error. The key is
2141        // not created and the reply is the same OK a live one gets.
2142        assert_eq!(
2143            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2144            "+OK\r\n"
2145        );
2146        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2147    }
2148
2149    #[test]
2150    fn dump_answers_nothing_for_a_key_that_is_not_there() {
2151        let mut f = Fixture::new();
2152        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2153        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2154        f.advance(50);
2155        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2156    }
2157
2158    #[test]
2159    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2160        let mut f = Fixture::new();
2161        f.run(&[b"SET", b"a", b"first"]);
2162        f.run(&[b"SET", b"b", b"second"]);
2163        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2164        assert_eq!(
2165            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2166            "-BUSYKEY Target key name already exists.\r\n"
2167        );
2168        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2169        assert_eq!(
2170            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2171            "+OK\r\n"
2172        );
2173        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2174    }
2175
2176    /// The busy key comes before the payload, which is not the order the
2177    /// arguments read in. Whether a key is taken should not depend on whether
2178    /// the bytes behind it happened to be good.
2179    #[test]
2180    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2181        let mut f = Fixture::new();
2182        f.run(&[b"SET", b"a", b"v"]);
2183        assert_eq!(
2184            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2185            "-BUSYKEY Target key name already exists.\r\n"
2186        );
2187        // And the options come before even that, so a bad FREQ beats the busy
2188        // key the same way a bad DB beats a missing source in COPY.
2189        assert_eq!(
2190            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2191            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2192        );
2193    }
2194
2195    #[test]
2196    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2197        let mut f = Fixture::new();
2198        f.run(&[b"SET", b"a", b"hello"]);
2199        let good = payload(&f.raw(&[b"DUMP", b"a"]));
2200
2201        let mut flipped = good.clone();
2202        flipped[2] ^= 0x40;
2203        assert_eq!(
2204            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2205            "-ERR DUMP payload version or checksum are wrong\r\n"
2206        );
2207        assert_eq!(
2208            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2209            "-ERR DUMP payload version or checksum are wrong\r\n"
2210        );
2211        // A footer that is right over a body that is not. The type byte says
2212        // string and there is nothing behind it, so the checksum agrees and the
2213        // value does not exist.
2214        let mut truncated = good[..1].to_vec();
2215        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2216        let crc = yo_common::crc::crc64(0, &truncated);
2217        truncated.extend_from_slice(&crc.to_le_bytes());
2218        assert_eq!(
2219            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2220            "-ERR Bad data format\r\n"
2221        );
2222        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2223    }
2224
2225    #[test]
2226    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2227        let mut f = Fixture::new();
2228        f.run(&[b"SET", b"a", b"v"]);
2229        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2230        assert_eq!(
2231            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2232            "-ERR Invalid TTL value, must be >= 0\r\n"
2233        );
2234        assert_eq!(
2235            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2236            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2237        );
2238        assert_eq!(
2239            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2240            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2241        );
2242        // Both are accepted and both are then dropped, which is D-26.
2243        assert_eq!(
2244            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2245            "+OK\r\n"
2246        );
2247        assert_eq!(
2248            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2249            "+OK\r\n"
2250        );
2251    }
2252
2253    /// Neither word is refused for being the wrong one. Each is only accepted
2254    /// while the other is unset, so the second of the two falls through to the
2255    /// plain syntax error rather than getting a message of its own.
2256    #[test]
2257    fn restore_takes_idletime_or_freq_and_not_both() {
2258        let mut f = Fixture::new();
2259        f.run(&[b"SET", b"a", b"v"]);
2260        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2261        assert_eq!(
2262            f.run(&[
2263                b"RESTORE",
2264                b"b",
2265                b"0",
2266                &bytes,
2267                b"IDLETIME",
2268                b"1",
2269                b"FREQ",
2270                b"2"
2271            ]),
2272            "-ERR syntax error\r\n"
2273        );
2274        assert_eq!(
2275            f.run(&[
2276                b"RESTORE",
2277                b"b",
2278                b"0",
2279                &bytes,
2280                b"FREQ",
2281                b"2",
2282                b"IDLETIME",
2283                b"1"
2284            ]),
2285            "-ERR syntax error\r\n"
2286        );
2287        assert_eq!(
2288            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2289            "-ERR syntax error\r\n"
2290        );
2291        assert_eq!(
2292            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2293            "-ERR syntax error\r\n"
2294        );
2295    }
2296
2297    #[test]
2298    fn copy_checks_its_options_before_it_looks_for_anything() {
2299        let mut f = Fixture::new();
2300        // No key exists at all, and every one of these is still the option
2301        // complaint rather than a zero, which is the order a real server uses.
2302        assert_eq!(
2303            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2304            "-ERR DB index is out of range\r\n"
2305        );
2306        assert_eq!(
2307            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2308            "-ERR DB index is out of range\r\n"
2309        );
2310        assert_eq!(
2311            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2312            "-ERR value is not an integer or out of range\r\n"
2313        );
2314        assert_eq!(
2315            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2316            "-ERR syntax error\r\n"
2317        );
2318        assert_eq!(
2319            f.run(&[b"COPY", b"a", b"a"]),
2320            "-ERR source and destination objects are the same\r\n"
2321        );
2322        // Repeated, reordered and lowercased, and the last DB wins.
2323        assert_eq!(
2324            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2325            ":0\r\n"
2326        );
2327    }
2328
2329    #[test]
2330    fn time_is_two_bulk_strings_and_moves() {
2331        let mut f = Fixture::new();
2332        let first = f.run(&[b"TIME"]);
2333        assert!(first.starts_with("*2\r\n$"), "got {first}");
2334        let parts: Vec<&str> = first.split("\r\n").collect();
2335        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2336        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2337        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2338        assert!((0..1_000_000).contains(&micros), "got {micros}");
2339        // The coarse clock the keyspace uses is a cached millisecond that a
2340        // background tick refreshes, so a TIME built on it would answer the
2341        // same microsecond twice in a row here.
2342        assert_ne!(first, f.run(&[b"TIME"]));
2343    }
2344
2345    #[test]
2346    fn a_keyspace_scan_walks_every_key_once() {
2347        let mut f = Fixture::new();
2348        for i in 0..500 {
2349            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2350        }
2351
2352        let mut seen: Vec<String> = Vec::new();
2353        let mut cursor = "0".to_owned();
2354        let mut calls = 0;
2355        loop {
2356            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2357            seen.extend(keys);
2358            cursor = next;
2359            calls += 1;
2360            assert!(calls < 10_000, "the cursor is not advancing");
2361            if cursor == "0" {
2362                break;
2363            }
2364        }
2365
2366        seen.sort();
2367        seen.dedup();
2368        assert_eq!(seen.len(), 500, "every key once and only once");
2369        // And more than one call to get them, or the COUNT is being ignored and
2370        // the loop above proved nothing about resuming.
2371        assert!(calls > 1, "500 keys came back in one batch");
2372    }
2373
2374    #[test]
2375    fn a_scan_narrows_by_pattern_and_by_type() {
2376        let mut f = Fixture::new();
2377        f.run(&[b"SET", b"str", b"v"]);
2378        f.run(&[b"SADD", b"members", b"a"]);
2379        f.run(&[b"HSET", b"fields", b"f", b"v"]);
2380
2381        let all = |f: &mut Fixture, args: &[&[u8]]| {
2382            let mut out: Vec<String> = Vec::new();
2383            let mut cursor = "0".to_owned();
2384            loop {
2385                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2386                line.extend_from_slice(args);
2387                let (next, keys) = scan_reply(&f.run(&line));
2388                out.extend(keys);
2389                cursor = next;
2390                if cursor == "0" {
2391                    break;
2392                }
2393            }
2394            out.sort();
2395            out
2396        };
2397
2398        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2399        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2400        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2401        // Case insensitive, the same as Redis's own comparison.
2402        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2403        // A type nothing can hold is not an error, it just matches nothing.
2404        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2405        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2406        // Both filters at once, and they are an and rather than an or.
2407        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2408    }
2409
2410    #[test]
2411    fn a_scan_says_what_is_wrong_with_it() {
2412        let mut f = Fixture::new();
2413        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2414        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2415        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2416        assert_eq!(
2417            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2418            "-ERR syntax error\r\n"
2419        );
2420        assert_eq!(
2421            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2422            "-ERR value is not an integer or out of range\r\n"
2423        );
2424        assert_eq!(
2425            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2426            "-ERR syntax error\r\n"
2427        );
2428        // A cursor the client made up is a cursor. It resumes somewhere
2429        // arbitrary and answers whatever is there, which is what Redis does and
2430        // is the only behaviour that does not need the server to remember every
2431        // cursor it has handed out.
2432        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2433    }
2434
2435    #[test]
2436    fn keys_and_randomkey_look_at_the_whole_database() {
2437        let mut f = Fixture::new();
2438        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2439        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2440
2441        for name in ["one", "two", "three"] {
2442            f.run(&[b"SET", name.as_bytes(), b"v"]);
2443        }
2444        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2445        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2446        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2447
2448        for _ in 0..50 {
2449            let got = f.run(&[b"RANDOMKEY"]);
2450            assert!(
2451                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2452                "got {got}"
2453            );
2454        }
2455    }
2456
2457    #[test]
2458    fn a_walk_does_not_answer_keys_that_have_expired() {
2459        let mut f = Fixture::new();
2460        f.run(&[b"SET", b"alive", b"v"]);
2461        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2462        f.server.advance_clock_ms(2);
2463        assert_eq!(
2464            f.run(&[b"DBSIZE"]),
2465            ":2\r\n",
2466            "nothing has collected it yet"
2467        );
2468
2469        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2470        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2471        assert_eq!(keys, ["alive"]);
2472        for _ in 0..20 {
2473            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2474        }
2475        // The walk collected it on the way past, which is what makes DBSIZE
2476        // here answer what Redis answers once its own cycle has been round.
2477        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2478    }
2479
2480    #[test]
2481    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2482        let mut f = Fixture::new();
2483        f.run(&[b"SET", b"k", b"v"]);
2484        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2485        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2486
2487        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2488        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2489        let ms = int(&f.run(&[b"PTTL", b"k"]));
2490        assert!((99_000..=100_000).contains(&ms), "got {ms}");
2491
2492        // The absolute pair, derived from the same one number the store kept.
2493        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2494        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2495        assert_eq!(at, (at_ms + 500) / 1000);
2496        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2497
2498        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2499        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2500        assert_eq!(
2501            f.run(&[b"PERSIST", b"k"]),
2502            ":0\r\n",
2503            "nothing to take off the second time"
2504        );
2505        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2506        assert_eq!(
2507            f.run(&[b"GET", b"k"]),
2508            "$1\r\nv\r\n",
2509            "and the value went through all of that untouched"
2510        );
2511    }
2512
2513    #[test]
2514    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2515        let mut f = Fixture::new();
2516        f.run(&[b"SET", b"str", b"v"]);
2517        f.run(&[b"SADD", b"set", b"a", b"b"]);
2518        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2519
2520        for key in [b"str".as_slice(), b"set", b"hash"] {
2521            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2522            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2523        }
2524        // The body is not touched by any of that, which is the whole reason the
2525        // deadline lives in the record and the body lives somewhere else.
2526        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2527        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2528        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2529    }
2530
2531    #[test]
2532    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2533        let mut f = Fixture::new();
2534        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2535            f.run(&[b"SET", key, b"v"]);
2536        }
2537        // Four ways of naming a moment that has passed, and all four are a
2538        // delete answering 1 rather than an error. Zero is a moment, minus one
2539        // is a moment, and the hash field commands refuse the negative one.
2540        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2541        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2542        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2543        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2544        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2545        assert_eq!(
2546            f.run(&[b"EXPIRE", b"a", b"100"]),
2547            ":0\r\n",
2548            "and the key really went, so there is nothing to put a deadline on"
2549        );
2550    }
2551
2552    #[test]
2553    fn the_four_conditions_decide_whether_the_deadline_moves() {
2554        let mut f = Fixture::new();
2555        f.run(&[b"SET", b"k", b"v"]);
2556
2557        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2558        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2559        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2560        assert_eq!(
2561            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2562            ":1\r\n",
2563            "no deadline reads as infinitely far away, so LT passes where GT fails"
2564        );
2565
2566        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2567        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2568        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2569        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2570        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2571        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2572
2573        // The condition is answered before the past check, so this is a 0 and
2574        // the key survives. The other order would delete it.
2575        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2576        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2577        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2578        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2579    }
2580
2581    #[test]
2582    fn the_conditions_are_a_set_and_not_a_keyword() {
2583        let mut f = Fixture::new();
2584        f.run(&[b"SET", b"k", b"v"]);
2585
2586        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2587        assert_eq!(
2588            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2589            ":0\r\n",
2590            "the same keyword twice means it once, and NX now has a deadline to fail on"
2591        );
2592
2593        // XX with LT is the one pair that is not either of them on its own: LT
2594        // alone would accept a key with no deadline and this does not.
2595        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2596        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2597        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2598        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2599        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2600        f.run(&[b"PERSIST", b"k"]);
2601        assert_eq!(
2602            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2603            ":0\r\n",
2604            "where LT on its own would have taken it"
2605        );
2606        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2607    }
2608
2609    #[test]
2610    fn a_key_is_gone_once_its_moment_passes() {
2611        let mut f = Fixture::new();
2612        f.run(&[b"SET", b"k", b"v"]);
2613        f.run(&[b"EXPIRE", b"k", b"100"]);
2614
2615        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2616        f.server.set_clock_ms(at as u64 + 1);
2617        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2618        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2619        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2620        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2621    }
2622
2623    #[test]
2624    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2625        let mut f = Fixture::new();
2626        f.run(&[b"SET", b"k", b"v"]);
2627        for (bad, want) in [
2628            (
2629                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2630                "-ERR value is not an integer or out of range\r\n",
2631            ),
2632            (
2633                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2634                "-ERR Unsupported option MAYBE\r\n",
2635            ),
2636            (
2637                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2638                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2639            ),
2640            (
2641                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2642                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2643            ),
2644            (
2645                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2646                "-ERR GT and LT options at the same time are not compatible\r\n",
2647            ),
2648            // Seconds that overflow when multiplied into milliseconds. Every
2649            // message names the command it came from.
2650            (
2651                &[b"EXPIRE", b"k", b"9223372036854775807"],
2652                "-ERR invalid expire time in 'expire' command\r\n",
2653            ),
2654            (
2655                &[b"EXPIREAT", b"k", b"9223372036854775807"],
2656                "-ERR invalid expire time in 'expireat' command\r\n",
2657            ),
2658            (
2659                &[b"PEXPIRE", b"k", b"9223372036854775807"],
2660                "-ERR invalid expire time in 'pexpire' command\r\n",
2661            ),
2662        ] {
2663            assert_eq!(f.run(bad), want, "for {bad:?}");
2664        }
2665        assert_eq!(
2666            f.run(&[b"TTL", b"k"]),
2667            ":-1\r\n",
2668            "and none of those put a deadline on anything"
2669        );
2670
2671        // The one of the four that has no arithmetic to overflow. Redis takes
2672        // it and holds the number as given, and a record here holds forty six
2673        // bits, so it lands in the year 4199 instead. D-17.
2674        assert_eq!(
2675            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2676            ":1\r\n"
2677        );
2678        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2679    }
2680
2681    #[test]
2682    fn flushing_empties_this_database_or_every_one_of_them() {
2683        let mut f = Fixture::new();
2684        f.run(&[b"SELECT", b"0"]);
2685        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2686        f.run(&[b"SELECT", b"1"]);
2687        f.run(&[b"SET", b"c", b"3"]);
2688        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2689        // ASYNC and SYNC are both taken and neither changes anything, since the
2690        // keyspace is empty before the OK goes out either way.
2691        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2692        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2693        // Only database one was emptied.
2694        f.run(&[b"SELECT", b"0"]);
2695        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2696        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2697        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2698        f.run(&[b"SELECT", b"1"]);
2699        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2700        // Anything else after the name is a syntax error, and so is a third
2701        // argument even when the second one is a word we take.
2702        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2703        assert_eq!(
2704            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2705            "-ERR syntax error\r\n"
2706        );
2707    }
2708
2709    #[test]
2710    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2711        let mut f = Fixture::new();
2712        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2713        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2714        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2715        // Nothing is cached, so nothing is there, one answer per hash asked
2716        // about.
2717        assert_eq!(
2718            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2719            "*2\r\n:0\r\n:0\r\n"
2720        );
2721        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2722        assert_eq!(
2723            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2724            "*0\r\n"
2725        );
2726        assert_eq!(
2727            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2728            "-ERR Library not found\r\n"
2729        );
2730
2731        // Redis's two messages here are its own, one per container, and one of
2732        // them reads like a typo.
2733        assert_eq!(
2734            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2735            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2736        );
2737        assert_eq!(
2738            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2739            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2740        );
2741        // A second argument after the mode is the generic one instead, because
2742        // the count is checked before the word is looked at.
2743        assert_eq!(
2744            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2745            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2746        );
2747        assert_eq!(
2748            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2749            "-ERR Unknown argument bogus\r\n"
2750        );
2751        assert_eq!(
2752            f.run(&[b"SCRIPT", b"EXISTS"]),
2753            "-ERR wrong number of arguments for 'script|exists' command\r\n"
2754        );
2755
2756        // The ones that need an interpreter are not here, and say so rather
2757        // than answering OK to a load that loaded nothing.
2758        assert_eq!(
2759            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2760            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2761        );
2762        assert_eq!(
2763            f.run(&[b"FUNCTION", b"STATS"]),
2764            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2765        );
2766    }
2767
2768    #[test]
2769    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2770        let mut f = Fixture::new();
2771        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2772        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2773        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2774        // Read back as a string it is still an integer, written out as digits
2775        // only because somebody asked for them.
2776        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2777        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2778        // A counter that is not a number is the error the store raises and this
2779        // layer only spells, which is the whole point of the split.
2780        f.run(&[b"SET", b"k", b"hello"]);
2781        assert_eq!(
2782            f.run(&[b"INCR", b"k"]),
2783            "-ERR value is not an integer or out of range\r\n"
2784        );
2785        assert_eq!(
2786            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2787            "-ERR increment would produce NaN or Infinity\r\n"
2788        );
2789    }
2790
2791    /// Every one of these was read off a running 8.8. They are the answers a
2792    /// client library's own test suite checks, and the shapes are not
2793    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
2794    /// integer, `INCREX` is a pair.
2795    #[test]
2796    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2797        let mut f = Fixture::new();
2798        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2799        // The same digest a real 8.8 answers for the same five bytes, which is
2800        // what makes `IFDEQ` usable against a mixed deployment.
2801        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2802        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2803        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2804        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2805        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2806        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2807        assert_eq!(
2808            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2809            "*2\r\n:1\r\n:0\r\n",
2810            "a refused increment reports the value it left alone and applied nothing"
2811        );
2812        assert_eq!(
2813            f.run(&[
2814                b"INCREX",
2815                b"n",
2816                b"BYINT",
2817                b"5",
2818                b"UBOUND",
2819                b"3",
2820                b"SATURATE"
2821            ]),
2822            "*2\r\n:3\r\n:2\r\n"
2823        );
2824        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2825        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2826    }
2827
2828    #[test]
2829    fn the_same_answers_come_out_in_resp3_spelling() {
2830        let mut f = Fixture::new();
2831        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2832        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2833        // A float counter is a double on RESP3 and the digits in a bulk string
2834        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
2835        assert_eq!(
2836            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2837            "*2\r\n,1.5\r\n,1.5\r\n"
2838        );
2839        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2840        // `RESET` puts the protocol back, which is the part that is easy to
2841        // miss and leaves a pooled connection speaking the wrong one.
2842        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2843        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2844    }
2845
2846    #[test]
2847    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2848        let mut f = Fixture::new();
2849        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2850        assert_eq!(flow, Flow::Continue);
2851        assert_eq!(
2852            reply,
2853            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2854        );
2855        // A name with a line ending in it cannot write its own frame into the
2856        // stream, which is the reason the error writer maps them to spaces.
2857        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2858        assert_eq!(reply.matches("\r\n").count(), 1);
2859    }
2860
2861    #[test]
2862    fn arity_is_checked_before_the_command_is() {
2863        let mut f = Fixture::new();
2864        assert_eq!(
2865            f.run(&[b"GET"]),
2866            "-ERR wrong number of arguments for 'get' command\r\n"
2867        );
2868        assert_eq!(
2869            f.run(&[b"MSET", b"k"]),
2870            "-ERR wrong number of arguments for 'mset' command\r\n"
2871        );
2872        // The table says `PING` takes one or more and a real server then
2873        // refuses three, which is the sort of thing that only shows up against
2874        // the real thing.
2875        assert_eq!(
2876            f.run(&[b"PING", b"a", b"b"]),
2877            "-ERR wrong number of arguments for 'ping' command\r\n"
2878        );
2879        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2880        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2881        // `DELEX` takes two or four and nothing between.
2882        assert_eq!(
2883            f.run(&[b"DELEX", b"k", b"IFEQ"]),
2884            "-ERR wrong number of arguments for 'delex' command\r\n"
2885        );
2886    }
2887
2888    /// The option rules, all of them measured against 8.8 rather than read off
2889    /// the documentation. The surprising one is that `SET` accepts the same
2890    /// keyword twice and `INCREX` does not.
2891    #[test]
2892    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2893        let mut f = Fixture::new();
2894        let syntax = "-ERR syntax error\r\n";
2895        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2896        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2897        assert_eq!(
2898            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2899            syntax
2900        );
2901        assert_eq!(
2902            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2903            syntax
2904        );
2905        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2906        // Twice is fine, and the last one wins.
2907        assert_eq!(
2908            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2909            "+OK\r\n"
2910        );
2911        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2912        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2913        // `INCREX` refuses what `SET` allows.
2914        assert_eq!(
2915            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2916            syntax
2917        );
2918        assert_eq!(
2919            f.run(&[b"INCREX", b"n", b"ENX"]),
2920            "-ERR ENX flag requires an expiration\r\n"
2921        );
2922        assert_eq!(
2923            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2924            "-ERR UBOUND is not an integer or out of range\r\n"
2925        );
2926        assert_eq!(
2927            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2928            "-ERR LBOUND can't be greater than UBOUND\r\n"
2929        );
2930        assert_eq!(
2931            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2932            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2933        );
2934    }
2935
2936    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
2937    /// key that is not there, which answers null without ever looking at the
2938    /// expiration it was given.
2939    #[test]
2940    fn the_expiry_rules_are_redis_own() {
2941        let mut f = Fixture::new();
2942        let bad = "-ERR invalid expire time in 'set' command\r\n";
2943        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2944        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2945        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2946        assert_eq!(
2947            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2948            bad
2949        );
2950        assert_eq!(
2951            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2952            "-ERR value is not an integer or out of range\r\n"
2953        );
2954        assert_eq!(
2955            f.run(&[b"SETEX", b"k", b"0", b"v"]),
2956            "-ERR invalid expire time in 'setex' command\r\n"
2957        );
2958        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2959        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2960        assert_eq!(
2961            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2962            "-ERR syntax error\r\n",
2963            "the option list is still checked before the key is looked up"
2964        );
2965        // A deadline in the past is accepted and the key goes with it.
2966        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2967        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2968        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2969    }
2970
2971    #[test]
2972    fn mset_takes_its_pairs_from_the_read_buffer() {
2973        let mut f = Fixture::new();
2974        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2975        assert_eq!(
2976            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2977            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2978        );
2979        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2980        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2981        assert_eq!(
2982            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2983            "-ERR wrong number of key-value pairs\r\n"
2984        );
2985        assert_eq!(
2986            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2987            "-ERR invalid numkeys value\r\n"
2988        );
2989        assert_eq!(
2990            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2991            "-ERR invalid numkeys value\r\n"
2992        );
2993    }
2994
2995    #[test]
2996    fn lcs_answers_the_length_the_string_and_the_runs() {
2997        let mut f = Fixture::new();
2998        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2999        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
3000        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
3001        assert_eq!(
3002            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
3003            "*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"
3004        );
3005        // Without `IDX` the two options that only mean something with it are
3006        // accepted and ignored, which is what a real server does.
3007        assert_eq!(
3008            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
3009            "$6\r\nmytext\r\n"
3010        );
3011    }
3012
3013    #[test]
3014    fn select_moves_the_connection_and_the_databases_stay_apart() {
3015        let mut f = Fixture::new();
3016        f.run(&[b"SET", b"k", b"zero"]);
3017        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
3018        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
3019        f.run(&[b"SET", b"k", b"four"]);
3020        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
3021        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3022        assert_eq!(
3023            f.run(&[b"SELECT", b"99"]),
3024            "-ERR DB index is out of range\r\n"
3025        );
3026        assert_eq!(
3027            f.run(&[b"SELECT", b"-1"]),
3028            "-ERR DB index is out of range\r\n"
3029        );
3030        assert_eq!(
3031            f.run(&[b"SELECT", b"abc"]),
3032            "-ERR value is not an integer or out of range\r\n"
3033        );
3034        // `RESET` brings it back to zero.
3035        f.run(&[b"SELECT", b"4"]);
3036        f.run(&[b"RESET"]);
3037        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
3038    }
3039
3040    #[test]
3041    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
3042        let mut f = Fixture::new();
3043        let reply = f.run(&[b"HELLO"]);
3044        assert!(reply.starts_with("*14\r\n"), "{reply}");
3045        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
3046        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
3047        assert!(
3048            reply.contains(":7\r\n"),
3049            "the connection id is in there: {reply}"
3050        );
3051        assert_eq!(
3052            f.run(&[b"HELLO", b"4"]),
3053            "-NOPROTO unsupported protocol version\r\n"
3054        );
3055        assert_eq!(
3056            f.run(&[b"HELLO", b"abc"]),
3057            "-ERR Protocol version is not an integer or out of range\r\n"
3058        );
3059        assert_eq!(
3060            f.run(&[b"HELLO", b"3", b"SETNAME"]),
3061            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
3062        );
3063        assert!(
3064            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
3065                .starts_with("%7\r\n")
3066        );
3067        assert_eq!(f.session.name(), b"bob");
3068        f.run(&[b"RESET"]);
3069        assert_eq!(f.session.name(), b"");
3070    }
3071
3072    #[test]
3073    fn command_describes_this_server_in_the_shape_a_driver_reads() {
3074        let mut f = Fixture::new();
3075        let count = format!(":{}\r\n", COMMANDS.len());
3076        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
3077        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
3078        assert_eq!(
3079            info,
3080            "*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\
3081             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
3082        );
3083        // A null in the list, and the plain one: `$-1` and not `*-1`.
3084        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
3085        assert_eq!(
3086            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
3087            "*1\r\n$8\r\ngetrange\r\n"
3088        );
3089        assert_eq!(
3090            f.run(&[b"COMMAND", b"NOPE"]),
3091            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
3092        );
3093    }
3094
3095    /// A cluster aware client asks this question and then routes on the
3096    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
3097    /// that matters.
3098    #[test]
3099    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
3100        let mut f = Fixture::new();
3101        assert_eq!(
3102            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
3103            "*1\r\n$1\r\nk\r\n"
3104        );
3105        assert_eq!(
3106            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
3107            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3108        );
3109        assert_eq!(
3110            f.run(&[
3111                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
3112            ]),
3113            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
3114        );
3115        assert_eq!(
3116            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
3117            "-ERR The command has no key arguments\r\n"
3118        );
3119        assert_eq!(
3120            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
3121            "-ERR Invalid number of arguments specified for command\r\n"
3122        );
3123    }
3124
3125    #[test]
3126    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
3127        let mut f = Fixture::new();
3128        assert_eq!(
3129            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3130            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
3131        );
3132        // A pattern matches more than one, and a setting two patterns both ask
3133        // for is still sent once.
3134        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3135        assert!(both.starts_with("*6\r\n"), "{both}");
3136        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3137        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3138        assert_eq!(
3139            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3140            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3141        );
3142        assert_eq!(
3143            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3144            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3145        );
3146        assert_eq!(
3147            f.run(&[b"CONFIG", b"GET"]),
3148            "-ERR wrong number of arguments for 'config|get' command\r\n"
3149        );
3150        // Too few arguments and an odd number of them are different
3151        // complaints, which is the sort of thing only the real server tells
3152        // you.
3153        assert_eq!(
3154            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3155            "-ERR wrong number of arguments for 'config|set' command\r\n"
3156        );
3157        assert_eq!(
3158            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3159            "-ERR syntax error\r\n"
3160        );
3161        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3162        assert_eq!(
3163            f.run(&[b"CONFIG", b"REWRITE"]),
3164            "-ERR The server is running without a config file\r\n"
3165        );
3166    }
3167
3168    #[test]
3169    fn the_eviction_policy_reads_back_what_was_written_to_it() {
3170        let mut f = Fixture::new();
3171        assert_eq!(
3172            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3173            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3174        );
3175        assert_eq!(
3176            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3177            "+OK\r\n",
3178            "the name is matched without regard to case, like every other one"
3179        );
3180        assert_eq!(
3181            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3182            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3183        );
3184        // And INFO agrees with CONFIG, which it did not when it was a literal.
3185        assert!(
3186            f.run(&[b"INFO", b"memory"])
3187                .contains("maxmemory_policy:allkeys-lfu"),
3188            "INFO and CONFIG disagree about the policy"
3189        );
3190        // The refusal names every legal value in the order the real server's
3191        // enum table lists them, because a client comparing the message compares
3192        // the whole string.
3193        assert_eq!(
3194            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3195            "-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"
3196        );
3197        // A bad pair leaves the good one in the same command alone, and the
3198        // policy is checked by the same pass that checks the numbers.
3199        assert_eq!(
3200            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3201            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3202        );
3203        f.run(&[
3204            b"CONFIG",
3205            b"SET",
3206            b"hash-max-listpack-entries",
3207            b"7",
3208            b"maxmemory-policy",
3209            b"nonsense",
3210        ]);
3211        assert_eq!(
3212            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3213            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3214        );
3215    }
3216
3217    #[test]
3218    fn the_three_eviction_numbers_read_back_too() {
3219        let mut f = Fixture::new();
3220        for (name, default, set) in [
3221            ("maxmemory-samples", "5", "12"),
3222            ("lfu-log-factor", "10", "3"),
3223            ("lfu-decay-time", "1", "60"),
3224        ] {
3225            let get = || {
3226                format!(
3227                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3228                    name.len(),
3229                    default.len()
3230                )
3231            };
3232            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3233            assert_eq!(
3234                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3235                "+OK\r\n"
3236            );
3237            assert_eq!(
3238                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3239                format!(
3240                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3241                    name.len(),
3242                    set.len()
3243                )
3244            );
3245            // A number that is not a number is refused with the same sentence
3246            // every other number gets, which names the setting the client typed.
3247            assert_eq!(
3248                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3249                format!(
3250                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3251                )
3252            );
3253        }
3254    }
3255
3256    #[test]
3257    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3258        let mut f = Fixture::new();
3259        assert_eq!(
3260            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3261            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3262            "no limit is the default"
3263        );
3264        // The pairing is Redis's and it is a trap: the bare letter is a power of
3265        // ten and the one with the b is a power of two.
3266        for (typed, bytes) in [
3267            (&b"1024"[..], "1024"),
3268            (b"1k", "1000"),
3269            (b"1kb", "1024"),
3270            (b"1M", "1000000"),
3271            (b"1Mb", "1048576"),
3272            (b"1gb", "1073741824"),
3273            (b"100mb", "104857600"),
3274        ] {
3275            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3276            assert_eq!(
3277                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3278                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3279                "set {}",
3280                String::from_utf8_lossy(typed)
3281            );
3282        }
3283        assert!(
3284            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3285            "the report agrees with the setting"
3286        );
3287
3288        // A unit nobody has heard of, and a negative number, which is not a very
3289        // large one however it is spelled.
3290        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3291            assert_eq!(
3292                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3293                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3294                "refused {}",
3295                String::from_utf8_lossy(bad)
3296            );
3297        }
3298        assert!(
3299            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3300            "and the refusal left the old one alone"
3301        );
3302    }
3303
3304    #[test]
3305    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3306        let mut f = Fixture::new();
3307        f.run(&[b"SET", b"here", b"already"]);
3308        // A byte, which is under what an empty server holds, so nothing this
3309        // command could do would get it under. The default policy is
3310        // `noeviction`, so nothing is what it does.
3311        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3312        assert_eq!(
3313            f.run(&[b"SET", b"k", b"v"]),
3314            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3315        );
3316        assert_eq!(
3317            f.run(&[b"LPUSH", b"l", b"v"]),
3318            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3319        );
3320        // Reading is allowed, and so is the one thing that would help.
3321        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3322        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3323        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3324
3325        // Taking the limit away lets the write through again.
3326        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3327        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3328    }
3329
3330    #[test]
3331    fn an_allkeys_policy_makes_room_instead_of_refusing() {
3332        let mut f = Fixture::new();
3333        let val = vec![b'v'; 256];
3334        for i in 0..24000u32 {
3335            let k = format!("key:{i:08}");
3336            f.run(&[b"SET", k.as_bytes(), &val]);
3337        }
3338        let full = f.server.memory_bytes();
3339        assert!(
3340            full > 3 * 1024 * 1024,
3341            "the arena is several segments: {full}"
3342        );
3343
3344        // Two megabytes under what it is holding, which is one segment's worth,
3345        // so getting there means giving a whole segment back and not just
3346        // dropping a few records.
3347        let limit = full - 2 * 1024 * 1024;
3348        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3349        f.run(&[
3350            b"CONFIG",
3351            b"SET",
3352            b"maxmemory",
3353            limit.to_string().as_bytes(),
3354        ]);
3355
3356        // Writes keep working the whole way down. The budget means one command
3357        // does not do it all, so this runs until the server has settled and
3358        // checks that nothing was refused on the way.
3359        for i in 0..2000u32 {
3360            let k = format!("new:{i:08}");
3361            assert_eq!(
3362                f.run(&[b"SET", k.as_bytes(), &val]),
3363                "+OK\r\n",
3364                "write {i} was refused"
3365            );
3366            f.server.refresh_memory();
3367            if f.server.memory_bytes() <= limit {
3368                break;
3369            }
3370        }
3371        assert!(
3372            f.server.memory_bytes() <= limit,
3373            "it never got under: {} against {limit}",
3374            f.server.memory_bytes()
3375        );
3376        let info = f.run(&[b"INFO", b"stats"]);
3377        assert!(!info.contains("evicted_keys:0"), "{info}");
3378        assert!(
3379            f.run(&[b"DBSIZE"]) != ":0\r\n",
3380            "and it did not empty the database to get there"
3381        );
3382    }
3383
3384    #[test]
3385    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3386        // The limit is judged against a number kept as the collections move,
3387        // rather than found by asking all of them, and the two have to be the
3388        // same number or the limit is enforced against a fiction. This does the
3389        // things that move it, which is growing a collection, shrinking one,
3390        // changing its representation, deleting it and reusing its slot, across
3391        // all five types, and checks the two against each other as it goes.
3392        let mut f = Fixture::new();
3393        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3394        let big = vec![b'v'; 200];
3395
3396        for i in 0..400u32 {
3397            let n = i.to_string();
3398            let n = n.as_bytes();
3399            f.run(&[b"SADD", b"s", n]);
3400            f.run(&[b"SADD", b"s2", &big]);
3401            f.run(&[b"HSET", b"h", n, &big]);
3402            f.run(&[b"RPUSH", b"l", &big]);
3403            f.run(&[b"ZADD", b"z", n, n]);
3404            f.run(&[b"ARSET", b"a", n, &big]);
3405            if i % 7 == 0 {
3406                f.run(&[b"SREM", b"s", n]);
3407                f.run(&[b"HDEL", b"h", n]);
3408                f.run(&[b"LPOP", b"l"]);
3409                f.run(&[b"ZREM", b"z", n]);
3410                f.run(&[b"ARDEL", b"a", n]);
3411            }
3412            if i % 53 == 0 {
3413                // Every type deleted and made again, so a slot goes on the free
3414                // list and comes back holding something else.
3415                f.run(&[b"DEL", b"s2"]);
3416            }
3417            assert_eq!(
3418                f.server.settled_memory(),
3419                f.server.memory_bytes(),
3420                "after round {i}"
3421            );
3422        }
3423
3424        // The run has to have built something, or the two numbers agreeing is
3425        // two zeroes agreeing.
3426        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3427        assert!(
3428            f.server.memory_bytes() > 512 * 1024,
3429            "{}",
3430            f.server.memory_bytes()
3431        );
3432
3433        // And it survives the collections going away entirely.
3434        f.run(&[b"FLUSHALL"]);
3435        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3436    }
3437
3438    #[test]
3439    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3440        // A server with no limit does not keep the running total, so setting a
3441        // limit on a database that is already full has to start it from a walk.
3442        // If it did not, the first reading would be zero and the server would
3443        // think it had all the room in the world.
3444        let mut f = Fixture::new();
3445        for i in 0..200u32 {
3446            let n = i.to_string();
3447            f.run(&[b"SADD", b"s", n.as_bytes()]);
3448            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3449        }
3450        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3451        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3452
3453        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3454        for i in 200..400u32 {
3455            let n = i.to_string();
3456            f.run(&[b"SADD", b"s", n.as_bytes()]);
3457        }
3458        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3459        assert_eq!(
3460            f.server.settled_memory(),
3461            f.server.memory_bytes(),
3462            "the writes it was not watching are in the number it started from"
3463        );
3464    }
3465
3466    #[test]
3467    fn evicted_keys_and_expired_keys_are_different_numbers() {
3468        let mut f = Fixture::new();
3469        // Nothing has been evicted and nothing can be under the default policy,
3470        // so this stays at zero while the other one moves.
3471        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3472        f.server.advance_clock_ms(20);
3473        f.run(&[b"GET", b"gone"]);
3474        let info = f.run(&[b"INFO", b"stats"]);
3475        assert!(info.contains("expired_keys:1"), "{info}");
3476        assert!(info.contains("evicted_keys:0"), "{info}");
3477    }
3478
3479    #[test]
3480    fn the_object_subcommands_follow_the_policy() {
3481        let mut f = Fixture::new();
3482        f.run(&[b"SET", b"s", b"v"]);
3483        // Under the default the clock is kept and the counter is not, and under
3484        // an LFU policy it is the other way round. Each subcommand refuses on
3485        // the side where its reading of the three bytes means nothing.
3486        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3487        assert!(
3488            f.run(&[b"OBJECT", b"FREQ", b"s"])
3489                .starts_with("-ERR An LFU maxmemory policy is not selected"),
3490        );
3491
3492        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3493        assert!(
3494            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3495                .starts_with("-ERR An LFU maxmemory policy is selected"),
3496        );
3497        // The key was written under a clock policy, so what comes back is that
3498        // clock read as a counter. It is a number and not an error, which is the
3499        // point: switching at runtime does not invalidate anything, it only makes
3500        // the old field mean something else until the key is used again.
3501        assert!(
3502            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3503            "FREQ should answer under an LFU policy"
3504        );
3505    }
3506
3507    #[test]
3508    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3509        let mut f = Fixture::new();
3510        f.run(&[b"SET", b"s", b"hello"]);
3511        f.run(&[b"SET", b"n", b"123"]);
3512        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3513        f.run(&[b"SADD", b"ss", b"a", b"b"]);
3514        f.run(&[b"HSET", b"h", b"f", b"v"]);
3515        for (key, want) in [
3516            (b"s".as_slice(), "embstr"),
3517            (b"n", "int"),
3518            (b"si", "intset"),
3519            (b"ss", "listpack"),
3520            (b"h", "listpack"),
3521        ] {
3522            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3523            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3524        }
3525
3526        // A field deadline widens the blob rather than promoting it, and this
3527        // is the only place a client can see that happen.
3528        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3529        assert_eq!(
3530            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3531            "$10\r\nlistpackex\r\n"
3532        );
3533
3534        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3535        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3536        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3537    }
3538
3539    #[test]
3540    fn object_answers_nil_for_a_key_that_is_not_there() {
3541        let mut f = Fixture::new();
3542        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3543            assert_eq!(
3544                f.run(&[b"OBJECT", sub, b"nokey"]),
3545                "$-1\r\n",
3546                "a nil and not an error, which is what 8.10.1 does"
3547            );
3548        }
3549        // And the key is looked up before FREQ has its complaint, so the
3550        // complaint only reaches a key that exists.
3551        f.run(&[b"SET", b"s", b"v"]);
3552        assert!(
3553            f.run(&[b"OBJECT", b"FREQ", b"s"])
3554                .starts_with("-ERR An LFU maxmemory policy is not"),
3555        );
3556        assert_eq!(
3557            f.run(&[b"OBJECT", b"NOPE", b"s"]),
3558            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3559        );
3560        assert_eq!(
3561            f.run(&[b"OBJECT", b"ENCODING"]),
3562            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3563        );
3564        assert_eq!(
3565            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3566            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3567        );
3568        assert_eq!(
3569            f.run(&[b"OBJECT"]),
3570            "-ERR wrong number of arguments for 'object' command\r\n"
3571        );
3572    }
3573
3574    #[test]
3575    fn config_moves_the_ladder_and_object_encoding_agrees() {
3576        let mut f = Fixture::new();
3577        assert_eq!(
3578            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3579            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3580            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3581        );
3582        // The old spelling is the same number under a different name, and a
3583        // glob that catches both sends both.
3584        assert_eq!(
3585            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3586            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3587        );
3588        assert!(
3589            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3590                .starts_with("*8\r\n")
3591        );
3592        assert!(
3593            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3594                .starts_with("*6\r\n")
3595        );
3596
3597        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3598        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3599
3600        assert_eq!(
3601            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3602            "+OK\r\n",
3603            "written under the old name and read back under the new one"
3604        );
3605        assert_eq!(
3606            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3607            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3608        );
3609        assert_eq!(
3610            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3611            "$8\r\nlistpack\r\n",
3612            "the hash that already exists is left exactly where it was"
3613        );
3614        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3615        assert_eq!(
3616            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3617            "$9\r\nhashtable\r\n",
3618            "and the next one built goes straight to a table"
3619        );
3620
3621        // The set has three of these and all three move.
3622        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3623        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3624        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3625        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3626        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3627        assert_eq!(
3628            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3629            "$9\r\nhashtable\r\n"
3630        );
3631    }
3632
3633    #[test]
3634    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3635        let mut f = Fixture::new();
3636        assert_eq!(
3637            f.run(&[
3638                b"CONFIG",
3639                b"SET",
3640                b"hash-max-listpack-entries",
3641                b"7",
3642                b"set-max-listpack-entries",
3643                b"abc"
3644            ]),
3645            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3646        );
3647        assert_eq!(
3648            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3649            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3650            "the pair in front of the bad one did not go in"
3651        );
3652        // The name in the complaint is the one that was typed, so the old
3653        // spelling comes back as the old spelling.
3654        assert_eq!(
3655            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3656            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3657        );
3658        assert_eq!(
3659            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3660            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3661        );
3662        // A number past what an i64 holds is the parse complaint and not the
3663        // range one, which is upstream reading it before it checks it.
3664        assert_eq!(
3665            f.run(&[
3666                b"CONFIG",
3667                b"SET",
3668                b"set-max-intset-entries",
3669                b"99999999999999999999"
3670            ]),
3671            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3672        );
3673        assert_eq!(
3674            f.run(&[
3675                b"CONFIG",
3676                b"SET",
3677                b"set-max-intset-entries",
3678                b"9223372036854775807"
3679            ]),
3680            "+OK\r\n"
3681        );
3682    }
3683
3684    #[test]
3685    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3686        let mut f = Fixture::new();
3687        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3688        f.run(&[b"SELECT", b"3"]);
3689        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3690        assert_eq!(
3691            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3692            "$9\r\nhashtable\r\n",
3693            "these are one server wide number in Redis, whatever a Keyspace carries"
3694        );
3695    }
3696
3697    #[test]
3698    fn info_reports_the_numbers_it_can_stand_behind() {
3699        let mut f = Fixture::new();
3700        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3701        let all = f.run(&[b"INFO"]);
3702        assert!(all.contains("redis_version:8.8.0"), "{all}");
3703        assert!(
3704            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3705            "{all}"
3706        );
3707        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3708        assert!(all.contains("role:master"), "{all}");
3709        // One section is one section.
3710        let clients = f.run(&[b"INFO", b"clients"]);
3711        assert!(clients.contains("connected_clients:0"), "{clients}");
3712        assert!(!clients.contains("redis_version"), "{clients}");
3713        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3714    }
3715
3716    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
3717    ///
3718    /// This is Redis's `unit/info-command` written against the fixture. Every
3719    /// assertion in it is one of theirs, in their order, and the two fields it
3720    /// turns on are the two that suite was failing on: `master_repl_offset`,
3721    /// which is in the default set, and `rejected_calls`, which is not.
3722    #[test]
3723    fn commandstats_is_asked_for_and_replication_is_not() {
3724        let mut f = Fixture::new();
3725        for arg in ["", "all", "default", "everything"] {
3726            let info = if arg.is_empty() {
3727                f.run(&[b"INFO"])
3728            } else {
3729                f.run(&[b"INFO", arg.as_bytes()])
3730            };
3731            assert!(info.contains("redis_version"), "{arg}: {info}");
3732            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3733            assert!(info.contains("used_memory"), "{arg}: {info}");
3734            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3735            let asked = arg == "all" || arg == "everything";
3736            assert_eq!(
3737                info.contains("rejected_calls"),
3738                asked,
3739                "{arg} should{} carry the command counters: {info}",
3740                if asked { "" } else { " not" }
3741            );
3742        }
3743
3744        let cpu = f.run(&[b"INFO", b"cpu"]);
3745        assert!(cpu.contains("used_cpu_user"), "{cpu}");
3746        assert!(!cpu.contains("used_memory"), "{cpu}");
3747
3748        // Their case, to make the point that a section name is not case
3749        // sensitive any more than a command name is.
3750        let stats = f.run(&[b"INFO", b"commandSTATS"]);
3751        assert!(!stats.contains("used_memory"), "{stats}");
3752        assert!(stats.contains("rejected_calls"), "{stats}");
3753
3754        // Two sections named, and neither of them pulls in a third.
3755        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3756        assert!(pair.contains("used_cpu_user"), "{pair}");
3757        assert!(!pair.contains("master_repl_offset"), "{pair}");
3758
3759        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3760        assert!(with_all.contains("used_memory"), "{with_all}");
3761        assert!(with_all.contains("master_repl_offset"), "{with_all}");
3762        assert!(with_all.contains("rejected_calls"), "{with_all}");
3763        // A section named twice is still written once.
3764        assert_eq!(
3765            with_all.matches("used_cpu_user_children").count(),
3766            1,
3767            "{with_all}"
3768        );
3769
3770        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3771        assert!(with_default.contains("used_memory"), "{with_default}");
3772        assert!(
3773            with_default.contains("master_repl_offset"),
3774            "{with_default}"
3775        );
3776        assert!(!with_default.contains("rejected_calls"), "{with_default}");
3777        assert_eq!(
3778            with_default.matches("used_cpu_user_children").count(),
3779            1,
3780            "{with_default}"
3781        );
3782    }
3783
3784    /// The memory section says what this process may use, not what the machine
3785    /// has.
3786    ///
3787    /// The distinction is the whole point of it. A server inside a container
3788    /// that reports the host's memory is a server whose operator sizes it for
3789    /// memory it will be killed for touching, so all three numbers are there:
3790    /// what the machine has, what the cgroup allows, and the quarter of the
3791    /// tighter one that pools are sized from.
3792    #[test]
3793    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3794        let mut f = Fixture::new();
3795        let info = f.run(&[b"INFO", b"memory"]);
3796        for field in [
3797            "total_system_memory:",
3798            "mem_cgroup_limit:",
3799            "mem_limit:",
3800            "mem_budget:",
3801        ] {
3802            assert!(info.contains(field), "no {field} in {info}");
3803        }
3804
3805        let field = |name: &str| -> u64 {
3806            info.lines()
3807                .find_map(|l| l.strip_prefix(name))
3808                .unwrap_or_else(|| panic!("no {name} in {info}"))
3809                .trim()
3810                .parse()
3811                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3812        };
3813        let limit = field("mem_limit:");
3814        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3815        // Zero means there is no limit to report, which is a real answer on a
3816        // machine with no cgroups and no way to ask how big it is.
3817        if limit != 0 {
3818            let host = field("total_system_memory:");
3819            let cgroup = field("mem_cgroup_limit:");
3820            assert!(
3821                limit == host || limit == cgroup,
3822                "the limit came from neither number: {info}"
3823            );
3824        }
3825    }
3826
3827    /// The three counters, each on the path that raises it.
3828    ///
3829    /// `calls` on a command that worked, `failed_calls` on one that ran and
3830    /// answered with an error, and `rejected_calls` on one that never ran at
3831    /// all. The last two are the pair that is easy to collapse into one number
3832    /// and that Redis keeps apart, because a client sending the wrong number of
3833    /// arguments and a client asking for a list element that is not there are
3834    /// not the same problem.
3835    #[test]
3836    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3837        let mut f = Fixture::new();
3838        f.run(&[b"SET", b"k", b"v"]);
3839        f.run(&[b"SET", b"k", b"w"]);
3840        // Ran, and answered with an error, because `k` is not a list.
3841        f.run(&[b"LPUSH", b"k", b"x"]);
3842        // Never ran: `LPUSH` takes at least three arguments.
3843        f.run(&[b"LPUSH", b"k"]);
3844
3845        let stats = f.run(&[b"INFO", b"commandstats"]);
3846        assert!(
3847            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3848            "{stats}"
3849        );
3850        assert!(
3851            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3852            "{stats}"
3853        );
3854        assert!(
3855            !stats.contains("cmdstat_zadd"),
3856            "a command nobody has sent has no row: {stats}"
3857        );
3858    }
3859
3860    /// A cache that writes with a deadline and never reads back used to hold
3861    /// every key it had ever written, because lazy expiry needs somebody to walk
3862    /// past a key before it can reclaim it and nobody ever did.
3863    #[test]
3864    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3865        let mut f = Fixture::new();
3866        for i in 0..3_000u32 {
3867            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3868        }
3869        for i in 0..1_000u32 {
3870            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3871        }
3872        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3873        f.advance(100);
3874        assert_eq!(
3875            f.run(&[b"DBSIZE"]),
3876            ":4000\r\n",
3877            "DBSIZE counts records and nothing has read past the dead ones yet"
3878        );
3879
3880        // What the shard loop does, one slice at a time.
3881        let mut spent = 0;
3882        for _ in 0..2_000 {
3883            spent += f.server.expire_step(4096);
3884            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3885                break;
3886            }
3887        }
3888        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3889        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3890        for i in 0..1_000u32 {
3891            assert_eq!(
3892                f.run(&[b"GET", format!("k{i}").as_bytes()]),
3893                "$1\r\nv\r\n",
3894                "it took a key that had no deadline"
3895            );
3896        }
3897    }
3898
3899    #[test]
3900    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3901        let mut f = Fixture::new();
3902        for i in 0..2_000u32 {
3903            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3904        }
3905        assert_eq!(f.server.expire_step(4096), 0);
3906        // And one database having them does not make the other fifteen pay.
3907        f.run(&[b"SELECT", b"3"]);
3908        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3909        f.advance(100);
3910        for _ in 0..64 {
3911            f.server.expire_step(4096);
3912        }
3913        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3914        f.run(&[b"SELECT", b"0"]);
3915        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3916        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3917    }
3918
3919    /// The gate, which is what stops a maintenance slice that runs every hundred
3920    /// nanoseconds from drawing a sample every hundred nanoseconds.
3921    #[test]
3922    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3923        let mut f = Fixture::new();
3924        for i in 0..500u32 {
3925            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3926        }
3927        f.advance(100);
3928        let at = f.server.striped(0).now_ms();
3929        f.server.set_clock_ms(at);
3930        // A small budget, so that one slice cannot finish the job and a second
3931        // one having nothing to do would mean the gate and not an empty
3932        // database.
3933        assert!(f.server.expire_slice(8) > 0, "the first one works");
3934        for _ in 0..1_000 {
3935            assert_eq!(
3936                f.server.expire_slice(8),
3937                0,
3938                "the millisecond has not moved and neither should this"
3939            );
3940        }
3941        assert!(
3942            f.server.striped(0).expires() > 400,
3943            "there is plenty left to take"
3944        );
3945        f.server.set_clock_ms(at + 1);
3946        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3947    }
3948
3949    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
3950    /// how much of a cache is volatile was reading a constant.
3951    #[test]
3952    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3953        let mut f = Fixture::new();
3954        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3955        assert!(
3956            f.run(&[b"INFO", b"keyspace"])
3957                .contains("db0:keys=3,expires=0"),
3958            "none of them has one yet"
3959        );
3960        f.run(&[b"EXPIRE", b"a", b"1000"]);
3961        f.run(&[b"EXPIRE", b"b", b"1000"]);
3962        let two = f.run(&[b"INFO", b"keyspace"]);
3963        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3964        f.run(&[b"PERSIST", b"a"]);
3965        f.run(&[b"DEL", b"b"]);
3966        let none = f.run(&[b"INFO", b"keyspace"]);
3967        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3968
3969        // Each database answers for itself, the way Redis reports it.
3970        f.run(&[b"SELECT", b"1"]);
3971        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3972        let both = f.run(&[b"INFO", b"keyspace"]);
3973        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3974        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3975    }
3976
3977    #[cfg(unix)]
3978    #[test]
3979    fn info_cpu_reports_processor_time_that_was_really_measured() {
3980        let mut f = Fixture::new();
3981        let cpu = f.run(&[b"INFO", b"cpu"]);
3982        assert!(cpu.contains("# CPU"), "{cpu}");
3983        // Redis's unit/info-command asks for this one by name in three tests.
3984        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3985        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3986        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3987        assert!(!cpu.contains("redis_version"), "{cpu}");
3988
3989        // It is a measurement and not a constant, so it goes up when work
3990        // happens. A tight loop rather than a sleep, because sleeping is the
3991        // one thing that does not move this number.
3992        let before = used_cpu_user(&cpu);
3993        let mut n = 0u64;
3994        let mut rounds = 0;
3995        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3996            for i in 0..1_000_000u64 {
3997                n = n.wrapping_add(i.wrapping_mul(i));
3998            }
3999            rounds += 1;
4000            // A bound rather than a spin, so a platform where this number does
4001            // not move fails here instead of hanging. Even a clock with whole
4002            // millisecond granularity gets there in the first round or two.
4003            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
4004        }
4005    }
4006
4007    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
4008    #[cfg(unix)]
4009    fn used_cpu_user(info: &str) -> f64 {
4010        info.lines()
4011            .find_map(|l| l.strip_prefix("used_cpu_user:"))
4012            .expect("no used_cpu_user in the reply")
4013            .trim()
4014            .parse()
4015            .expect("used_cpu_user is not a number")
4016    }
4017
4018    /// The safety net under the rule that a body checks its arguments before
4019    /// it writes anything. `MGET` writes its array header first and then reads
4020    /// each key, so if a later argument could fail the header would already be
4021    /// out. Nothing in the string group does that today and this is what would
4022    /// catch the first one that did.
4023    #[test]
4024    fn a_command_that_fails_leaves_nothing_half_written() {
4025        let mut f = Fixture::new();
4026        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
4027        assert_eq!(reply, "-ERR offset is out of range\r\n");
4028        assert!(!reply.contains(':'), "no integer went out in front of it");
4029    }
4030
4031    #[test]
4032    fn quit_answers_first_and_closes_after() {
4033        let mut f = Fixture::new();
4034        let (flow, reply) = f.flow(&[b"QUIT"]);
4035        assert_eq!(reply, "+OK\r\n");
4036        assert_eq!(flow, Flow::Close);
4037    }
4038
4039    /// A server that has not been asked to stop is not stopping, and one that
4040    /// has says so without writing anything back.
4041    ///
4042    /// The empty reply is the point. Redis answers nothing at all here and the
4043    /// client sees the socket close, and an `OK` would be a promise from a
4044    /// process that is about to not exist.
4045    #[test]
4046    fn shutdown_writes_nothing_and_sets_the_flag() {
4047        let mut f = Fixture::new();
4048        assert!(!f.server.stopping(), "nobody has asked yet");
4049
4050        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
4051        assert_eq!(reply, "");
4052        assert_eq!(flow, Flow::Close);
4053        assert!(f.server.stopping());
4054    }
4055
4056    /// Every flag combination 8.10.1 takes, and every one it refuses.
4057    ///
4058    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
4059    /// contradict each other, `ABORT` says to do nothing so it cannot be
4060    /// combined with a word about how to do it, and repeating any one of them
4061    /// is fine. All of it was read off a running 8.10.1 rather than worked out
4062    /// from the documentation, which does not say.
4063    #[test]
4064    fn shutdown_takes_the_flags_redis_takes() {
4065        for flags in [
4066            &[b"NOSAVE".as_slice()][..],
4067            &[b"SAVE"],
4068            &[b"NOW"],
4069            &[b"FORCE"],
4070            &[b"nosave"],
4071            &[b"NOW", b"NOW"],
4072            &[b"SAVE", b"SAVE"],
4073            &[b"NOSAVE", b"NOW", b"FORCE"],
4074        ] {
4075            let mut f = Fixture::new();
4076            let mut parts = vec![b"SHUTDOWN".as_slice()];
4077            parts.extend_from_slice(flags);
4078            let (flow, reply) = f.flow(&parts);
4079            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
4080            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
4081            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
4082        }
4083
4084        for flags in [
4085            &[b"BOGUS".as_slice()][..],
4086            &[b"SAVE", b"NOSAVE"],
4087            &[b"NOSAVE", b"SAVE"],
4088            &[b"ABORT", b"NOW"],
4089            &[b"NOSAVE", b"ABORT"],
4090            &[b"NOW", b"FORCE", b"ABORT"],
4091        ] {
4092            let mut f = Fixture::new();
4093            let mut parts = vec![b"SHUTDOWN".as_slice()];
4094            parts.extend_from_slice(flags);
4095            assert_eq!(
4096                f.run(&parts),
4097                "-ERR syntax error\r\n",
4098                "SHUTDOWN {flags:?} was accepted"
4099            );
4100            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
4101        }
4102    }
4103
4104    /// `ABORT` has nothing to call off, ever.
4105    ///
4106    /// A shutdown here is decided and done inside one turn of the loop, so
4107    /// there is no window in which one is in progress. That makes Redis's
4108    /// message for a cancel with nothing to cancel the right answer every time
4109    /// rather than only when nothing happens to be pending. Two `ABORT`s is
4110    /// still one `ABORT`, which is what 8.10.1 does.
4111    #[test]
4112    fn shutdown_abort_never_has_anything_to_abort() {
4113        let mut f = Fixture::new();
4114        for parts in [
4115            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
4116            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
4117        ] {
4118            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
4119            assert!(!f.server.stopping(), "an abort stopped the server");
4120        }
4121    }
4122
4123    /// A fixture whose server writes into a directory of its own.
4124    ///
4125    /// Every test here really writes files, because the whole point of the
4126    /// command is the files and a backup that is only a state machine would
4127    /// pass a test suite and fail the first person who tried to restore one.
4128    /// The directory carries the test's name so that the suite can run its
4129    /// tests in parallel the way it always does.
4130    struct Backups {
4131        f: Fixture,
4132        dir: PathBuf,
4133    }
4134
4135    impl Backups {
4136        fn new(name: &str) -> Backups {
4137            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4138            let _ = std::fs::remove_dir_all(&dir);
4139            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4140            let mut f = Fixture::new();
4141            f.server.set_dir(dir.clone());
4142            Backups { f, dir }
4143        }
4144
4145        fn run(&mut self, parts: &[&[u8]]) -> String {
4146            self.f.run(parts)
4147        }
4148
4149        /// The names in `backupdir`, sorted, so a test can say what is on disk.
4150        fn files(&self) -> Vec<String> {
4151            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4152                Ok(entries) => entries
4153                    .filter_map(|e| e.ok())
4154                    .map(|e| e.file_name().to_string_lossy().into_owned())
4155                    .collect(),
4156                Err(_) => Vec::new(),
4157            };
4158            names.sort();
4159            names
4160        }
4161
4162        fn read(&self, name: &str) -> Vec<u8> {
4163            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4164        }
4165    }
4166
4167    impl Drop for Backups {
4168        fn drop(&mut self) {
4169            let _ = std::fs::remove_dir_all(&self.dir);
4170        }
4171    }
4172
4173    /// The four states and the moves between them, in the order a client walks
4174    /// them, with the files checked at every step.
4175    #[test]
4176    fn backup_walks_the_states_the_reference_walks() {
4177        let mut b = Backups::new("states");
4178        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4179
4180        assert!(status(&mut b).contains("idle"));
4181        assert!(b.files().is_empty(), "an idle server has written a backup");
4182
4183        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4184        assert!(status(&mut b).contains("incrementing"));
4185        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4186
4187        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4188        assert!(status(&mut b).contains("sealed"));
4189        assert_eq!(
4190            b.files(),
4191            [
4192                "appendonly.aof.1.base.rdb",
4193                "appendonly.aof.1.incr.aof",
4194                "appendonly.aof.manifest",
4195            ]
4196        );
4197
4198        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4199        assert!(status(&mut b).contains("idle"));
4200        assert!(b.files().is_empty(), "cleanup left something behind");
4201    }
4202
4203    /// Every move that is refused, in the reference's words.
4204    #[test]
4205    fn backup_refuses_the_moves_the_reference_refuses() {
4206        let mut b = Backups::new("refusals");
4207
4208        assert_eq!(
4209            b.run(&[b"BACKUP", b"SEAL"]),
4210            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4211        );
4212        assert_eq!(
4213            b.run(&[b"BACKUP", b"ABORT"]),
4214            "-ERR No backup in progress\r\n"
4215        );
4216        // Cleanup from idle is not an error, it is a way of saying there was
4217        // nothing to clean up.
4218        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4219
4220        b.run(&[b"BACKUP", b"START"]);
4221        assert_eq!(
4222            b.run(&[b"BACKUP", b"START"]),
4223            "-ERR A backup is already in progress, ABORT it first\r\n"
4224        );
4225        assert_eq!(
4226            b.run(&[b"BACKUP", b"CLEANUP"]),
4227            "-ERR Backup is in progress\r\n"
4228        );
4229
4230        b.run(&[b"BACKUP", b"SEAL"]);
4231        assert_eq!(
4232            b.run(&[b"BACKUP", b"START"]),
4233            "-ERR A sealed backup exists, CLEANUP it first\r\n"
4234        );
4235        assert_eq!(
4236            b.run(&[b"BACKUP", b"SEAL"]),
4237            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4238        );
4239        assert_eq!(
4240            b.run(&[b"BACKUP", b"ABORT"]),
4241            "-ERR No backup in progress\r\n"
4242        );
4243    }
4244
4245    /// An abort takes the base file away and leaves a state saying who did it.
4246    ///
4247    /// The next backup takes the next sequence number rather than reusing the
4248    /// one whose files were just thrown away, so a directory somebody copied a
4249    /// half finished backup out of cannot end up with two different files under
4250    /// one name.
4251    #[test]
4252    fn backup_abort_removes_the_file_and_says_who_did_it() {
4253        let mut b = Backups::new("abort");
4254        b.run(&[b"BACKUP", b"START"]);
4255        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4256
4257        let status = b.run(&[b"BACKUP", b"STATUS"]);
4258        assert!(status.contains("failed"), "{status}");
4259        assert!(status.contains("aborted by user"), "{status}");
4260        assert!(b.files().is_empty(), "abort left the base file behind");
4261        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4262
4263        // A start from failed works, and is the second backup.
4264        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4265        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4266        let status = b.run(&[b"BACKUP", b"STATUS"]);
4267        assert!(status.contains("incrementing"), "{status}");
4268        assert!(!status.contains("aborted"), "the old error was kept");
4269    }
4270
4271    /// `LIST` names nothing, then one file, then three, and they are absolute.
4272    #[test]
4273    fn backup_list_names_the_files_that_are_pinned_so_far() {
4274        let mut b = Backups::new("list");
4275        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4276
4277        b.run(&[b"BACKUP", b"START"]);
4278        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4279        let base = base.to_string_lossy().into_owned();
4280        assert_eq!(
4281            b.run(&[b"BACKUP", b"LIST"]),
4282            format!("*1\r\n${}\r\n{base}\r\n", base.len())
4283        );
4284
4285        b.run(&[b"BACKUP", b"SEAL"]);
4286        let listed = b.run(&[b"BACKUP", b"LIST"]);
4287        assert!(listed.starts_with("*3\r\n"), "{listed}");
4288        // The order is the manifest's order, base then incremental then the
4289        // manifest itself, which is the order a restore needs them in.
4290        let names: Vec<&str> = listed
4291            .lines()
4292            .filter(|l| l.starts_with('/') || l.contains(":\\"))
4293            .collect();
4294        assert_eq!(names.len(), 3, "{listed}");
4295        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4296        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4297        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4298    }
4299
4300    /// The base file is the dataset as it was at `START` and not at `SEAL`.
4301    ///
4302    /// That is D-46 and it is the one thing about this a client can notice, so
4303    /// it is pinned here rather than left to be discovered by whoever restores
4304    /// one. The incremental file is empty for the same reason: there is no
4305    /// append only log underneath this server to copy the writes in between out
4306    /// of.
4307    #[test]
4308    fn a_backup_holds_the_dataset_as_it_was_at_start() {
4309        let mut b = Backups::new("contents");
4310        b.run(&[b"SET", b"bk", b"v1"]);
4311        b.run(&[b"BACKUP", b"START"]);
4312        b.run(&[b"SET", b"bk", b"v2"]);
4313        b.run(&[b"BACKUP", b"SEAL"]);
4314
4315        let base = b.read("appendonly.aof.1.base.rdb");
4316        assert!(base.starts_with(b"REDIS"), "not an RDB file");
4317        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4318        assert!(
4319            !base.windows(2).any(|w| w == b"v2"),
4320            "the base file moved on after START"
4321        );
4322        // The aux field a loader acts on, and the one that says this file is
4323        // the base of an append only file rather than a standalone dump. Its
4324        // value is the one byte string 1, which the encoder writes as an
4325        // integer the way a real server writes it.
4326        let at = base
4327            .windows(8)
4328            .position(|w| w == b"aof-base")
4329            .expect("no aof-base aux field");
4330        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4331
4332        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4333        assert_eq!(
4334            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4335            "file appendonly.aof.1.base.rdb seq 1 type b\n\
4336             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4337        );
4338    }
4339
4340    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
4341    /// RESP2, which is what every other map shaped reply in this server does.
4342    #[test]
4343    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4344        let mut b = Backups::new("status");
4345        b.f.server.set_clock_ms(1_700_000_000_000);
4346
4347        assert_eq!(
4348            b.run(&[b"BACKUP", b"STATUS"]),
4349            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4350             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4351        );
4352
4353        b.f.out = Out::new(Proto::Resp3);
4354        b.run(&[b"BACKUP", b"START"]);
4355        assert_eq!(
4356            b.run(&[b"BACKUP", b"STATUS"]),
4357            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4358             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4359        );
4360
4361        b.run(&[b"BACKUP", b"SEAL"]);
4362        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4363        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4364    }
4365
4366    /// A sealed backup that nobody cleans up goes away on its own once
4367    /// `backup-sealed-ttl` seconds have passed since the seal.
4368    #[test]
4369    fn a_sealed_backup_is_swept_away_after_the_timeout() {
4370        let mut b = Backups::new("ttl");
4371        b.f.server.set_clock_ms(1_000_000);
4372        assert_eq!(
4373            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4374            "+OK\r\n"
4375        );
4376        b.run(&[b"BACKUP", b"START"]);
4377        b.run(&[b"BACKUP", b"SEAL"]);
4378
4379        // A minute short of the deadline, nothing happens.
4380        b.f.server.set_clock_ms(1_000_000 + 59_000);
4381        b.f.server.backup_expire();
4382        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4383        assert_eq!(b.files().len(), 3);
4384
4385        b.f.server.set_clock_ms(1_000_000 + 60_000);
4386        b.f.server.backup_expire();
4387        let status = b.run(&[b"BACKUP", b"STATUS"]);
4388        assert!(status.contains("idle"), "{status}");
4389        assert!(b.files().is_empty(), "the timeout left the files behind");
4390
4391        // Zero is the default and means a sealed backup is kept for ever.
4392        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4393        b.run(&[b"BACKUP", b"START"]);
4394        b.run(&[b"BACKUP", b"SEAL"]);
4395        b.f.server.set_clock_ms(9_000_000_000);
4396        b.f.server.backup_expire();
4397        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4398    }
4399
4400    /// The three settings around the command, read and written the way 8.10.1
4401    /// reads and writes them.
4402    #[test]
4403    fn the_backup_settings_behave_the_way_the_reference_does() {
4404        let mut b = Backups::new("config");
4405        let dir = b.dir.to_string_lossy().into_owned();
4406
4407        assert_eq!(
4408            b.run(&[b"CONFIG", b"GET", b"dir"]),
4409            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4410        );
4411        assert_eq!(
4412            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4413            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4414        );
4415        assert_eq!(
4416            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4417            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4418        );
4419
4420        // `dir` is a protected config, so it is refused even for the value it
4421        // already holds, and `backupdirname` is immutable.
4422        assert_eq!(
4423            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4424            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4425        );
4426        assert_eq!(
4427            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4428            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4429        );
4430        assert!(
4431            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4432                .contains("argument couldn't be parsed into an integer")
4433        );
4434        assert!(
4435            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4436                .contains("argument must be between 0 and 9223372036854775807 inclusive")
4437        );
4438    }
4439
4440    /// The help text, which has `HELP` in it twice because the reference's does.
4441    #[test]
4442    fn backup_help_is_the_text_the_reference_sends() {
4443        let mut f = Fixture::new();
4444        let help = f.run(&[b"BACKUP", b"HELP"]);
4445        assert!(help.starts_with("*17\r\n"), "{help}");
4446        assert!(
4447            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4448        );
4449        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
4450        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
4451        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
4452    }
4453
4454    /// What a mistyped `BACKUP` gets told.
4455    ///
4456    /// The arity error names `backup` where the reference names `backup|start`,
4457    /// which is D-46: the table reports one arity for the container the way the
4458    /// reference does, and the per subcommand table that would carry the better
4459    /// name is not built yet. Every subcommand is exactly two words, so nothing
4460    /// legal is refused by it.
4461    #[test]
4462    fn backup_refuses_what_it_cannot_read() {
4463        let mut f = Fixture::new();
4464        assert_eq!(
4465            f.run(&[b"BACKUP"]),
4466            "-ERR wrong number of arguments for 'backup' command\r\n"
4467        );
4468        assert_eq!(
4469            f.run(&[b"BACKUP", b"START", b"x"]),
4470            "-ERR wrong number of arguments for 'backup' command\r\n"
4471        );
4472        assert_eq!(
4473            f.run(&[b"BACKUP", b"NOPE"]),
4474            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4475        );
4476    }
4477
4478    #[test]
4479    fn the_command_counter_counts_every_command_including_the_bad_ones() {
4480        let mut f = Fixture::new();
4481        f.run(&[b"PING"]);
4482        f.run(&[b"NOPE"]);
4483        f.run(&[b"GET"]);
4484        assert_eq!(f.server.stats.commands, 3);
4485    }
4486
4487    #[test]
4488    fn a_set_goes_from_bytes_to_bytes() {
4489        let mut f = Fixture::new();
4490        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4491        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4492        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4493        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4494        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4495        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4496        assert_eq!(
4497            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4498            "*3\r\n:1\r\n:0\r\n:1\r\n"
4499        );
4500        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4501        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4502    }
4503
4504    #[test]
4505    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4506        let mut f = Fixture::new();
4507        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4508        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4509        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4510        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4511        assert_eq!(
4512            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4513            "*2\r\n:0\r\n:0\r\n"
4514        );
4515        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4516    }
4517
4518    #[test]
4519    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4520        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
4521        // and one that gets a `*` hands it a list, without either of them being
4522        // told which command was sent.
4523        let mut f = Fixture::new();
4524        f.run(&[b"SADD", b"s", b"one"]);
4525        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4526
4527        f.run(&[b"HELLO", b"3"]);
4528        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4529    }
4530
4531    #[test]
4532    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4533        // An intset holds the number, so these digits exist for the first time
4534        // in the reply buffer.
4535        let mut f = Fixture::new();
4536        f.run(&[b"SADD", b"s", b"42"]);
4537        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4538        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4539        assert_eq!(
4540            f.run(&[b"SISMEMBER", b"s", b"042"]),
4541            ":0\r\n",
4542            "the member is the bytes and not the number they parse to"
4543        );
4544    }
4545
4546    #[test]
4547    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4548        let mut f = Fixture::new();
4549        f.run(&[b"SET", b"str", b"v"]);
4550        f.run(&[b"SADD", b"set", b"a"]);
4551
4552        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4553        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4554        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4555        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4556        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4557        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4558        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4559        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4560        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4561
4562        // MGET is the one that does not, because Redis gives nil for the odd
4563        // key out rather than failing the good keys next to it.
4564        assert_eq!(
4565            f.run(&[b"MGET", b"str", b"set", b"nope"]),
4566            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4567        );
4568        // And plain SET overwrites any type, which takes the body with it.
4569        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4570        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4571    }
4572
4573    #[test]
4574    fn a_wrongtype_leaves_nothing_half_written() {
4575        // SMISMEMBER writes an array header and then one reply per member, so
4576        // it is the first command in the server that could get a header out in
4577        // front of an error if it checked its key in the wrong order.
4578        let mut f = Fixture::new();
4579        f.run(&[b"SET", b"k", b"v"]);
4580        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4581        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4582        assert!(!reply.contains('*'), "an array header went out in front");
4583    }
4584
4585    #[test]
4586    fn emptying_a_set_takes_the_key_with_it() {
4587        let mut f = Fixture::new();
4588        f.run(&[b"SADD", b"s", b"a", b"b"]);
4589        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4590        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4591        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4592        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4593        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4594    }
4595
4596    /// Pull the cursor and the members out of one `SSCAN` reply.
4597    ///
4598    /// Crude on purpose. A test that walked a set through a real client would
4599    /// be testing the client, and what these tests are about is the shape of
4600    /// the bytes and the fact that a walk sees every member once.
4601    fn split_scan(reply: &str) -> (String, Vec<String>) {
4602        let mut lines = reply.split("\r\n");
4603        assert_eq!(lines.next(), Some("*2"), "got {reply}");
4604        lines.next().expect("the cursor header");
4605        let cursor = lines.next().expect("the cursor").to_owned();
4606        let header = lines.next().expect("the member header");
4607        let n: usize = header[1..].parse().expect("a member count");
4608        let mut members = Vec::with_capacity(n);
4609        for _ in 0..n {
4610            lines.next().expect("a member header");
4611            members.push(lines.next().expect("a member").to_owned());
4612        }
4613        (cursor, members)
4614    }
4615
4616    #[test]
4617    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
4618        let mut f = Fixture::new();
4619        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
4620
4621        let one = f.run(&[b"SPOP", b"s"]);
4622        assert!(
4623            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
4624            "got {one}"
4625        );
4626        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4627
4628        // A count takes that many, and the last one takes the key with it.
4629        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
4630        assert!(rest.starts_with("*3\r\n"), "got {rest}");
4631        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4632        // And a pop at a key that is not there is a nil, not an empty bulk.
4633        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
4634        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
4635    }
4636
4637    #[test]
4638    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
4639        // The one place in the server where the reply type carries something
4640        // the command name does not. SPOP's members are distinct so a RESP3
4641        // client can build a set out of them. SRANDMEMBER with a negative count
4642        // can hand back the same member three times, and a set would lose two.
4643        let mut f = Fixture::new();
4644        f.run(&[b"HELLO", b"3"]);
4645        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
4646
4647        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
4648        // And a positive count is an array too, since Redis makes it one.
4649        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4650
4651        // A negative count against a set of one is where the difference bites:
4652        // the same member three times, which is a three element reply and would
4653        // have been a one element reply if it had gone out as a set.
4654        f.run(&[b"SADD", b"one", b"z"]);
4655        assert_eq!(
4656            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
4657            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
4658        );
4659    }
4660
4661    #[test]
4662    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
4663        let mut f = Fixture::new();
4664        f.run(&[b"SADD", b"s", b"only"]);
4665        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4666        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4667        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
4668
4669        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
4670        // The count form answers an empty array rather than a nil, which is the
4671        // pair of answers Redis gives and is not the pair it looks like.
4672        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
4673        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
4674        // Asking for more than is there answers all of it once and not padding.
4675        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
4676    }
4677
4678    #[test]
4679    fn a_pop_count_that_is_not_a_positive_number_says_so() {
4680        let mut f = Fixture::new();
4681        f.run(&[b"SADD", b"s", b"a"]);
4682        let bad = "-ERR value is out of range, must be positive\r\n";
4683        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
4684        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
4685        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
4686        // Zero is allowed and is a real answer rather than an error.
4687        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
4688        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
4689    }
4690
4691    #[test]
4692    fn a_scan_walks_a_set_of_any_size_exactly_once() {
4693        let mut f = Fixture::new();
4694        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
4695        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
4696            .into_iter()
4697            .chain(members.iter().map(Vec::as_slice))
4698            .collect();
4699        f.run(&args);
4700
4701        let mut seen = Vec::new();
4702        let mut cursor = "0".to_owned();
4703        loop {
4704            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
4705            let (next, got) = split_scan(&reply);
4706            seen.extend(got);
4707            cursor = next;
4708            if cursor == "0" {
4709                break;
4710            }
4711        }
4712        seen.sort();
4713        seen.dedup();
4714        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
4715
4716        // A set small enough to be a listpack answers in one call whatever
4717        // cursor it was handed, which is what Redis does for that encoding.
4718        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
4719        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
4720        assert_eq!(cursor, "0");
4721        assert_eq!(got.len(), 3);
4722        // And a key that is not there is a finished scan of nothing.
4723        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
4724    }
4725
4726    #[test]
4727    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
4728        let mut f = Fixture::new();
4729        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
4730
4731        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
4732        let mut got = got;
4733        got.sort();
4734        assert_eq!(got, ["aa", "ab"]);
4735
4736        // An integer member has no digits stored anywhere, so MATCH is the one
4737        // place a scan pays to write some.
4738        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4739        let mut got = got;
4740        got.sort();
4741        assert_eq!(got, ["12", "13"]);
4742
4743        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4744        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4745        assert_eq!(
4746            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4747            "-ERR syntax error\r\n"
4748        );
4749        // A count under one is a syntax error and not a range error, which is
4750        // the odder of Redis's two answers and the reason it is copied exactly.
4751        assert_eq!(
4752            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4753            "-ERR syntax error\r\n"
4754        );
4755    }
4756
4757    #[test]
4758    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4759        let mut f = Fixture::new();
4760        f.run(&[b"SADD", b"src", b"a", b"b"]);
4761        f.run(&[b"SADD", b"dst", b"c"]);
4762
4763        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4764        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4765        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4766        // A member that is not in the source is a zero and moves nothing.
4767        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4768        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4769
4770        // A destination that does not exist gets made, and a source that runs
4771        // out goes away.
4772        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4773        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4774        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4775    }
4776
4777    #[test]
4778    fn moving_checks_the_types_in_the_order_redis_checks_them() {
4779        // Not the order it looks like it should be. A source that is not there
4780        // answers zero without ever looking at the destination, so this is a
4781        // zero and not a WRONGTYPE even though the destination is a string.
4782        let mut f = Fixture::new();
4783        f.run(&[b"SET", b"str", b"v"]);
4784        f.run(&[b"SADD", b"set", b"a"]);
4785
4786        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4787        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4788        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4789        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4790        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4791        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4792        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4793        assert_eq!(
4794            f.run(&[b"SISMEMBER", b"set", b"a"]),
4795            ":1\r\n",
4796            "and none of that moved anything"
4797        );
4798    }
4799
4800    #[test]
4801    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4802        // SSCAN writes an outer array header before it walks, so it is the
4803        // command most likely to get bytes out in front of an error.
4804        let mut f = Fixture::new();
4805        f.run(&[b"SADD", b"s", b"a"]);
4806        for bad in [
4807            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4808            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4809            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4810        ] {
4811            let reply = f.run(bad);
4812            assert!(reply.starts_with("-ERR"), "got {reply}");
4813            assert!(!reply.contains('*'), "an array header went out in front");
4814        }
4815    }
4816
4817    #[test]
4818    fn a_hash_writes_reads_and_deletes_its_fields() {
4819        let mut f = Fixture::new();
4820        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4821        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4822        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4823        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4824        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4825        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4826        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4827        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4828        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4829        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4830
4831        // The value the client sent is `9`, so HGET h b must not find the `2`
4832        // that is a value. A search with a step of one would have.
4833        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4834
4835        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4836        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4837        assert_eq!(
4838            f.run(&[b"EXISTS", b"h"]),
4839            ":0\r\n",
4840            "and losing the last field lost the key"
4841        );
4842    }
4843
4844    #[test]
4845    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4846        let mut f = Fixture::new();
4847        f.run(&[b"HSET", b"h", b"a", b"1"]);
4848        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4849        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4850        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4851        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4852        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4853
4854        f.run(&[b"HELLO", b"3"]);
4855        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4856        assert_eq!(
4857            f.run(&[b"HGETALL", b"nokey"]),
4858            "%0\r\n",
4859            "a missing key is the empty hash and never a nil"
4860        );
4861        assert_eq!(
4862            f.run(&[b"HKEYS", b"h"]),
4863            "*1\r\n$1\r\na\r\n",
4864            "and the two that answer one side stay arrays"
4865        );
4866    }
4867
4868    #[test]
4869    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4870        let mut f = Fixture::new();
4871        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4872        assert_eq!(
4873            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4874            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4875            "the reply is positional, so b is a nil and not a gap"
4876        );
4877        assert_eq!(
4878            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4879            "*2\r\n$-1\r\n$-1\r\n",
4880            "and a missing key is all nils rather than an empty array"
4881        );
4882
4883        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4884        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4885        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4886    }
4887
4888    #[test]
4889    fn a_hash_counts_up_and_says_so_when_it_cannot() {
4890        let mut f = Fixture::new();
4891        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4892        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4893        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4894        assert_eq!(
4895            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4896            "$4\r\n10.5\r\n",
4897            "a bulk string and not a double, on both protocols"
4898        );
4899
4900        f.run(&[b"HSET", b"h", b"s", b"words"]);
4901        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4902        assert!(
4903            bad.starts_with("-ERR hash value is not an integer"),
4904            "{bad}"
4905        );
4906        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4907        assert!(
4908            bad.starts_with("-ERR value is not an integer"),
4909            "a bad argument is not yet a hash value, {bad}"
4910        );
4911        assert_eq!(
4912            f.run(&[b"HGET", b"h", b"s"]),
4913            "$5\r\nwords\r\n",
4914            "and neither of them wrote anything"
4915        );
4916    }
4917
4918    #[test]
4919    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4920        let mut f = Fixture::new();
4921        for i in 0..500 {
4922            let field = format!("field-{i}");
4923            let value = format!("value-{i}");
4924            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4925        }
4926
4927        let mut seen: Vec<String> = Vec::new();
4928        let mut cursor = "0".to_owned();
4929        loop {
4930            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4931            let (next, items) = scan_reply(&reply);
4932            assert_eq!(items.len() % 2, 0, "a pair went out half written");
4933            for pair in items.chunks(2) {
4934                assert_eq!(
4935                    pair[0].strip_prefix("field-"),
4936                    pair[1].strip_prefix("value-"),
4937                    "a field came back with someone else's value"
4938                );
4939                seen.push(pair[0].clone());
4940            }
4941            cursor = next;
4942            if cursor == "0" {
4943                break;
4944            }
4945        }
4946        seen.sort();
4947        seen.dedup();
4948        assert_eq!(seen.len(), 500, "every field once and only once");
4949
4950        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4951        assert!(
4952            items.iter().all(|s| s.starts_with("field-")),
4953            "NOVALUES still sent the values"
4954        );
4955
4956        let (_, one) = scan_reply(&f.run(&[
4957            b"HSCAN",
4958            b"h",
4959            b"0",
4960            b"MATCH",
4961            b"field-499",
4962            b"COUNT",
4963            b"1000",
4964        ]));
4965        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4966    }
4967
4968    #[test]
4969    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4970        let mut f = Fixture::new();
4971        f.run(&[b"HSET", b"h", b"a", b"1"]);
4972        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4973        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4974        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4975        assert_eq!(
4976            f.run(&[b"HRANDFIELD", b"h", b"3"]),
4977            "*1\r\n$1\r\na\r\n",
4978            "a positive count is capped at the size of the hash"
4979        );
4980        assert_eq!(
4981            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4982            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4983            "and a negative one repeats itself"
4984        );
4985        assert_eq!(
4986            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4987            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4988            "flat on RESP2"
4989        );
4990
4991        f.run(&[b"HELLO", b"3"]);
4992        assert_eq!(
4993            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4994            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4995            "and nested on RESP3, but still an array and never a map"
4996        );
4997    }
4998
4999    #[test]
5000    fn every_hash_command_says_wrongtype_and_writes_nothing() {
5001        let mut f = Fixture::new();
5002        f.run(&[b"SET", b"str", b"v"]);
5003        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5004
5005        for cmd in [
5006            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
5007            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
5008            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
5009            &[b"HGET".as_slice(), b"str", b"f"][..],
5010            &[b"HMGET".as_slice(), b"str", b"f"][..],
5011            &[b"HDEL".as_slice(), b"str", b"f"][..],
5012            &[b"HLEN".as_slice(), b"str"][..],
5013            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
5014            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
5015            &[b"HGETALL".as_slice(), b"str"][..],
5016            &[b"HKEYS".as_slice(), b"str"][..],
5017            &[b"HVALS".as_slice(), b"str"][..],
5018            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
5019            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
5020            &[b"HRANDFIELD".as_slice(), b"str"][..],
5021            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
5022            &[b"HSCAN".as_slice(), b"str", b"0"][..],
5023        ] {
5024            let reply = f.run(cmd);
5025            assert_eq!(reply, wrong, "{:?}", cmd[0]);
5026        }
5027        assert_eq!(
5028            f.run(&[b"GET", b"str"]),
5029            "$1\r\nv\r\n",
5030            "and none of them touched the value"
5031        );
5032    }
5033
5034    #[test]
5035    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
5036        let mut f = Fixture::new();
5037        f.run(&[b"HSET", b"h", b"f", b"v"]);
5038        for bad in [
5039            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
5040            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
5041            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
5042            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
5043        ] {
5044            let reply = f.run(bad);
5045            assert!(reply.starts_with("-ERR"), "got {reply}");
5046            assert!(!reply.contains('*'), "an array header went out in front");
5047        }
5048    }
5049
5050    #[test]
5051    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
5052        let mut f = Fixture::new();
5053        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5054        assert_eq!(
5055            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
5056            "*1\r\n:1\r\n"
5057        );
5058        assert_eq!(
5059            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5060            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
5061            "one answer per field, and the two sentinels are TTL's own"
5062        );
5063
5064        // The same deadline in the other three units, all of them derived from
5065        // the one number the store kept.
5066        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
5067        assert!((99_000..=100_000).contains(&ms), "got {ms}");
5068        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5069        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
5070        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
5071        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
5072
5073        assert_eq!(
5074            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
5075            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
5076            "one for the deadline taken off, and it does not say what it was"
5077        );
5078        assert_eq!(
5079            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5080            "*1\r\n:-1\r\n"
5081        );
5082        assert_eq!(
5083            f.run(&[b"HGET", b"h", b"a"]),
5084            "$1\r\n1\r\n",
5085            "and the field is still there with the value it had"
5086        );
5087    }
5088
5089    #[test]
5090    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
5091        let mut f = Fixture::new();
5092        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5093        assert_eq!(
5094            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
5095            "*1\r\n:2\r\n",
5096            "two, and not one, because nothing was stored"
5097        );
5098        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5099        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5100
5101        assert_eq!(
5102            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
5103            "*1\r\n:2\r\n"
5104        );
5105        assert_eq!(
5106            f.run(&[b"EXISTS", b"h"]),
5107            ":0\r\n",
5108            "and the last field going took the key with it"
5109        );
5110
5111        // Zero is a delete and not an error, where minus one is an error. That
5112        // is Redis's split and it is easy to get backwards.
5113        f.run(&[b"HSET", b"h", b"a", b"1"]);
5114        assert_eq!(
5115            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
5116            "*1\r\n:2\r\n"
5117        );
5118    }
5119
5120    #[test]
5121    fn a_field_is_gone_once_its_moment_passes() {
5122        let mut f = Fixture::new();
5123        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5124        assert_eq!(
5125            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
5126            "*1\r\n:1\r\n"
5127        );
5128        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
5129
5130        // Time moves once per turn of the event loop and nowhere else, so a
5131        // test moves it by hand rather than by sleeping. There is nothing to
5132        // sleep for: the deadline is a number and so is the clock.
5133        f.server.advance_clock_ms(60);
5134        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5135        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5136        assert_eq!(
5137            f.run(&[b"HGETALL", b"h"]),
5138            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5139            "and the walks do not hand back a field that has expired"
5140        );
5141    }
5142
5143    #[test]
5144    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5145        let mut f = Fixture::new();
5146        for cmd in [
5147            &[
5148                b"HEXPIRE".as_slice(),
5149                b"nokey",
5150                b"100",
5151                b"FIELDS",
5152                b"2",
5153                b"a",
5154                b"b",
5155            ][..],
5156            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5157            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5158            &[
5159                b"HEXPIRETIME".as_slice(),
5160                b"nokey",
5161                b"FIELDS",
5162                b"2",
5163                b"a",
5164                b"b",
5165            ][..],
5166            &[
5167                b"HPERSIST".as_slice(),
5168                b"nokey",
5169                b"FIELDS",
5170                b"2",
5171                b"a",
5172                b"b",
5173            ][..],
5174        ] {
5175            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5176        }
5177    }
5178
5179    #[test]
5180    fn writing_a_field_clears_the_deadline_that_was_on_it() {
5181        let mut f = Fixture::new();
5182        f.run(&[b"HSET", b"h", b"a", b"1"]);
5183        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5184        f.run(&[b"HSET", b"h", b"a", b"2"]);
5185        assert_eq!(
5186            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5187            "*1\r\n:-1\r\n",
5188            "Redis has done this since 7.4, and it is why HGETEX exists"
5189        );
5190    }
5191
5192    #[test]
5193    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5194        let mut f = Fixture::new();
5195        f.run(&[b"HSET", b"h", b"a", b"1"]);
5196        assert_eq!(
5197            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5198            "*1\r\n:0\r\n",
5199            "XX on a field with no deadline changes nothing"
5200        );
5201        assert_eq!(
5202            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5203            "*1\r\n:1\r\n"
5204        );
5205        assert_eq!(
5206            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5207            "*1\r\n:0\r\n",
5208            "and NX will not move one that is already there"
5209        );
5210        assert_eq!(
5211            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5212            "*1\r\n:0\r\n"
5213        );
5214        assert_eq!(
5215            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5216            "*1\r\n:1\r\n"
5217        );
5218        assert_eq!(
5219            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5220            "*1\r\n:1\r\n"
5221        );
5222        assert_eq!(
5223            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5224            "*1\r\n:50\r\n"
5225        );
5226    }
5227
5228    #[test]
5229    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5230        let mut f = Fixture::new();
5231        f.run(&[b"HSET", b"h", b"a", b"1"]);
5232        for (bad, want) in [
5233            (
5234                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5235                "-ERR invalid expire time, must be >= 0",
5236            ),
5237            (
5238                &[
5239                    b"HEXPIRE".as_slice(),
5240                    b"h",
5241                    b"9999999999999999",
5242                    b"FIELDS",
5243                    b"1",
5244                    b"a",
5245                ][..],
5246                "-ERR invalid expire time in 'hexpire' command",
5247            ),
5248            (
5249                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5250                "-ERR wrong number of arguments for 'hexpire' command",
5251            ),
5252            (
5253                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5254                "-ERR Parameter `numFields` should be greater than 0",
5255            ),
5256            (
5257                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5258                "-ERR wrong number of arguments",
5259            ),
5260            (
5261                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5262                "-ERR wrong number of arguments",
5263            ),
5264        ] {
5265            let reply = f.run(bad);
5266            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5267            assert!(!reply.contains('*'), "an array header went out in front");
5268        }
5269        assert_eq!(
5270            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5271            "*1\r\n:-1\r\n",
5272            "and not one of them put a deadline on anything"
5273        );
5274    }
5275
5276    #[test]
5277    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5278        let mut f = Fixture::new();
5279        f.run(&[b"SET", b"str", b"v"]);
5280        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5281
5282        for cmd in [
5283            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5284            &[
5285                b"HPEXPIRE".as_slice(),
5286                b"str",
5287                b"100",
5288                b"FIELDS",
5289                b"1",
5290                b"f",
5291            ][..],
5292            &[
5293                b"HEXPIREAT".as_slice(),
5294                b"str",
5295                b"9999999999",
5296                b"FIELDS",
5297                b"1",
5298                b"f",
5299            ][..],
5300            &[
5301                b"HPEXPIREAT".as_slice(),
5302                b"str",
5303                b"9999999999999",
5304                b"FIELDS",
5305                b"1",
5306                b"f",
5307            ][..],
5308            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5309            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5310            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5311            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5312            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5313        ] {
5314            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5315        }
5316        assert_eq!(
5317            f.run(&[b"GET", b"str"]),
5318            "$1\r\nv\r\n",
5319            "and none of them touched the value"
5320        );
5321    }
5322
5323    #[test]
5324    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5325        let mut f = Fixture::new();
5326        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5327        assert_eq!(
5328            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5329            "*2\r\n$1\r\n1\r\n$-1\r\n",
5330            "positional, so the field that was not there is a nil in its place"
5331        );
5332        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5333        assert_eq!(
5334            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5335            "*1\r\n$-1\r\n"
5336        );
5337        assert_eq!(
5338            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5339            "*1\r\n$1\r\n2\r\n"
5340        );
5341        assert_eq!(
5342            f.run(&[b"EXISTS", b"h"]),
5343            ":0\r\n",
5344            "and the last field took the key"
5345        );
5346    }
5347
5348    #[test]
5349    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5350        let mut f = Fixture::new();
5351        f.run(&[b"HSET", b"h", b"a", b"1"]);
5352        assert_eq!(
5353            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5354            "*1\r\n$1\r\n1\r\n"
5355        );
5356        assert_eq!(
5357            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5358            "*1\r\n:-1\r\n",
5359            "no option means leave it alone, which is the one place this is not GETEX"
5360        );
5361
5362        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5363        assert_eq!(
5364            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5365            "*1\r\n:100\r\n"
5366        );
5367        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5368        assert_eq!(
5369            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5370            "*1\r\n:100\r\n",
5371            "and a plain read really does leave it alone"
5372        );
5373        assert_eq!(
5374            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5375            "*1\r\n$1\r\n1\r\n"
5376        );
5377        assert_eq!(
5378            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5379            "*1\r\n:-1\r\n"
5380        );
5381
5382        assert_eq!(
5383            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5384            "*1\r\n$1\r\n1\r\n",
5385            "the value goes out before the deadline that has already gone is applied"
5386        );
5387        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5388        assert_eq!(
5389            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5390            "*1\r\n$-1\r\n"
5391        );
5392    }
5393
5394    #[test]
5395    fn hsetex_writes_all_of_it_or_none_of_it() {
5396        let mut f = Fixture::new();
5397        assert_eq!(
5398            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5399            ":1\r\n"
5400        );
5401        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5402        assert_eq!(
5403            f.run(&[
5404                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5405            ]),
5406            ":0\r\n",
5407            "FNX wants every field named to be missing"
5408        );
5409        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5410        assert_eq!(
5411            f.run(&[b"HEXISTS", b"h", b"new"]),
5412            ":0\r\n",
5413            "and none of the list was written"
5414        );
5415        assert_eq!(
5416            f.run(&[
5417                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5418            ]),
5419            ":0\r\n",
5420            "and FXX wants every one of them to be there"
5421        );
5422        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5423        assert_eq!(
5424            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5425            ":1\r\n"
5426        );
5427        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5428
5429        assert_eq!(
5430            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5431            ":0\r\n"
5432        );
5433        assert_eq!(
5434            f.run(&[b"EXISTS", b"gone"]),
5435            ":0\r\n",
5436            "a key with no fields cannot meet FXX and is not created trying"
5437        );
5438    }
5439
5440    #[test]
5441    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5442        let mut f = Fixture::new();
5443        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5444        assert_eq!(
5445            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5446            "*1\r\n:100\r\n"
5447        );
5448
5449        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5450        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5451        assert_eq!(
5452            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5453            "*1\r\n:100\r\n",
5454            "KEEPTTL put back what the write cleared"
5455        );
5456
5457        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5458        assert_eq!(
5459            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5460            "*1\r\n:-1\r\n",
5461            "and without it a write clears the deadline the way HSET does"
5462        );
5463
5464        // Any order, because Redis reads these in a loop and not in a fixed
5465        // sequence.
5466        assert_eq!(
5467            f.run(&[
5468                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5469            ]),
5470            ":1\r\n"
5471        );
5472        assert_eq!(
5473            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5474            "*1\r\n:100\r\n"
5475        );
5476
5477        assert_eq!(
5478            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5479            ":1\r\n",
5480            "written, and not the separate code the HEXPIRE family has for this"
5481        );
5482        assert_eq!(
5483            f.run(&[b"EXISTS", b"h"]),
5484            ":0\r\n",
5485            "and storing it and then removing it emptied the hash"
5486        );
5487    }
5488
5489    #[test]
5490    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5491        let mut f = Fixture::new();
5492        f.run(&[b"HSET", b"h", b"a", b"1"]);
5493        for (bad, want) in [
5494            // HGETDEL has three sentences of its own for these three mistakes.
5495            (
5496                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5497                "-ERR Number of fields must be a positive integer",
5498            ),
5499            (
5500                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5501                "-ERR The `numfields` parameter must match the number of arguments",
5502            ),
5503            (
5504                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5505                "-ERR Mandatory argument FIELDS is missing or not at the right position",
5506            ),
5507            // And HGETEX and HSETEX have three different ones between them.
5508            (
5509                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5510                "-ERR invalid number of fields",
5511            ),
5512            (
5513                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5514                "-ERR wrong number of arguments",
5515            ),
5516            (
5517                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5518                "-ERR unknown argument: FIELD",
5519            ),
5520            (
5521                &[
5522                    b"HGETEX".as_slice(),
5523                    b"h",
5524                    b"KEEPTTL",
5525                    b"FIELDS",
5526                    b"1",
5527                    b"a",
5528                ][..],
5529                "-ERR unknown argument: KEEPTTL",
5530            ),
5531            (
5532                &[
5533                    b"HGETEX".as_slice(),
5534                    b"h",
5535                    b"EX",
5536                    b"100",
5537                    b"PERSIST",
5538                    b"FIELDS",
5539                    b"1",
5540                    b"a",
5541                ][..],
5542                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5543            ),
5544            (
5545                &[
5546                    b"HSETEX".as_slice(),
5547                    b"h",
5548                    b"EX",
5549                    b"1",
5550                    b"KEEPTTL",
5551                    b"FIELDS",
5552                    b"1",
5553                    b"a",
5554                    b"1",
5555                ][..],
5556                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5557            ),
5558            (
5559                &[
5560                    b"HSETEX".as_slice(),
5561                    b"h",
5562                    b"FNX",
5563                    b"FXX",
5564                    b"FIELDS",
5565                    b"1",
5566                    b"a",
5567                    b"1",
5568                ][..],
5569                "-ERR Only one of FXX or FNX arguments can be specified",
5570            ),
5571            (
5572                &[
5573                    b"HSETEX".as_slice(),
5574                    b"h",
5575                    b"FIELDS",
5576                    b"2",
5577                    b"a",
5578                    b"1",
5579                    b"b",
5580                ][..],
5581                "-ERR wrong number of arguments",
5582            ),
5583            (
5584                &[
5585                    b"HGETEX".as_slice(),
5586                    b"h",
5587                    b"EX",
5588                    b"-1",
5589                    b"FIELDS",
5590                    b"1",
5591                    b"a",
5592                ][..],
5593                "-ERR invalid expire time, must be >= 0",
5594            ),
5595            (
5596                &[
5597                    b"HGETEX".as_slice(),
5598                    b"h",
5599                    b"PXAT",
5600                    b"99999999999999",
5601                    b"FIELDS",
5602                    b"1",
5603                    b"a",
5604                ][..],
5605                "-ERR invalid expire time in 'hgetex' command",
5606            ),
5607            (
5608                &[
5609                    b"HSETEX".as_slice(),
5610                    b"h",
5611                    b"EX",
5612                    b"abc",
5613                    b"FIELDS",
5614                    b"1",
5615                    b"a",
5616                    b"1",
5617                ][..],
5618                "-ERR value is not an integer or out of range",
5619            ),
5620        ] {
5621            let reply = f.run(bad);
5622            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5623            assert!(!reply.contains('*'), "an array header went out in front");
5624        }
5625        assert_eq!(
5626            f.run(&[b"HGET", b"h", b"a"]),
5627            "$1\r\n1\r\n",
5628            "and not one of them wrote anything"
5629        );
5630        assert_eq!(
5631            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5632            "*1\r\n:-1\r\n"
5633        );
5634    }
5635
5636    #[test]
5637    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
5638        let mut f = Fixture::new();
5639        f.run(&[b"SET", b"str", b"v"]);
5640        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5641        for cmd in [
5642            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5643            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5644            &[
5645                b"HGETEX".as_slice(),
5646                b"str",
5647                b"EX",
5648                b"100",
5649                b"FIELDS",
5650                b"1",
5651                b"f",
5652            ][..],
5653            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
5654        ] {
5655            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5656        }
5657        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5658    }
5659
5660    /// The two orders `HIMPORT` juggles, which are not the same order.
5661    ///
5662    /// Values arrive in the order the fields were declared in and the hash is
5663    /// built in sorted order, so the first value is not generally the first
5664    /// field. And the sort is by length before bytes, which nothing else here
5665    /// sorts names with: `b` comes before `aa` where a plain byte comparison
5666    /// would put `aa` first. Both read off 8.10.1.
5667    #[test]
5668    fn himport_writes_declared_values_into_sorted_fields() {
5669        let mut f = Fixture::new();
5670        assert_eq!(
5671            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
5672            "+OK\r\n"
5673        );
5674        assert_eq!(
5675            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
5676            "+OK\r\n"
5677        );
5678        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
5679        assert_eq!(
5680            f.run(&[b"HGETALL", b"k"]),
5681            bulks(&["a", "3", "b", "1", "aa", "2"])
5682        );
5683    }
5684
5685    /// It replaces the key rather than writing over it, so a field the fieldset
5686    /// does not name is gone afterwards and so is the deadline.
5687    #[test]
5688    fn himport_set_replaces_the_whole_key() {
5689        let mut f = Fixture::new();
5690        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
5691        f.run(&[b"EXPIRE", b"k", b"100"]);
5692        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5693        assert_eq!(
5694            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5695            "+OK\r\n"
5696        );
5697        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5698        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5699    }
5700
5701    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
5702    /// throws them away, and a key built from one outlives it.
5703    #[test]
5704    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
5705        let mut f = Fixture::new();
5706        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
5707        f.run(&[b"SELECT", b"1"]);
5708        assert_eq!(
5709            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5710            "+OK\r\n"
5711        );
5712        f.run(&[b"SELECT", b"0"]);
5713        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
5714        assert_eq!(
5715            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
5716            "-ERR no such fieldset\r\n"
5717        );
5718    }
5719
5720    /// Which complaint wins when a line is wrong in more than one place.
5721    ///
5722    /// The type of the key beats both of the others, so a `HIMPORT SET` against
5723    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
5724    /// the ordering a real server has and not the one the argument order
5725    /// suggests.
5726    #[test]
5727    fn himport_complains_in_the_order_a_real_server_does() {
5728        let mut f = Fixture::new();
5729        f.run(&[b"SET", b"str", b"v"]);
5730        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5731        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5732        assert_eq!(
5733            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
5734            wrong,
5735            "the type beats a missing fieldset"
5736        );
5737        assert_eq!(
5738            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
5739            wrong,
5740            "and it beats a value count that does not fit"
5741        );
5742        assert_eq!(
5743            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
5744            "-ERR no such fieldset\r\n"
5745        );
5746        // One sentence for too few and for too many alike.
5747        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
5748            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
5749            line.extend_from_slice(values);
5750            assert_eq!(
5751                f.run(&line),
5752                "-ERR value count does not match fieldset field count\r\n",
5753                "{} values into two fields",
5754                values.len()
5755            );
5756        }
5757        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5758    }
5759
5760    /// The arity of each subcommand, and the unknown one.
5761    #[test]
5762    fn himport_checks_each_subcommand_count_under_its_own_name() {
5763        let mut f = Fixture::new();
5764        assert_eq!(
5765            f.run(&[b"HIMPORT"]),
5766            "-ERR wrong number of arguments for 'himport' command\r\n"
5767        );
5768        for (rest, name) in [
5769            (&["PREPARE"][..], "prepare"),
5770            (&["PREPARE", "fs"][..], "prepare"),
5771            (&["SET"][..], "set"),
5772            (&["SET", "k"][..], "set"),
5773            (&["SET", "k", "fs"][..], "set"),
5774            (&["DISCARD"][..], "discard"),
5775            (&["DISCARD", "a", "b"][..], "discard"),
5776            (&["DISCARDALL", "x"][..], "discardall"),
5777        ] {
5778            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
5779            line.extend(rest.iter().map(|a| a.as_bytes()));
5780            assert_eq!(
5781                f.run(&line),
5782                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
5783                "HIMPORT {}",
5784                rest.join(" ")
5785            );
5786        }
5787        assert_eq!(
5788            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
5789            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
5790        );
5791    }
5792
5793    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
5794    /// is the answer of the two that could not be guessed from outside.
5795    #[test]
5796    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
5797        let mut f = Fixture::new();
5798        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5799        assert_eq!(
5800            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
5801            "-ERR duplicate field name in fieldset\r\n"
5802        );
5803        assert_eq!(
5804            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5805            "+OK\r\n"
5806        );
5807        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5808    }
5809
5810    /// Preparing the same name twice replaces it, and the two discards count
5811    /// what they took rather than answering OK.
5812    #[test]
5813    fn himport_prepare_replaces_and_the_discards_count() {
5814        let mut f = Fixture::new();
5815        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5816        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
5817        assert_eq!(
5818            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5819            "+OK\r\n"
5820        );
5821        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
5822
5823        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
5824        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
5825        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
5826        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
5827        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
5828        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
5829    }
5830
5831    /// The one integer of a single element array reply.
5832    /// The number out of a plain integer reply.
5833    ///
5834    /// [`int_reply`] is the same thing wrapped in a one element array, which is
5835    /// the shape every hash field command answers in.
5836    fn int(reply: &str) -> i64 {
5837        let body = reply
5838            .strip_prefix(':')
5839            .and_then(|s| s.strip_suffix("\r\n"))
5840            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
5841        body.parse().expect("an integer")
5842    }
5843
5844    fn int_reply(reply: &str) -> i64 {
5845        let body = reply
5846            .strip_prefix("*1\r\n:")
5847            .and_then(|s| s.strip_suffix("\r\n"))
5848            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
5849        body.parse().expect("an integer")
5850    }
5851
5852    /// The cursor and the flat items of a scan reply.
5853    fn scan_reply(reply: &str) -> (String, Vec<String>) {
5854        let mut lines = reply.split("\r\n");
5855        assert_eq!(lines.next(), Some("*2"), "got {reply}");
5856        lines.next().expect("the cursor header");
5857        let cursor = lines.next().expect("a cursor").to_owned();
5858        let header = lines.next().expect("an item count");
5859        let n: usize = header[1..].parse().expect("a count");
5860        let mut items = Vec::with_capacity(n);
5861        for _ in 0..n {
5862            lines.next().expect("an item header");
5863            items.push(lines.next().expect("an item").to_owned());
5864        }
5865        (cursor, items)
5866    }
5867
5868    /// The members of a set reply, sorted, since none of these promise an
5869    /// order and a test that asserted one would be asserting an accident.
5870    fn sorted(reply: &str) -> Vec<String> {
5871        let mut lines = reply.split("\r\n");
5872        let header = lines.next().expect("a header");
5873        assert!(
5874            header.starts_with('*') || header.starts_with('~'),
5875            "got {reply}"
5876        );
5877        let n: usize = header[1..].parse().expect("a member count");
5878        let mut got = Vec::with_capacity(n);
5879        for _ in 0..n {
5880            lines.next().expect("a member header");
5881            got.push(lines.next().expect("a member").to_owned());
5882        }
5883        got.sort();
5884        got
5885    }
5886
5887    #[test]
5888    fn the_algebra_answers_what_the_sets_share_and_do_not() {
5889        let mut f = Fixture::new();
5890        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5891        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5892        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
5893
5894        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
5895        assert_eq!(
5896            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
5897            ["1", "2", "3", "4", "5"]
5898        );
5899        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
5900        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
5901
5902        // A key that is not there is an empty set, which empties an
5903        // intersection and does nothing at all to a union.
5904        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
5905        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5906        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5907        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5908    }
5909
5910    #[test]
5911    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5912        let mut f = Fixture::new();
5913        f.run(&[b"SADD", b"a", b"x"]);
5914        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5915        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5916        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5917
5918        f.run(&[b"HELLO", b"3"]);
5919        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5920        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5921        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5922        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5923    }
5924
5925    #[test]
5926    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5927        let mut f = Fixture::new();
5928        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5929        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5930
5931        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5932        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5933        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5934        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5935        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5936        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5937
5938        // An empty answer deletes the destination rather than leaving an empty
5939        // set behind, and the destination may be one of the sources.
5940        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5941        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5942        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5943        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5944
5945        // And a destination holding something else is overwritten, the same way
5946        // SET overwrites, rather than refused.
5947        f.run(&[b"SET", b"str", b"v"]);
5948        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5949        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5950    }
5951
5952    #[test]
5953    fn sintercard_counts_without_building_and_stops_at_a_limit() {
5954        let mut f = Fixture::new();
5955        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5956        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5957
5958        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5959        assert_eq!(
5960            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5961            ":2\r\n"
5962        );
5963        assert_eq!(
5964            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5965            ":3\r\n",
5966            "a limit of zero is no limit"
5967        );
5968        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5969        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5970
5971        // The counted keys are what make its three error messages its own.
5972        assert_eq!(
5973            f.run(&[b"SINTERCARD", b"0", b"a"]),
5974            "-ERR numkeys should be greater than 0\r\n"
5975        );
5976        assert_eq!(
5977            f.run(&[b"SINTERCARD", b"abc", b"a"]),
5978            "-ERR numkeys should be greater than 0\r\n"
5979        );
5980        assert_eq!(
5981            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5982            "-ERR Number of keys can't be greater than number of args\r\n"
5983        );
5984        assert_eq!(
5985            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5986            "-ERR LIMIT can't be negative\r\n"
5987        );
5988        assert_eq!(
5989            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5990            "-ERR syntax error\r\n"
5991        );
5992        // A key really can be called LIMIT, which is why the count exists.
5993        f.run(&[b"SADD", b"LIMIT", b"2"]);
5994        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5995    }
5996
5997    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
5998    /// over a difference. Every number here was read off 8.10.1 first.
5999    #[test]
6000    fn sunioncard_and_sdiffcard_count_without_building() {
6001        let mut f = Fixture::new();
6002        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
6003        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
6004
6005        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
6006        assert_eq!(
6007            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
6008            ":2\r\n"
6009        );
6010        assert_eq!(
6011            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
6012            ":6\r\n",
6013            "a limit of zero is no limit"
6014        );
6015        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
6016        assert_eq!(
6017            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
6018            ":4\r\n",
6019            "a missing key adds nothing to a union"
6020        );
6021
6022        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
6023        assert_eq!(
6024            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
6025            ":1\r\n"
6026        );
6027        assert_eq!(
6028            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
6029            ":2\r\n",
6030            "a difference is not symmetric"
6031        );
6032        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
6033        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
6034        assert_eq!(
6035            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
6036            ":0\r\n",
6037            "nothing taken away from nothing"
6038        );
6039
6040        // The same three messages SINTERCARD has, because the line is the same
6041        // line and is parsed once for all three.
6042        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
6043            assert_eq!(
6044                f.run(&[name, b"0", b"a"]),
6045                "-ERR numkeys should be greater than 0\r\n"
6046            );
6047            assert_eq!(
6048                f.run(&[name, b"abc", b"a"]),
6049                "-ERR numkeys should be greater than 0\r\n"
6050            );
6051            assert_eq!(
6052                f.run(&[name, b"-1", b"a"]),
6053                "-ERR numkeys should be greater than 0\r\n"
6054            );
6055            assert_eq!(
6056                f.run(&[name, b"3", b"a", b"b"]),
6057                "-ERR Number of keys can't be greater than number of args\r\n"
6058            );
6059            assert_eq!(
6060                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
6061                "-ERR LIMIT can't be negative\r\n"
6062            );
6063            assert_eq!(
6064                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
6065                "-ERR LIMIT can't be negative\r\n",
6066                "a LIMIT that is not a number gets the negative message too"
6067            );
6068            assert_eq!(
6069                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
6070                "-ERR syntax error\r\n"
6071            );
6072            assert_eq!(
6073                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
6074                "-ERR syntax error\r\n"
6075            );
6076            assert_eq!(
6077                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
6078                "-ERR syntax error\r\n"
6079            );
6080        }
6081
6082        // And a key called LIMIT is a key, here as much as on SINTERCARD.
6083        f.run(&[b"SADD", b"LIMIT", b"2"]);
6084        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
6085        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
6086    }
6087
6088    #[test]
6089    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
6090        let mut f = Fixture::new();
6091        f.run(&[b"SADD", b"a", b"1"]);
6092        f.run(&[b"SADD", b"d", b"old"]);
6093        f.run(&[b"SET", b"str", b"v"]);
6094
6095        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6096        for bad in [
6097            &[b"SINTER".as_slice(), b"a", b"str"][..],
6098            &[b"SUNION".as_slice(), b"str"][..],
6099            &[b"SDIFF".as_slice(), b"a", b"str"][..],
6100            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
6101            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
6102            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
6103            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
6104        ] {
6105            let reply = f.run(bad);
6106            assert_eq!(reply, wrong, "for {:?}", bad[0]);
6107        }
6108        assert_eq!(
6109            f.run(&[b"SMEMBERS", b"d"]),
6110            "*1\r\n$3\r\nold\r\n",
6111            "and the destination was left alone every time"
6112        );
6113    }
6114
6115    /// The leak a set can spring that nothing on the wire would ever show: the
6116    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
6117    #[test]
6118    fn churning_sets_does_not_grow_the_server() {
6119        let mut f = Fixture::new();
6120        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6121        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
6122            .chain(std::iter::once(&b"s"[..]))
6123            .chain(members.iter().map(Vec::as_slice))
6124            .collect();
6125
6126        f.run(&args);
6127        f.run(&[b"DEL", b"s"]);
6128        f.server.compact_step();
6129        let after_first = f.server.memory_bytes();
6130
6131        for _ in 0..200 {
6132            f.run(&args);
6133            f.run(&[b"DEL", b"s"]);
6134            f.server.compact_step();
6135        }
6136        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6137        assert!(
6138            f.server.memory_bytes() <= after_first * 2,
6139            "held {} after two hundred passes against {after_first} after one",
6140            f.server.memory_bytes()
6141        );
6142    }
6143
6144    // --------------------------------------------------------------- bitmaps
6145
6146    /// The two single bit commands, and the encoding rule underneath them.
6147    ///
6148    /// A write always leaves the value `raw` and a read never re-encodes, which
6149    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
6150    /// with its first digit changed after a `SETBIT`.
6151    #[test]
6152    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6153        let mut f = Fixture::new();
6154        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6155        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6156        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6157        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6158        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6159        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6160
6161        // Writing a nought past the end still creates the key and still pads.
6162        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6163        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6164        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6165
6166        f.run(&[b"SET", b"num", b"12345"]);
6167        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6168        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6169        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6170        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6171        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6172    }
6173
6174    /// Counting, in bytes and in bits.
6175    ///
6176    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
6177    /// says 22 for it. The server is the thing being copied here.
6178    #[test]
6179    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6180        let mut f = Fixture::new();
6181        f.run(&[b"SET", b"mykey", b"foobar"]);
6182        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6183        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6184        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6185        assert_eq!(
6186            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6187            ":6\r\n"
6188        );
6189        assert_eq!(
6190            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6191            ":25\r\n"
6192        );
6193        assert_eq!(
6194            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6195            ":17\r\n"
6196        );
6197        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6198
6199        // A start past the end is left where it is and the end is pulled back,
6200        // so the range comes out backwards and counts nothing.
6201        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6202
6203        // A lone start is a syntax error here, where BITPOS allows it.
6204        assert_eq!(
6205            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6206            "-ERR syntax error\r\n"
6207        );
6208        assert_eq!(
6209            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6210            "-ERR syntax error\r\n"
6211        );
6212    }
6213
6214    /// Searching, and the one place a miss is not minus one.
6215    ///
6216    /// A search for a nought that runs to the end of the string answers the
6217    /// length in bits, because the string is treated as if it had noughts after
6218    /// it forever. Give it an explicit end and it answers minus one instead.
6219    #[test]
6220    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6221        let mut f = Fixture::new();
6222        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6223        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6224        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6225        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6226        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6227        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6228
6229        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6230        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6231        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6232        assert_eq!(
6233            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6234            ":8\r\n"
6235        );
6236
6237        // A missing key is all noughts, so a one is never found and a nought is
6238        // at position zero.
6239        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6240        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6241    }
6242
6243    /// The eight operations, with the answers a real server gives for them.
6244    #[test]
6245    fn the_eight_combinations_write_what_a_real_server_writes() {
6246        let mut f = Fixture::new();
6247        f.run(&[b"SET", b"a", b"abc"]);
6248        f.run(&[b"SET", b"b", b"abd"]);
6249        let cases: &[(&[u8], &str)] = &[
6250            (b"AND", "ab`"),
6251            (b"OR", "abg"),
6252            (b"XOR", "\u{0}\u{0}\u{7}"),
6253            (b"DIFF", "\u{0}\u{0}\u{3}"),
6254            (b"DIFF1", "\u{0}\u{0}\u{4}"),
6255            (b"ANDOR", "ab`"),
6256            (b"ONE", "\u{0}\u{0}\u{7}"),
6257        ];
6258        for (op, want) in cases {
6259            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6260            assert_eq!(
6261                f.run(&[b"GET", b"d"]),
6262                format!("$3\r\n{want}\r\n"),
6263                "{op:?}"
6264            );
6265        }
6266        // The one whose answer is not text, so it is compared as bytes.
6267        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6268        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6269
6270        // A missing source is a string of noughts as long as it needs to be, so
6271        // an AND against one writes three zero bytes rather than nothing.
6272        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6273        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6274
6275        // Every source missing is an empty result, and an empty result takes
6276        // the destination with it.
6277        f.run(&[b"SET", b"dest", b"x"]);
6278        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6279        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6280    }
6281
6282    /// What `BITOP` says when it is asked for something it cannot do.
6283    #[test]
6284    fn bitop_names_the_operation_in_its_own_complaints() {
6285        let mut f = Fixture::new();
6286        f.run(&[b"SET", b"a", b"abc"]);
6287        assert_eq!(
6288            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6289            "-ERR syntax error\r\n"
6290        );
6291        assert_eq!(
6292            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6293            "-ERR BITOP NOT must be called with a single source key.\r\n"
6294        );
6295        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6296            assert_eq!(
6297                f.run(&[b"BITOP", op, b"d", b"a"]),
6298                format!(
6299                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
6300                    String::from_utf8_lossy(op)
6301                )
6302            );
6303        }
6304        f.run(&[b"LPUSH", b"l", b"x"]);
6305        assert_eq!(
6306            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6307            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6308        );
6309    }
6310
6311    /// Packed fields, the three overflow policies and the `#` offset.
6312    #[test]
6313    fn bitfield_reads_and_writes_packed_fields() {
6314        let mut f = Fixture::new();
6315        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6316        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6317
6318        assert_eq!(
6319            f.run(&[
6320                b"BITFIELD",
6321                b"bf",
6322                b"INCRBY",
6323                b"u2",
6324                b"100",
6325                b"1",
6326                b"GET",
6327                b"u4",
6328                b"0"
6329            ]),
6330            "*2\r\n:1\r\n:0\r\n"
6331        );
6332        // The field at bit 100 is two bits wide, so it ends in the thirteenth
6333        // byte and the value grew to thirteen bytes to hold it.
6334        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6335
6336        // A `#` offset counts in fields rather than in bits.
6337        assert_eq!(
6338            f.run(&[
6339                b"BITFIELD",
6340                b"bf",
6341                b"SET",
6342                b"u8",
6343                b"#0",
6344                b"255",
6345                b"GET",
6346                b"u8",
6347                b"#0"
6348            ]),
6349            "*2\r\n:0\r\n:255\r\n"
6350        );
6351
6352        assert_eq!(
6353            f.run(&[
6354                b"BITFIELD",
6355                b"bf",
6356                b"OVERFLOW",
6357                b"SAT",
6358                b"INCRBY",
6359                b"i8",
6360                b"0",
6361                b"120",
6362                b"INCRBY",
6363                b"i8",
6364                b"0",
6365                b"120"
6366            ]),
6367            "*2\r\n:119\r\n:127\r\n"
6368        );
6369        assert_eq!(
6370            f.run(&[
6371                b"BITFIELD",
6372                b"bf2",
6373                b"OVERFLOW",
6374                b"FAIL",
6375                b"INCRBY",
6376                b"u2",
6377                b"0",
6378                b"5"
6379            ]),
6380            "*1\r\n$-1\r\n"
6381        );
6382        assert_eq!(
6383            f.run(&[
6384                b"BITFIELD",
6385                b"bf3",
6386                b"OVERFLOW",
6387                b"WRAP",
6388                b"INCRBY",
6389                b"u2",
6390                b"0",
6391                b"5"
6392            ]),
6393            "*1\r\n:1\r\n"
6394        );
6395        assert_eq!(
6396            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6397            "*1\r\n:4611686018427387904\r\n"
6398        );
6399    }
6400
6401    /// A bad subcommand anywhere in the line stops all of it.
6402    ///
6403    /// Redis checks the whole argument list before it runs any of it, so the
6404    /// `SET` in front of the bad type here never happens and the key it would
6405    /// have created is not there afterwards.
6406    #[test]
6407    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6408        let mut f = Fixture::new();
6409        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6410        assert_eq!(
6411            f.run(&[
6412                b"BITFIELD",
6413                b"bad",
6414                b"SET",
6415                b"u8",
6416                b"0",
6417                b"1",
6418                b"GET",
6419                b"u99",
6420                b"0"
6421            ]),
6422            bad_type
6423        );
6424        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6425        assert_eq!(
6426            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6427            bad_type
6428        );
6429        assert_eq!(
6430            f.run(&[b"BITFIELD", b"bad", b"GET"]),
6431            "-ERR syntax error\r\n"
6432        );
6433        assert_eq!(
6434            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6435            "-ERR syntax error\r\n"
6436        );
6437        assert_eq!(
6438            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6439            "-ERR syntax error\r\n"
6440        );
6441        assert_eq!(
6442            f.run(&[
6443                b"BITFIELD",
6444                b"bad",
6445                b"OVERFLOW",
6446                b"NOPE",
6447                b"GET",
6448                b"u8",
6449                b"0"
6450            ]),
6451            "-ERR Invalid OVERFLOW type specified\r\n"
6452        );
6453        assert_eq!(
6454            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6455            "-ERR value is not an integer or out of range\r\n"
6456        );
6457        for at in [&b"#-1"[..], b"abc"] {
6458            assert_eq!(
6459                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6460                "-ERR bit offset is not an integer or out of range\r\n"
6461            );
6462        }
6463    }
6464
6465    /// The read only twin reads, refuses to write, and creates nothing.
6466    #[test]
6467    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6468        let mut f = Fixture::new();
6469        f.run(&[b"SET", b"n", b"123"]);
6470        assert_eq!(
6471            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6472            "*1\r\n:49\r\n"
6473        );
6474        // A read does not unpack an int the way a write does.
6475        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6476
6477        // An OVERFLOW word is allowed even though nothing here can overflow.
6478        assert_eq!(
6479            f.run(&[
6480                b"BITFIELD_RO",
6481                b"n",
6482                b"OVERFLOW",
6483                b"SAT",
6484                b"GET",
6485                b"u8",
6486                b"0"
6487            ]),
6488            "*1\r\n:49\r\n"
6489        );
6490        for sub in [&b"SET"[..], b"INCRBY"] {
6491            assert_eq!(
6492                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6493                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6494            );
6495        }
6496
6497        assert_eq!(
6498            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6499            "*1\r\n:0\r\n"
6500        );
6501        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6502    }
6503
6504    /// The offsets a bitmap command will not take.
6505    #[test]
6506    fn an_offset_off_the_end_of_the_world_is_refused() {
6507        let mut f = Fixture::new();
6508        let bad = "-ERR bit offset is not an integer or out of range\r\n";
6509        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6510            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6511            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6512        }
6513        for arg in [&b"2"[..], b"-1"] {
6514            assert_eq!(
6515                f.run(&[b"BITPOS", b"k", arg]),
6516                "-ERR The bit argument must be 1 or 0.\r\n"
6517            );
6518        }
6519        assert_eq!(
6520            f.run(&[b"BITPOS", b"k", b"abc"]),
6521            "-ERR value is not an integer or out of range\r\n"
6522        );
6523        assert_eq!(
6524            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6525            "-ERR value is not an integer or out of range\r\n"
6526        );
6527        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6528        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6529        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6530    }
6531
6532    /// Every one of the seven refuses a key that is not a string.
6533    #[test]
6534    fn every_bitmap_command_says_wrongtype() {
6535        let mut f = Fixture::new();
6536        f.run(&[b"LPUSH", b"l", b"x"]);
6537        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6538        let cases: &[&[&[u8]]] = &[
6539            &[b"SETBIT", b"l", b"0", b"1"],
6540            &[b"GETBIT", b"l", b"0"],
6541            &[b"BITCOUNT", b"l"],
6542            &[b"BITPOS", b"l", b"1"],
6543            &[b"BITOP", b"AND", b"d", b"l"],
6544            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6545            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6546        ];
6547        for case in cases {
6548            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6549        }
6550    }
6551
6552    // --------------------------------------------------------- hyperloglogs
6553
6554    #[test]
6555    fn a_sketch_is_added_to_and_counted() {
6556        let mut f = Fixture::new();
6557        // Creating the key counts as a change, even with nothing to add.
6558        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6559        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6560        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6561        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6562        // And it is a string, which is not an implementation detail: a client
6563        // can `GET` a sketch out of one server and `SET` it into another.
6564        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6565        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6566
6567        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6568        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6569        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6570    }
6571
6572    #[test]
6573    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6574        let mut f = Fixture::new();
6575        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6576        // Not text, so it is compared as bytes.
6577        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";
6578        let mut reply = b"$27\r\n".to_vec();
6579        reply.extend_from_slice(want);
6580        reply.extend_from_slice(b"\r\n");
6581        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6582    }
6583
6584    #[test]
6585    fn counting_several_keys_counts_their_union() {
6586        let mut f = Fixture::new();
6587        f.run(&[b"PFADD", b"a", b"x", b"y"]);
6588        f.run(&[b"PFADD", b"b", b"y", b"z"]);
6589        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6590        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6591        // A key that is not there is an empty sketch, not an error and not
6592        // something that gets created by being counted.
6593        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6594        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6595        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6596    }
6597
6598    #[test]
6599    fn a_merge_keeps_what_the_destination_had() {
6600        let mut f = Fixture::new();
6601        f.run(&[b"PFADD", b"a", b"x", b"y"]);
6602        f.run(&[b"PFADD", b"b", b"z"]);
6603        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
6604        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
6605        // The destination is one of the sources, so a second merge adds to it.
6606        f.run(&[b"PFADD", b"c", b"w"]);
6607        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
6608        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
6609        // And with no sources it is a no-op that still answers OK and still
6610        // creates a destination that was not there.
6611        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
6612        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
6613    }
6614
6615    #[test]
6616    fn the_debug_forms_answer_four_different_shapes() {
6617        let mut f = Fixture::new();
6618        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6619        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
6620        assert_eq!(
6621            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6622            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
6623        );
6624        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
6625        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
6626        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
6627        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
6628        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6629        // A dense sketch has no opcodes left to print.
6630        assert_eq!(
6631            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6632            "-ERR HLL encoding is not sparse\r\n"
6633        );
6634
6635        // All 16384 registers, of which three are not nought.
6636        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
6637        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
6638        assert_eq!(reply.matches(":0\r\n").count(), 16381);
6639        assert_eq!(reply.matches(":1\r\n").count(), 2);
6640        assert_eq!(reply.matches(":2\r\n").count(), 1);
6641
6642        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
6643    }
6644
6645    #[test]
6646    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
6647        let mut f = Fixture::new();
6648        f.run(&[b"SET", b"plain", b"not a sketch"]);
6649        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
6650        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
6651        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
6652        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
6653        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
6654
6655        // A key that is not a string at all gets the ordinary sentence, and a
6656        // destination that would have been written is not created.
6657        f.run(&[b"RPUSH", b"l", b"x"]);
6658        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6659        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
6660        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
6661        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
6662        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6663        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
6664    }
6665
6666    #[test]
6667    fn pfdebug_has_its_own_complaints() {
6668        let mut f = Fixture::new();
6669        f.run(&[b"PFADD", b"h", b"a"]);
6670        // The word is quoted exactly as the client spelled it, and this is not
6671        // the "Try X HELP." sentence every other container command uses.
6672        assert_eq!(
6673            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6674            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6675        );
6676        // Where all three of the real commands take a missing key as empty.
6677        let gone = "-ERR The specified key does not exist\r\n";
6678        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
6679        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
6680        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
6681        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
6682        assert_eq!(
6683            f.run(&[b"PFDEBUG"]),
6684            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
6685        );
6686        assert_eq!(
6687            f.run(&[b"PFSELFTEST", b"x"]),
6688            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
6689        );
6690    }
6691
6692    #[test]
6693    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
6694        let mut f = Fixture::new();
6695        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6696        // The sketch with its last byte cut off, which is still a header and a
6697        // magic and is a run length encoding that stops short of register 16384.
6698        let reply = f.raw(&[b"GET", b"h"]);
6699        let short = reply[5..reply.len() - 3].to_vec();
6700        f.run(&[b"SET", b"h", &short]);
6701        assert_eq!(
6702            f.run(&[b"PFCOUNT", b"h"]),
6703            "-INVALIDOBJ Corrupted HLL object detected\r\n"
6704        );
6705    }
6706
6707    #[test]
6708    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
6709        let mut f = Fixture::new();
6710        // One that stays sparse and one that has gone dense, since the payload
6711        // carries the bytes and the two encodings are different lengths.
6712        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
6713        for i in 0..10_000u32 {
6714            let ele = format!("e{i}");
6715            f.run(&[b"PFADD", b"big", ele.as_bytes()]);
6716        }
6717        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
6718        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
6719
6720        for key in [&b"small"[..], b"big"] {
6721            let mut copy = key.to_vec();
6722            copy.push(b'2');
6723            let bytes = payload(&f.raw(&[b"DUMP", key]));
6724            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
6725            // The bytes, the encoding and the estimate all come back, which is
6726            // the whole of what byte compatibility across a round trip means.
6727            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
6728            assert_eq!(
6729                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
6730                f.run(&[b"PFDEBUG", b"ENCODING", key])
6731            );
6732            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
6733        }
6734        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
6735        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
6736    }
6737
6738    /// One RESP2 bulk string. The JSON replies are almost all one of these and
6739    /// the text inside them has quotes in it, so writing the frame out by hand
6740    /// buries the part of the assertion that matters.
6741    fn bulk(s: &str) -> String {
6742        format!("${}\r\n{s}\r\n", s.len())
6743    }
6744
6745    /// A RESP2 array of bulk strings, which is what most of the list replies
6746    /// are and what writing them out by hand in every assertion looks like.
6747    fn bulks(parts: &[&str]) -> String {
6748        let mut s = format!("*{}\r\n", parts.len());
6749        for p in parts {
6750            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
6751        }
6752        s
6753    }
6754
6755    #[test]
6756    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
6757        let mut f = Fixture::new();
6758        // Each element in turn goes at the head, so the last one sent is at the
6759        // front when it is over. That reads like a bug in the client and it is
6760        // what every Redis has always done.
6761        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
6762        assert_eq!(
6763            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6764            bulks(&["c", "b", "a"])
6765        );
6766        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
6767        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
6768        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
6769        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
6770        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
6771        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
6772    }
6773
6774    #[test]
6775    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
6776        let mut f = Fixture::new();
6777        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
6778        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
6779        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6780        f.run(&[b"RPUSH", b"k", b"a"]);
6781        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
6782        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
6783        assert_eq!(
6784            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6785            bulks(&["z", "a", "y"])
6786        );
6787    }
6788
6789    /// The four ways a pop can come back with nothing, which are three
6790    /// different replies and a RESP2 client can tell all of them apart.
6791    #[test]
6792    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
6793        let mut f = Fixture::new();
6794        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
6795        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
6796        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
6797        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
6798        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6799        // A count of zero against a list that is there is an empty array and
6800        // not a null array, which is the fourth answer.
6801        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
6802        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
6803        // More than there is takes what there is and the key goes with it.
6804        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
6805        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6806    }
6807
6808    #[test]
6809    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
6810        let mut f = Fixture::new();
6811        f.run(&[b"RPUSH", b"k", b"a"]);
6812        let range = "-ERR value is out of range, must be positive\r\n";
6813        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
6814        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
6815        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
6816        // Redis calls this an arity error and not a syntax error, which is a
6817        // distinction it does not always make.
6818        assert_eq!(
6819            f.run(&[b"LPOP", b"k", b"1", b"2"]),
6820            "-ERR wrong number of arguments for 'lpop' command\r\n"
6821        );
6822        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6823    }
6824
6825    #[test]
6826    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
6827        let mut f = Fixture::new();
6828        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6829        assert_eq!(
6830            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6831            bulks(&["a", "b", "c"])
6832        );
6833        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
6834        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
6835        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
6836        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
6837        assert_eq!(
6838            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
6839            bulks(&["a", "b", "c"])
6840        );
6841        // A key that is not there is an empty range and not a nil, which is the
6842        // one place a list disagrees with a set.
6843        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
6844        assert_eq!(
6845            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
6846            "-ERR value is not an integer or out of range\r\n"
6847        );
6848    }
6849
6850    #[test]
6851    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
6852        let mut f = Fixture::new();
6853        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6854        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
6855        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
6856        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
6857        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
6858        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
6859        assert_eq!(
6860            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6861            bulks(&["a", "b", "z"])
6862        );
6863        // Both ways of missing are errors here rather than a nil, because a
6864        // list is never empty and there is nothing else the reply could be.
6865        assert_eq!(
6866            f.run(&[b"LSET", b"k", b"99", b"z"]),
6867            "-ERR index out of range\r\n"
6868        );
6869        assert_eq!(
6870            f.run(&[b"LSET", b"nope", b"0", b"z"]),
6871            "-ERR no such key\r\n"
6872        );
6873    }
6874
6875    #[test]
6876    fn linsert_says_three_things_with_one_signed_number() {
6877        let mut f = Fixture::new();
6878        // Zero for a key that is not there, which is not the same as minus one
6879        // for a pivot that is not in a list that is.
6880        assert_eq!(
6881            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
6882            ":0\r\n"
6883        );
6884        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6885        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
6886        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
6887        assert_eq!(
6888            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6889            bulks(&["X", "a", "b", "Y"])
6890        );
6891        assert_eq!(
6892            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
6893            ":-1\r\n"
6894        );
6895        assert_eq!(
6896            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
6897            "-ERR syntax error\r\n"
6898        );
6899    }
6900
6901    #[test]
6902    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
6903        let mut f = Fixture::new();
6904        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
6905        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
6906        assert_eq!(
6907            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6908            bulks(&["b", "c", "a"])
6909        );
6910        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
6911        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6912        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
6913        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
6914        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6915        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
6916    }
6917
6918    #[test]
6919    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
6920        let mut f = Fixture::new();
6921        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
6922        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
6923        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6924        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
6925        // leave `EXISTS` answering zero rather than leaving an empty one.
6926        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
6927        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6928        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
6929    }
6930
6931    #[test]
6932    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
6933        let mut f = Fixture::new();
6934        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
6935        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
6936        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
6937        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
6938        assert_eq!(
6939            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
6940            "*2\r\n:0\r\n:3\r\n"
6941        );
6942        assert_eq!(
6943            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
6944            "*3\r\n:6\r\n:3\r\n:0\r\n"
6945        );
6946        // MAXLEN counts elements looked at and not matches found, so three
6947        // stops after `a b c` and finds the one match in it.
6948        assert_eq!(
6949            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
6950            "*1\r\n:0\r\n"
6951        );
6952        // Nothing found is three different replies depending on how it was
6953        // asked and whether the key is there at all.
6954        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
6955        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
6956        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
6957        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
6958    }
6959
6960    #[test]
6961    fn lpos_words_its_three_mistakes_the_way_redis_does() {
6962        let mut f = Fixture::new();
6963        f.run(&[b"RPUSH", b"p", b"a"]);
6964        // The whole sentence and not a prefix, because the older wording of it
6965        // is still all over the internet and clients match on the text.
6966        assert_eq!(
6967            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
6968            "-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"
6969        );
6970        assert_eq!(
6971            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
6972            "-ERR COUNT can't be negative\r\n"
6973        );
6974        assert_eq!(
6975            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
6976            "-ERR MAXLEN can't be negative\r\n"
6977        );
6978        assert_eq!(
6979            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
6980            "-ERR syntax error\r\n"
6981        );
6982        assert_eq!(
6983            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
6984            "-ERR syntax error\r\n"
6985        );
6986    }
6987
6988    #[test]
6989    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
6990        let mut f = Fixture::new();
6991        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6992        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
6993        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6994        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
6995        assert_eq!(
6996            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
6997            "$1\r\na\r\n"
6998        );
6999        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
7000        // The same key twice is the documented way to rotate a list and falls
7001        // out of taking the element before deciding where to put it.
7002        f.run(&[b"DEL", b"r"]);
7003        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
7004        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
7005        assert_eq!(
7006            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
7007            bulks(&["3", "1", "2"])
7008        );
7009        assert_eq!(
7010            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
7011            "$-1\r\n"
7012        );
7013        assert_eq!(
7014            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
7015            "-ERR syntax error\r\n"
7016        );
7017    }
7018
7019    #[test]
7020    fn a_move_checks_the_destination_before_it_takes_anything() {
7021        let mut f = Fixture::new();
7022        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
7023        f.run(&[b"SET", b"str", b"v"]);
7024        assert_eq!(
7025            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
7026            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7027        );
7028        // The element is still where it was, rather than having gone nowhere.
7029        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
7030    }
7031
7032    #[test]
7033    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
7034        // OBO is what you get from sending LMOVE that many times, BULK keeps
7035        // the source order. The two only differ when both ends are the same,
7036        // which is the whole reason the word exists.
7037        for (from, to, order, want) in [
7038            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
7039            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
7040            ("LEFT", "LEFT", "OBO", ["b", "a"]),
7041            ("LEFT", "LEFT", "BULK", ["a", "b"]),
7042            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
7043            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
7044            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
7045            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
7046        ] {
7047            let mut f = Fixture::new();
7048            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
7049            let how = format!("{from} {to} {order}");
7050            let reply = f.run(&[
7051                b"LMOVEM",
7052                b"s",
7053                b"d",
7054                from.as_bytes(),
7055                to.as_bytes(),
7056                b"COUNT",
7057                b"2",
7058                order.as_bytes(),
7059            ]);
7060            assert_eq!(reply, bulks(&want), "the reply for {how}");
7061            assert_eq!(
7062                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
7063                bulks(&want),
7064                "the destination for {how}"
7065            );
7066        }
7067    }
7068
7069    #[test]
7070    fn a_block_move_of_one_needs_no_count_at_all() {
7071        let mut f = Fixture::new();
7072        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7073        assert_eq!(
7074            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
7075            bulks(&["a"])
7076        );
7077        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
7078        // Six and seven arguments are neither of the two forms, so the
7079        // reference calls both of them a syntax error rather than guessing.
7080        assert_eq!(
7081            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
7082            "-ERR syntax error\r\n"
7083        );
7084        assert_eq!(
7085            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
7086            "-ERR syntax error\r\n"
7087        );
7088    }
7089
7090    #[test]
7091    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
7092        let mut f = Fixture::new();
7093        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7094        // A null array and not a null bulk string, which `redis-cli` prints as
7095        // `(nil)` either way and only the raw wire tells apart. What it would
7096        // have sent is an array, so its nothing is an array's nothing.
7097        assert_eq!(
7098            f.run(&[
7099                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
7100            ]),
7101            "*-1\r\n"
7102        );
7103        assert_eq!(
7104            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7105            bulks(&["a", "b", "c"])
7106        );
7107        // COUNT takes what there is, and an emptied source goes away.
7108        assert_eq!(
7109            f.run(&[
7110                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
7111            ]),
7112            bulks(&["a", "b", "c"])
7113        );
7114        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
7115        assert_eq!(
7116            f.run(&[
7117                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7118            ]),
7119            "*-1\r\n"
7120        );
7121    }
7122
7123    #[test]
7124    fn a_block_move_onto_itself_rotates_by_the_count() {
7125        let mut f = Fixture::new();
7126        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
7127        assert_eq!(
7128            f.run(&[
7129                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
7130            ]),
7131            bulks(&["a", "b"])
7132        );
7133        assert_eq!(
7134            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7135            bulks(&["c", "a", "b"])
7136        );
7137    }
7138
7139    #[test]
7140    fn a_block_move_reads_the_count_before_the_ordering_word() {
7141        let mut f = Fixture::new();
7142        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7143        f.run(&[b"SET", b"str", b"v"]);
7144        let count = "-ERR count should be greater than 0\r\n";
7145        assert_eq!(
7146            f.run(&[
7147                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7148            ]),
7149            count
7150        );
7151        assert_eq!(
7152            f.run(&[
7153                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7154            ]),
7155            count
7156        );
7157        assert_eq!(
7158            f.run(&[
7159                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7160            ]),
7161            "-ERR syntax error\r\n"
7162        );
7163        assert_eq!(
7164            f.run(&[
7165                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7166            ]),
7167            "-ERR syntax error\r\n"
7168        );
7169        // Every argument is read before the keys are looked at, so a bad count
7170        // beats a wrong type even when the type is wrong on the source.
7171        assert_eq!(
7172            f.run(&[
7173                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7174            ]),
7175            count
7176        );
7177        assert_eq!(
7178            f.run(&[
7179                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7180            ]),
7181            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7182        );
7183        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7184    }
7185
7186    #[test]
7187    fn lmpop_answers_from_the_first_key_that_has_anything() {
7188        let mut f = Fixture::new();
7189        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7190        // The name of the key that answered comes back with the elements,
7191        // because the client cannot work out which one it was.
7192        assert_eq!(
7193            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7194            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7195        );
7196        assert_eq!(
7197            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7198            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7199        );
7200        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7201        // A null array and not a null, even though what it stands in for is an
7202        // array holding a key name and then another array.
7203        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7204    }
7205
7206    #[test]
7207    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7208        let mut f = Fixture::new();
7209        f.run(&[b"RPUSH", b"k", b"a"]);
7210        assert_eq!(
7211            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7212            "-ERR numkeys should be greater than 0\r\n"
7213        );
7214        assert_eq!(
7215            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7216            "-ERR numkeys should be greater than 0\r\n"
7217        );
7218        assert_eq!(
7219            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7220            "-ERR count should be greater than 0\r\n"
7221        );
7222        // A key count that eats the direction is a syntax error and not a
7223        // sentence about key counts, because the direction is simply not there.
7224        assert_eq!(
7225            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7226            "-ERR syntax error\r\n"
7227        );
7228        assert_eq!(
7229            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7230            "-ERR syntax error\r\n"
7231        );
7232        assert_eq!(
7233            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7234            "-ERR syntax error\r\n"
7235        );
7236        assert_eq!(
7237            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7238            "-ERR syntax error\r\n"
7239        );
7240        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7241    }
7242
7243    #[test]
7244    fn every_list_command_says_wrongtype_and_writes_nothing() {
7245        let mut f = Fixture::new();
7246        f.run(&[b"SET", b"str", b"v"]);
7247        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7248        for cmd in [
7249            &[b"LPUSH".as_slice(), b"str", b"a"][..],
7250            &[b"RPUSH", b"str", b"a"],
7251            &[b"LPUSHX", b"str", b"a"],
7252            &[b"RPUSHX", b"str", b"a"],
7253            &[b"LPOP", b"str"],
7254            &[b"LPOP", b"str", b"2"],
7255            &[b"RPOP", b"str"],
7256            &[b"LLEN", b"str"],
7257            &[b"LRANGE", b"str", b"0", b"-1"],
7258            &[b"LINDEX", b"str", b"0"],
7259            &[b"LSET", b"str", b"0", b"a"],
7260            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7261            &[b"LREM", b"str", b"0", b"a"],
7262            &[b"LTRIM", b"str", b"0", b"-1"],
7263            &[b"LPOS", b"str", b"a"],
7264            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7265            &[b"RPOPLPUSH", b"str", b"d"],
7266            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7267            &[b"LMPOP", b"1", b"str", b"LEFT"],
7268        ] {
7269            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7270        }
7271        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7272        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7273    }
7274
7275    /// A timeout is not an integer and it is not an ordinary float either: the
7276    /// three sentences it can answer with are its own, and which one a given
7277    /// argument gets is not what reading the code would suggest.
7278    #[test]
7279    fn a_timeout_has_three_ways_of_being_wrong() {
7280        let mut f = Fixture::new();
7281        let not_float = "-ERR timeout is not a float or out of range\r\n";
7282        let range = "-ERR timeout is out of range\r\n";
7283        for (bad, want) in [
7284            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7285            (&[b"BLPOP", b"k", b"nan"], not_float),
7286            (&[b"BLPOP", b"k", b""], not_float),
7287            // Whitespace on either side, which `strtold` would take and Redis
7288            // does not.
7289            (&[b"BLPOP", b"k", b" 1"], not_float),
7290            (&[b"BLPOP", b"k", b"1 "], not_float),
7291            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7292            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7293            // These three parse, so they are not the not-a-float error, and all
7294            // three are further off than an i64 of milliseconds reaches.
7295            (&[b"BLPOP", b"k", b"1e400"], range),
7296            (&[b"BLPOP", b"k", b"inf"], range),
7297            (&[b"BLPOP", b"k", b"9999999999999999"], range),
7298            (&[b"BRPOP", b"k", b"abc"], not_float),
7299            (
7300                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7301                not_float,
7302            ),
7303            (
7304                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7305                "-ERR timeout is negative\r\n",
7306            ),
7307            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7308        ] {
7309            assert_eq!(f.run(bad), want, "for {bad:?}");
7310        }
7311    }
7312
7313    /// A timeout of exactly zero means no timeout, and there are two ways of
7314    /// writing exactly zero.
7315    #[test]
7316    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7317        let mut f = Fixture::new();
7318        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7319            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7320            assert_eq!(flow, Flow::Block, "for {timeout:?}");
7321            assert!(out.is_empty(), "for {timeout:?}");
7322        }
7323        // Positive, so it is a real deadline, and the deadline is this
7324        // millisecond. Nothing is written here either: the reply comes from the
7325        // sweep, which is the engine's and not this layer's.
7326        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7327        assert_eq!(flow, Flow::Block);
7328        assert!(out.is_empty());
7329    }
7330
7331    #[test]
7332    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7333        let mut f = Fixture::new();
7334        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7335
7336        // The one difference from LPOP: the reply names the key that answered,
7337        // which is what makes BLPOP over several keys usable.
7338        assert_eq!(
7339            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7340            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7341        );
7342        assert_eq!(
7343            f.run(&[b"BRPOP", b"L", b"0"]),
7344            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7345        );
7346        assert_eq!(
7347            f.run(&[
7348                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7349            ]),
7350            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7351        );
7352        assert_eq!(
7353            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7354            "$1\r\nd\r\n"
7355        );
7356        assert_eq!(
7357            f.run(&[b"EXISTS", b"L"]),
7358            ":0\r\n",
7359            "and the key went with it"
7360        );
7361        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7362        // Onto itself, which is how a list is rotated and is a real thing to ask
7363        // a blocking move for.
7364        f.run(&[b"RPUSH", b"D", b"x"]);
7365        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7366        assert_eq!(
7367            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7368            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7369        );
7370    }
7371
7372    #[test]
7373    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7374        let mut f = Fixture::new();
7375        f.run(&[b"RPUSH", b"k", b"a"]);
7376        for (bad, want) in [
7377            (
7378                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7379                "-ERR numkeys should be greater than 0\r\n",
7380            ),
7381            (
7382                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7383                "-ERR numkeys should be greater than 0\r\n",
7384            ),
7385            // Two keys named and one given, so the word that should have been
7386            // the direction is a key and there is no direction left.
7387            (
7388                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7389                "-ERR syntax error\r\n",
7390            ),
7391            (
7392                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7393                "-ERR syntax error\r\n",
7394            ),
7395            (
7396                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7397                "-ERR syntax error\r\n",
7398            ),
7399            (
7400                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7401                "-ERR syntax error\r\n",
7402            ),
7403            // A count that is not a number at all gets the same sentence a zero
7404            // or a negative one gets, rather than the usual one about integers.
7405            (
7406                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7407                "-ERR count should be greater than 0\r\n",
7408            ),
7409            (
7410                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7411                "-ERR count should be greater than 0\r\n",
7412            ),
7413        ] {
7414            assert_eq!(f.run(bad), want, "for {bad:?}");
7415        }
7416        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7417    }
7418
7419    #[test]
7420    fn a_blocking_move_reads_its_directions_before_its_timeout() {
7421        let mut f = Fixture::new();
7422        // Both are wrong. Redis checks the directions first, so this is the
7423        // syntax error and not a complaint about the timeout.
7424        assert_eq!(
7425            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7426            "-ERR syntax error\r\n"
7427        );
7428        assert_eq!(
7429            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7430            "-ERR syntax error\r\n"
7431        );
7432    }
7433
7434    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
7435    /// wait, which is the same relationship every other command in this file has
7436    /// with the one it wraps.
7437    #[test]
7438    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7439        let mut f = Fixture::new();
7440        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7441        assert_eq!(
7442            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7443            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7444        );
7445        assert_eq!(
7446            f.run(&[
7447                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7448            ]),
7449            bulks(&["e", "d"])
7450        );
7451        assert_eq!(
7452            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7453            bulks(&["a", "e", "d"])
7454        );
7455        // `EXACTLY` with enough there does not wait either.
7456        assert_eq!(
7457            f.run(&[
7458                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7459            ]),
7460            bulks(&["b", "c"])
7461        );
7462        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7463    }
7464
7465    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
7466    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
7467    /// whole block has arrived.
7468    #[test]
7469    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7470        let mut f = Fixture::new();
7471        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7472        // Two there and three asked for. `COUNT` takes the two.
7473        assert_eq!(
7474            f.flow(&[
7475                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7476            ]),
7477            (Flow::Continue, bulks(&["a", "b"]))
7478        );
7479
7480        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7481        // The same line with `EXACTLY` parks instead, and takes nothing on the
7482        // way past.
7483        assert_eq!(
7484            f.flow(&[
7485                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7486            ])
7487            .0,
7488            Flow::Block
7489        );
7490        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7491    }
7492
7493    #[test]
7494    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7495        let mut f = Fixture::new();
7496        let syntax = "-ERR syntax error\r\n";
7497        // All three are wrong and the directions are read first.
7498        assert_eq!(
7499            f.run(&[
7500                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7501            ]),
7502            syntax
7503        );
7504        // Directions fine, timeout and count both wrong, so the timeout wins.
7505        assert_eq!(
7506            f.run(&[
7507                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7508            ]),
7509            "-ERR timeout is not a float or out of range\r\n"
7510        );
7511        assert_eq!(
7512            f.run(&[
7513                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7514            ]),
7515            "-ERR timeout is negative\r\n"
7516        );
7517        // And with the timeout fine, the count before the ordering word.
7518        assert_eq!(
7519            f.run(&[
7520                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7521            ]),
7522            "-ERR count should be greater than 0\r\n"
7523        );
7524        assert_eq!(
7525            f.run(&[
7526                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7527            ]),
7528            syntax
7529        );
7530        // Seven and eight arguments are neither of the two forms, the same way
7531        // six and seven are for `LMOVEM`.
7532        assert_eq!(
7533            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7534            syntax
7535        );
7536        assert_eq!(
7537            f.run(&[
7538                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7539            ]),
7540            syntax
7541        );
7542    }
7543
7544    /// The four ways a blocking command sees a key of another type, and the one
7545    /// way it does not.
7546    #[test]
7547    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7548        let mut f = Fixture::new();
7549        f.run(&[b"SET", b"S", b"v"]);
7550        f.run(&[b"RPUSH", b"D", b"x"]);
7551        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7552
7553        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7554        // Every key is checked even when an earlier one would have blocked, so
7555        // an empty key in front of a string does not hide it.
7556        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7557        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7558        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7559        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7560        // The destination, which is only reached because the source has
7561        // something in it.
7562        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7563        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7564        assert_eq!(
7565            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7566            wrong
7567        );
7568        assert_eq!(
7569            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7570            wrong
7571        );
7572
7573        // And the one that does not: an empty source means the destination is
7574        // never looked at, so this waits rather than erroring, and on a real
7575        // server it times out.
7576        assert_eq!(
7577            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7578                .0,
7579            Flow::Block
7580        );
7581        // `BLMOVEM` has a second way of not being ready, and it hides the
7582        // destination just as well: the source is a list with two elements in it
7583        // and `EXACTLY` wants three, so the string never gets looked at.
7584        assert_eq!(
7585            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7586                .0,
7587            Flow::Block
7588        );
7589        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7590        assert_eq!(
7591            f.flow(&[
7592                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7593            ])
7594            .0,
7595            Flow::Block
7596        );
7597    }
7598
7599    /// The same churn the set and the string get, because a list that leaks a
7600    /// chunk per push looks exactly like one that does not until it has run for
7601    /// an afternoon.
7602    #[test]
7603    fn churning_lists_does_not_grow_the_server() {
7604        let mut f = Fixture::new();
7605        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
7606        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
7607            .into_iter()
7608            .chain(vals.iter().map(Vec::as_slice))
7609            .collect();
7610
7611        f.run(&args);
7612        f.run(&[b"DEL", b"k"]);
7613        f.server.compact_step();
7614        let after_first = f.server.memory_bytes();
7615
7616        for _ in 0..200 {
7617            f.run(&args);
7618            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
7619            f.server.compact_step();
7620        }
7621        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7622        assert!(
7623            f.server.memory_bytes() <= after_first * 2,
7624            "held {} after two hundred passes against {after_first} after one",
7625            f.server.memory_bytes()
7626        );
7627    }
7628
7629    // ------------------------------------------------------------ sorted set
7630
7631    #[test]
7632    fn a_sorted_set_takes_scores_and_gives_them_back() {
7633        let mut f = Fixture::new();
7634        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
7635        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
7636        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
7637        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
7638        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
7639        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
7640        assert_eq!(
7641            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
7642            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
7643        );
7644        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
7645        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
7646        // The key goes when the last member does.
7647        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
7648        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7649    }
7650
7651    #[test]
7652    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
7653        let mut f = Fixture::new();
7654        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
7655        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
7656        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
7657        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
7658
7659        f.out = Out::new(Proto::Resp3);
7660        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
7661        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
7662        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
7663        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
7664    }
7665
7666    #[test]
7667    fn the_zadd_options_gate_what_gets_written() {
7668        let mut f = Fixture::new();
7669        f.run(&[b"ZADD", b"z", b"5", b"a"]);
7670        // NX leaves a member that is there alone, XX will not create one.
7671        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
7672        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
7673        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
7674        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
7675        // GT and LT only move a score one way.
7676        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
7677        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
7678        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
7679        // CH counts a moved score and plain ZADD does not.
7680        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
7681        assert_eq!(
7682            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
7683            ":2\r\n"
7684        );
7685    }
7686
7687    #[test]
7688    fn zadd_incr_answers_a_score_or_nothing_at_all() {
7689        let mut f = Fixture::new();
7690        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
7691        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
7692        // A gate that refuses is the string nil, because the reply it stands in
7693        // for is a score.
7694        assert_eq!(
7695            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
7696            "$-1\r\n"
7697        );
7698        assert_eq!(
7699            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
7700            "$-1\r\n"
7701        );
7702        assert_eq!(
7703            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
7704            "$-1\r\n"
7705        );
7706        assert_eq!(
7707            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
7708            "$1\r\n8\r\n"
7709        );
7710        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
7711        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
7712    }
7713
7714    #[test]
7715    fn the_two_infinities_will_not_be_added_together() {
7716        let mut f = Fixture::new();
7717        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
7718        let nan = "-ERR resulting score is not a number (NaN)\r\n";
7719        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
7720        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
7721        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
7722        // And a key made for an increment that then fails does not stay behind.
7723        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
7724    }
7725
7726    #[test]
7727    fn zadd_says_its_mistakes_the_way_redis_says_them() {
7728        let mut f = Fixture::new();
7729        // The pairs are counted before the options are looked at, so this is a
7730        // syntax error about having none and not a complaint about NX and XX.
7731        assert_eq!(
7732            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
7733            "-ERR syntax error\r\n"
7734        );
7735        assert_eq!(
7736            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
7737            "-ERR XX and NX options at the same time are not compatible\r\n"
7738        );
7739        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
7740        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
7741        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
7742        assert_eq!(
7743            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
7744            "-ERR INCR option supports a single increment-element pair\r\n"
7745        );
7746        // An odd number of arguments after the options.
7747        assert_eq!(
7748            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
7749            "-ERR syntax error\r\n"
7750        );
7751        // Every score is read before the first is stored.
7752        assert_eq!(
7753            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
7754            "-ERR value is not a valid float\r\n"
7755        );
7756        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7757    }
7758
7759    #[test]
7760    fn a_rank_says_where_a_member_sits_from_either_end() {
7761        let mut f = Fixture::new();
7762        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7763        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
7764        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
7765        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
7766        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
7767        // WITHSCORE changes both shapes: the answer and the nothing.
7768        assert_eq!(
7769            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
7770            "*2\r\n:1\r\n$1\r\n2\r\n"
7771        );
7772        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
7773        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
7774        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
7775        // A bad option is a syntax error and one argument too many is an arity
7776        // error, which is Redis's split.
7777        assert_eq!(
7778            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
7779            "-ERR syntax error\r\n"
7780        );
7781        assert_eq!(
7782            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
7783            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
7784        );
7785    }
7786
7787    #[test]
7788    fn the_two_counts_read_their_two_kinds_of_bound() {
7789        let mut f = Fixture::new();
7790        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7791        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
7792        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
7793        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
7794        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
7795        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
7796        assert_eq!(
7797            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
7798            "-ERR min or max is not a float\r\n"
7799        );
7800
7801        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
7802        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
7803        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
7804        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
7805        // A bare member is not a bound, because a member can start with any
7806        // byte and there would be no way to say the bracket if it were optional.
7807        assert_eq!(
7808            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
7809            "-ERR min or max not valid string range item\r\n"
7810        );
7811    }
7812
7813    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
7814    ///
7815    /// Every byte in here was read off a real 8.10.1 rather than worked out,
7816    /// because the interesting part of this command is not what it selects, it
7817    /// is which of the two ends the client is expected to name first.
7818    #[test]
7819    fn one_range_command_selects_by_rank_or_score_or_name() {
7820        let mut f = Fixture::new();
7821        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7822        assert_eq!(
7823            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7824            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7825        );
7826        assert_eq!(
7827            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
7828            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7829        );
7830        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
7831        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
7832        // REV over ranks reverses the walk and leaves the two arguments alone,
7833        // because a rank counts from the end the walk starts at.
7834        assert_eq!(
7835            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
7836            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7837        );
7838        assert_eq!(
7839            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
7840            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7841        );
7842        // And REV over scores does swap them, since a bound does not count from
7843        // anywhere. This is the one line of the parse that tells the two apart.
7844        assert_eq!(
7845            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
7846            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7847        );
7848        assert_eq!(
7849            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
7850            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7851        );
7852        assert_eq!(
7853            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
7854            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7855        );
7856    }
7857
7858    /// The older spellings, which are the same six windows with the mode in the
7859    /// name and the high end named first on the three that go backwards.
7860    #[test]
7861    fn the_older_range_spellings_name_their_high_end_first() {
7862        let mut f = Fixture::new();
7863        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7864        assert_eq!(
7865            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
7866            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7867        );
7868        assert_eq!(
7869            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
7870            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7871        );
7872        assert_eq!(
7873            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
7874            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7875        );
7876        assert_eq!(
7877            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
7878            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7879        );
7880        // The two arguments the wrong way round is an empty answer and not an
7881        // error, which is what the swap being in the parse rather than in the
7882        // window buys.
7883        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
7884        assert_eq!(
7885            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
7886            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7887        );
7888        assert_eq!(
7889            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
7890            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
7891        );
7892        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
7893        // way of spelling the mode, they are a syntax error.
7894        for cmd in [
7895            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
7896            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
7897            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
7898        ] {
7899            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
7900        }
7901    }
7902
7903    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
7904    /// only some of them accept.
7905    #[test]
7906    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
7907        let mut f = Fixture::new();
7908        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7909        assert_eq!(
7910            f.run(&[
7911                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
7912            ]),
7913            "*1\r\n$1\r\nb\r\n"
7914        );
7915        // A negative offset skips past everything, a negative count is no bound.
7916        assert_eq!(
7917            f.run(&[
7918                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
7919            ]),
7920            "*0\r\n"
7921        );
7922        assert_eq!(
7923            f.run(&[
7924                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
7925            ]),
7926            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7927        );
7928        // The two options in either order, which falls out of the parse loop.
7929        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";
7930        assert_eq!(
7931            f.run(&[
7932                b"ZRANGEBYSCORE",
7933                b"z",
7934                b"1",
7935                b"3",
7936                b"WITHSCORES",
7937                b"LIMIT",
7938                b"0",
7939                b"2"
7940            ]),
7941            both
7942        );
7943        assert_eq!(
7944            f.run(&[
7945                b"ZRANGEBYSCORE",
7946                b"z",
7947                b"1",
7948                b"3",
7949                b"LIMIT",
7950                b"0",
7951                b"2",
7952                b"WITHSCORES"
7953            ]),
7954            both
7955        );
7956        // LIMIT on a range by rank is refused after the whole option list has
7957        // been read, so this complains about LIMIT and not about WITHSCORES.
7958        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
7959        assert_eq!(
7960            f.run(&[
7961                b"ZREVRANGE",
7962                b"z",
7963                b"0",
7964                b"-1",
7965                b"WITHSCORES",
7966                b"LIMIT",
7967                b"0",
7968                b"1"
7969            ]),
7970            needs_by
7971        );
7972        assert_eq!(
7973            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
7974            needs_by
7975        );
7976        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
7977        assert_eq!(
7978            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
7979            not_bylex
7980        );
7981        assert_eq!(
7982            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
7983            not_bylex
7984        );
7985        // Two modes at once, an option nobody knows, a LIMIT missing its count,
7986        // and the three number errors, which are three different sentences.
7987        for cmd in [
7988            &[
7989                b"ZRANGE".as_slice(),
7990                b"z",
7991                b"0",
7992                b"-1",
7993                b"BYSCORE",
7994                b"BYLEX",
7995            ][..],
7996            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
7997            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
7998        ] {
7999            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8000        }
8001        assert_eq!(
8002            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
8003            "-ERR min or max is not a float\r\n"
8004        );
8005        assert_eq!(
8006            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
8007            "-ERR min or max not valid string range item\r\n"
8008        );
8009        assert_eq!(
8010            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
8011            "-ERR value is not an integer or out of range\r\n"
8012        );
8013    }
8014
8015    /// `WITHSCORES` is the one place in this group where the two protocols
8016    /// disagree about the shape of the reply and not just the type of a value.
8017    #[test]
8018    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
8019        let mut f = Fixture::new();
8020        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8021        assert_eq!(
8022            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8023            "*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"
8024        );
8025        f.out = Out::new(Proto::Resp3);
8026        assert_eq!(
8027            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8028            "*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"
8029        );
8030        assert_eq!(
8031            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8032            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
8033        );
8034    }
8035
8036    /// The store form, which is the same parse with the destination in front.
8037    #[test]
8038    fn a_range_store_writes_the_window_into_another_key() {
8039        let mut f = Fixture::new();
8040        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8041        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
8042        // A window that selects nothing deletes the destination rather than
8043        // leaving an empty sorted set, because an empty one does not exist.
8044        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
8045        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8046        assert_eq!(
8047            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
8048            ":2\r\n"
8049        );
8050        assert_eq!(
8051            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8052            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8053        );
8054        // The destination is allowed to be the source, because the result is
8055        // built whole before anything is written over.
8056        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
8057        assert_eq!(
8058            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
8059            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8060        );
8061        // It takes every option ZRANGE takes except WITHSCORES, which is a
8062        // plain syntax error here and not the sentence about BYLEX.
8063        assert_eq!(
8064            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
8065            "-ERR syntax error\r\n"
8066        );
8067    }
8068
8069    /// The three removals, which are the read side's window with the walk
8070    /// turned into a removal and no options at all.
8071    #[test]
8072    fn the_three_removals_share_their_window_with_the_reads() {
8073        let mut f = Fixture::new();
8074        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8075        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
8076        assert_eq!(
8077            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
8078            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
8079        );
8080        assert_eq!(
8081            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
8082            ":1\r\n"
8083        );
8084        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
8085        // The last member going takes the key with it.
8086        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
8087        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8088        assert_eq!(
8089            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
8090            ":0\r\n"
8091        );
8092        assert_eq!(
8093            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
8094            "-ERR value is not an integer or out of range\r\n"
8095        );
8096    }
8097
8098    /// The algebra, which is one gather and three names for it.
8099    #[test]
8100    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
8101        let mut f = Fixture::new();
8102        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8103        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8104        assert_eq!(
8105            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
8106            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
8107        );
8108        // The scores are added where a member is in both, and the answer comes
8109        // out in the order those combined scores put it in.
8110        assert_eq!(
8111            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
8112            "*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"
8113        );
8114        assert_eq!(
8115            f.run(&[
8116                b"ZUNION",
8117                b"2",
8118                b"z",
8119                b"y",
8120                b"WEIGHTS",
8121                b"2",
8122                b"3",
8123                b"WITHSCORES"
8124            ]),
8125            "*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"
8126        );
8127        assert_eq!(
8128            f.run(&[
8129                b"ZUNION",
8130                b"2",
8131                b"z",
8132                b"y",
8133                b"AGGREGATE",
8134                b"MIN",
8135                b"WITHSCORES"
8136            ]),
8137            "*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"
8138        );
8139        assert_eq!(
8140            f.run(&[
8141                b"ZUNION",
8142                b"2",
8143                b"z",
8144                b"y",
8145                b"AGGREGATE",
8146                b"MAX",
8147                b"WITHSCORES"
8148            ]),
8149            "*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"
8150        );
8151        assert_eq!(
8152            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8153            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8154        );
8155        assert_eq!(
8156            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8157            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8158        );
8159        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8160        // A plain set is an input, and it behaves as a sorted set in which
8161        // every member scores one.
8162        f.run(&[b"SADD", b"p", b"a", b"d"]);
8163        assert_eq!(
8164            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8165            "*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"
8166        );
8167        // A difference never combines two scores, so it has nothing for either
8168        // of the two options to do and refuses both.
8169        for cmd in [
8170            &[
8171                b"ZDIFF".as_slice(),
8172                b"2",
8173                b"z",
8174                b"y",
8175                b"WEIGHTS",
8176                b"1",
8177                b"1",
8178            ][..],
8179            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8180        ] {
8181            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8182        }
8183    }
8184
8185    /// The count of keys, which is what lets a key be named `WEIGHTS`.
8186    #[test]
8187    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8188        let mut f = Fixture::new();
8189        f.run(&[b"ZADD", b"z", b"1", b"a"]);
8190        f.run(&[b"ZADD", b"y", b"2", b"b"]);
8191        // Redis names the command in this one, so each spelling says its own.
8192        assert_eq!(
8193            f.run(&[b"ZUNION", b"0", b"z"]),
8194            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8195        );
8196        assert_eq!(
8197            f.run(&[b"ZUNION", b"-1", b"z"]),
8198            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8199        );
8200        assert_eq!(
8201            f.run(&[b"ZINTERCARD", b"0", b"z"]),
8202            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8203        );
8204        // A count bigger than the line is a plain syntax error, which reads
8205        // oddly and is what Redis says.
8206        assert_eq!(
8207            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8208            "-ERR syntax error\r\n"
8209        );
8210        assert_eq!(
8211            f.run(&[b"ZUNION", b"x", b"z"]),
8212            "-ERR value is not an integer or out of range\r\n"
8213        );
8214        // A WEIGHTS list that is not one per key is a syntax error, and a
8215        // weight that is not a number gets a sentence of its own.
8216        assert_eq!(
8217            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8218            "-ERR syntax error\r\n"
8219        );
8220        assert_eq!(
8221            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8222            "-ERR weight value is not a float\r\n"
8223        );
8224        assert_eq!(
8225            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8226            "-ERR syntax error\r\n"
8227        );
8228    }
8229
8230    /// The three store forms, which answer a count and take no WITHSCORES.
8231    #[test]
8232    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
8233        let mut f = Fixture::new();
8234        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8235        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
8236        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8237        assert_eq!(
8238            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8239            "*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"
8240        );
8241        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8242        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8243        // An empty result deletes the destination rather than leaving an empty
8244        // sorted set, because an empty one does not exist.
8245        assert_eq!(
8246            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8247            ":0\r\n"
8248        );
8249        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8250        // The destination is allowed to name its own source.
8251        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8252        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8253        for cmd in [
8254            &[
8255                b"ZUNIONSTORE".as_slice(),
8256                b"d",
8257                b"2",
8258                b"z",
8259                b"y",
8260                b"WITHSCORES",
8261            ][..],
8262            &[
8263                b"ZDIFFSTORE",
8264                b"d",
8265                b"2",
8266                b"z",
8267                b"y",
8268                b"WEIGHTS",
8269                b"1",
8270                b"1",
8271            ],
8272        ] {
8273            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8274        }
8275    }
8276
8277    /// `ZINTERCARD`, which counts without building anything.
8278    #[test]
8279    fn intercard_counts_and_stops_at_its_limit() {
8280        let mut f = Fixture::new();
8281        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8282        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8283        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8284        // A limit of zero is no limit, which is Redis's reading of it.
8285        assert_eq!(
8286            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8287            ":2\r\n"
8288        );
8289        assert_eq!(
8290            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8291            ":1\r\n"
8292        );
8293        // A negative limit and a limit that is not a number at all get the same
8294        // sentence, which looks like a mistake in Redis and is copied as one.
8295        let bad = "-ERR LIMIT can't be negative\r\n";
8296        assert_eq!(
8297            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8298            bad
8299        );
8300        assert_eq!(
8301            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8302            bad
8303        );
8304        for cmd in [
8305            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8306            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8307            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8308        ] {
8309            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8310        }
8311    }
8312
8313    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
8314    #[test]
8315    fn a_draw_answers_one_member_or_an_array_of_them() {
8316        let mut f = Fixture::new();
8317        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8318        // No count is one member or a nil, a count is an array that may be
8319        // empty, and those are two reply types the client has to tell apart.
8320        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8321        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8322        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8323        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8324        // A positive count draws without replacement, so a count over the size
8325        // answers the whole set and never a member twice.
8326        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8327        assert!(all.starts_with("*3\r\n"), "{all}");
8328        for m in ["a", "b", "c"] {
8329            assert!(all.contains(m), "{all}");
8330        }
8331        // A negative one draws with replacement and answers exactly as many as
8332        // it was asked for, whatever the size of the set.
8333        assert!(
8334            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8335            "five draws with replacement"
8336        );
8337        assert!(
8338            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8339                .starts_with("*4\r\n"),
8340            "two pairs, flat on RESP2"
8341        );
8342        f.out = Out::new(Proto::Resp3);
8343        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8344        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8345        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8346        f.out = Out::new(Proto::Resp2);
8347        assert_eq!(
8348            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8349            "-ERR syntax error\r\n"
8350        );
8351        assert_eq!(
8352            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8353            "-ERR value is not an integer or out of range\r\n"
8354        );
8355    }
8356
8357    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
8358    #[test]
8359    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8360        let mut f = Fixture::new();
8361        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8362        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";
8363        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8364        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8365        assert_eq!(
8366            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8367            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8368        );
8369        assert_eq!(
8370            f.run(&[b"ZSCAN", b"nokey", b"0"]),
8371            "*2\r\n$1\r\n0\r\n*0\r\n"
8372        );
8373        // A score stays a bulk string on RESP3, which is the one place the two
8374        // protocols agree about a score and everywhere else they do not.
8375        f.out = Out::new(Proto::Resp3);
8376        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8377        f.out = Out::new(Proto::Resp2);
8378        assert_eq!(
8379            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8380            "-ERR NOVALUES option can only be used in HSCAN\r\n"
8381        );
8382        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8383        assert_eq!(
8384            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8385            "-ERR syntax error\r\n"
8386        );
8387    }
8388
8389    /// The count is what decides the shape, and its value is not.
8390    #[test]
8391    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8392        let mut f = Fixture::new();
8393        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8394        // No count, so one flat pair, and the score is a bulk string on RESP2.
8395        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8396        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8397        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8398        // A count, so pairs, and on RESP2 they are flattened into one run.
8399        assert_eq!(
8400            f.run(&[b"ZPOPMIN", b"z", b"2"]),
8401            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8402        );
8403        // An empty array rather than a null, which is where a sorted set pop and
8404        // a list pop part company, and the same answer a count of zero gives.
8405        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8406        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8407        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8408        // The last member takes the key with it.
8409        assert_eq!(
8410            f.run(&[b"ZPOPMIN", b"z", b"9"]),
8411            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8412        );
8413        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8414
8415        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8416        f.out = Out::new(Proto::Resp3);
8417        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8418        assert_eq!(
8419            f.run(&[b"ZPOPMIN", b"z", b"1"]),
8420            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8421        );
8422        f.out = Out::new(Proto::Resp2);
8423        // Both of these are the range error rather than the usual sentence about
8424        // integers, which is the odd answer and so the one worth copying.
8425        let bad = "-ERR value is out of range, must be positive\r\n";
8426        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8427        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8428        assert_eq!(
8429            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8430            "-ERR syntax error\r\n"
8431        );
8432    }
8433
8434    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
8435    #[test]
8436    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8437        let mut f = Fixture::new();
8438        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8439        assert_eq!(
8440            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8441            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8442        );
8443        // Nested on RESP2 as well, because the key name is already in front of
8444        // the pairs and there is nothing left to flatten into.
8445        assert_eq!(
8446            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8447            "*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"
8448        );
8449        // A null array and not a null, the same as LMPOP.
8450        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8451        f.out = Out::new(Proto::Resp3);
8452        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8453        f.out = Out::new(Proto::Resp2);
8454        let numkeys = "-ERR numkeys should be greater than 0\r\n";
8455        for bad in [
8456            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8457            &[b"ZMPOP", b"-1", b"z", b"MIN"],
8458            &[b"ZMPOP", b"x", b"z", b"MIN"],
8459        ] {
8460            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8461        }
8462        let count = "-ERR count should be greater than 0\r\n";
8463        for bad in [
8464            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8465            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8466            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8467        ] {
8468            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8469        }
8470        let syntax = "-ERR syntax error\r\n";
8471        for bad in [
8472            // Two keys named and one given, so the word that should have been
8473            // the direction is a key and there is no direction left.
8474            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8475            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8476            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8477            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8478        ] {
8479            assert_eq!(f.run(bad), syntax, "{bad:?}");
8480        }
8481    }
8482
8483    /// The three that wait, when there is something there and they do not have
8484    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
8485    #[test]
8486    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8487        let mut f = Fixture::new();
8488        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8489        assert_eq!(
8490            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8491            (
8492                Flow::Continue,
8493                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8494            )
8495        );
8496        assert_eq!(
8497            f.run(&[b"BZPOPMAX", b"z", b"0"]),
8498            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8499        );
8500        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8501        assert_eq!(
8502            f.run(&[
8503                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8504            ]),
8505            "*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"
8506        );
8507        f.out = Out::new(Proto::Resp3);
8508        assert_eq!(
8509            f.run(&[b"BZPOPMIN", b"z", b"0"]),
8510            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8511        );
8512        f.out = Out::new(Proto::Resp2);
8513        // Nothing to take, so the client is parked and nothing was written.
8514        assert_eq!(
8515            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8516            (Flow::Block, String::new())
8517        );
8518        assert_eq!(
8519            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8520            (Flow::Block, String::new())
8521        );
8522        // The timeout is read before the key count, so this complains about the
8523        // timeout and not about the count.
8524        assert_eq!(
8525            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8526            "-ERR timeout is not a float or out of range\r\n"
8527        );
8528        assert_eq!(
8529            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8530            "-ERR numkeys should be greater than 0\r\n"
8531        );
8532        assert_eq!(
8533            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8534            "-ERR timeout is negative\r\n"
8535        );
8536    }
8537
8538    /// A parked sorted set client is served by whatever puts a member under one
8539    /// of its keys, and is not served by something of another type landing
8540    /// there.
8541    #[test]
8542    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8543        let mut f = Fixture::new();
8544        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8545        assert_eq!(f.server.waiters().len(), 1);
8546        // A string under the key is not what it asked for, so it stays parked
8547        // rather than being handed a WRONGTYPE on a command that was accepted.
8548        f.run(&[b"SET", b"z", b"v"]);
8549        let mut out = Out::new(Proto::Resp2);
8550        assert!(!f.server.serve_waiter(0, 0, &mut out));
8551        assert!(out.as_slice().is_empty());
8552        f.run(&[b"DEL", b"z"]);
8553        f.run(&[b"ZADD", b"z", b"5", b"m"]);
8554        assert!(f.server.serve_waiter(0, 0, &mut out));
8555        assert_eq!(
8556            core::str::from_utf8(out.as_slice()).expect("ascii"),
8557            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8558        );
8559        // And the member is gone, which is what makes a queue of workers on a
8560        // sorted set work at all.
8561        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8562    }
8563
8564    #[test]
8565    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8566        let mut f = Fixture::new();
8567        f.run(&[b"SET", b"s", b"v"]);
8568        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8569        for cmd in [
8570            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8571            &[b"ZINCRBY", b"s", b"1", b"a"],
8572            &[b"ZCARD", b"s"],
8573            &[b"ZSCORE", b"s", b"a"],
8574            &[b"ZMSCORE", b"s", b"a"],
8575            &[b"ZREM", b"s", b"a"],
8576            &[b"ZRANK", b"s", b"a"],
8577            &[b"ZREVRANK", b"s", b"a"],
8578            &[b"ZCOUNT", b"s", b"1", b"2"],
8579            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8580            &[b"ZRANGE", b"s", b"0", b"-1"],
8581            &[b"ZREVRANGE", b"s", b"0", b"-1"],
8582            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8583            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8584            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8585            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8586            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8587            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8588            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8589            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8590            &[b"ZUNION", b"1", b"s"],
8591            &[b"ZINTER", b"1", b"s"],
8592            &[b"ZDIFF", b"1", b"s"],
8593            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8594            &[b"ZINTERSTORE", b"d", b"1", b"s"],
8595            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8596            &[b"ZINTERCARD", b"1", b"s"],
8597            &[b"ZRANDMEMBER", b"s"],
8598            &[b"ZSCAN", b"s", b"0"],
8599            &[b"ZPOPMIN", b"s"],
8600            &[b"ZPOPMAX", b"s", b"2"],
8601            &[b"ZMPOP", b"1", b"s", b"MIN"],
8602            &[b"BZPOPMIN", b"s", b"0"],
8603            &[b"BZPOPMAX", b"s", b"0"],
8604            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
8605        ] {
8606            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
8607        }
8608        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
8609    }
8610
8611    /// The same churn the set, the string and the list get, because a sorted
8612    /// set that leaks a tree node per add looks exactly like one that does not
8613    /// until it has run for an afternoon.
8614    #[test]
8615    fn churning_sorted_sets_does_not_grow_the_server() {
8616        let mut f = Fixture::new();
8617        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
8618        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
8619        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
8620        for i in 0..200 {
8621            args.push(&scores[i]);
8622            args.push(&members[i]);
8623        }
8624
8625        f.run(&args);
8626        f.run(&[b"DEL", b"z"]);
8627        f.server.compact_step();
8628        let after_first = f.server.memory_bytes();
8629
8630        for _ in 0..200 {
8631            f.run(&args);
8632            f.run(&[b"DEL", b"z"]);
8633            f.server.compact_step();
8634        }
8635        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8636        assert!(
8637            f.server.memory_bytes() <= after_first * 2,
8638            "held {} after two hundred passes against {after_first} after one",
8639            f.server.memory_bytes()
8640        );
8641    }
8642
8643    // ------------------------------------------------------------------- geo
8644
8645    /// The three places every Redis geo example uses, and one more.
8646    ///
8647    /// Every reply this section asserts on came off a running 8.10.1 with these
8648    /// three loaded, byte for byte, including the number of digits in a
8649    /// coordinate and the four places on a distance.
8650    fn sicily(f: &mut Fixture) {
8651        f.run(&[
8652            b"GEOADD",
8653            b"Sicily",
8654            b"13.361389",
8655            b"38.115556",
8656            b"Palermo",
8657            b"15.087269",
8658            b"37.502669",
8659            b"Catania",
8660        ]);
8661        f.run(&[
8662            b"GEOADD",
8663            b"Sicily",
8664            b"13.583333",
8665            b"37.316667",
8666            b"Agrigento",
8667        ]);
8668    }
8669
8670    #[test]
8671    fn places_go_in_as_scores_and_come_back_as_positions() {
8672        let mut f = Fixture::new();
8673        assert_eq!(
8674            f.run(&[
8675                b"GEOADD",
8676                b"Sicily",
8677                b"13.361389",
8678                b"38.115556",
8679                b"Palermo",
8680                b"15.087269",
8681                b"37.502669",
8682                b"Catania"
8683            ]),
8684            ":2\r\n"
8685        );
8686        // A geo key is a sorted set and says so, which is not an implementation
8687        // detail either: a client removes a place with ZREM and counts them
8688        // with ZCARD, and the score is the number a real server stores.
8689        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
8690        assert_eq!(
8691            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
8692            "$16\r\n3479099956230698\r\n"
8693        );
8694        assert_eq!(
8695            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
8696            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
8697        );
8698        assert_eq!(
8699            f.run(&[
8700                b"GEOHASH",
8701                b"Sicily",
8702                b"Palermo",
8703                b"Catania",
8704                b"NonExisting"
8705            ]),
8706            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
8707        );
8708        // A key that is not there is an empty one, and the two nulls are not
8709        // the same null: GEOPOS answers the array one and GEOHASH the string
8710        // one, which a RESP2 client can tell apart.
8711        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
8712        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
8713    }
8714
8715    #[test]
8716    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
8717        let mut f = Fixture::new();
8718        sicily(&mut f);
8719        assert_eq!(
8720            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
8721            "$11\r\n166274.1516\r\n"
8722        );
8723        assert_eq!(
8724            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
8725            "$8\r\n166.2742\r\n"
8726        );
8727        assert_eq!(
8728            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
8729            "$8\r\n103.3182\r\n"
8730        );
8731        // A member that is not there and a key that is not there are the same
8732        // nil, and the unit is read before the key is looked up, so a bad unit
8733        // on a missing key is still an error.
8734        assert_eq!(
8735            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
8736            "$-1\r\n"
8737        );
8738        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
8739        assert_eq!(
8740            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
8741            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
8742        );
8743        assert_eq!(
8744            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
8745            "-ERR syntax error\r\n"
8746        );
8747    }
8748
8749    #[test]
8750    fn a_search_finds_what_is_inside_it_nearest_first() {
8751        let mut f = Fixture::new();
8752        sicily(&mut f);
8753        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
8754        assert_eq!(
8755            f.run(&[
8756                b"GEOSEARCH",
8757                b"Sicily",
8758                b"FROMLONLAT",
8759                b"15",
8760                b"37",
8761                b"BYRADIUS",
8762                b"200",
8763                b"km",
8764                b"ASC"
8765            ]),
8766            all
8767        );
8768        // The older spelling of the same search, which is the same nine boxes
8769        // and the same order.
8770        assert_eq!(
8771            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
8772            all
8773        );
8774        assert_eq!(
8775            f.run(&[
8776                b"GEORADIUS_RO",
8777                b"Sicily",
8778                b"15",
8779                b"37",
8780                b"200",
8781                b"km",
8782                b"ASC"
8783            ]),
8784            all
8785        );
8786        // A count with no ordering means the nearest ones, so DESC has to be
8787        // asked for to get the far end.
8788        assert_eq!(
8789            f.run(&[
8790                b"GEORADIUS",
8791                b"Sicily",
8792                b"15",
8793                b"37",
8794                b"200",
8795                b"km",
8796                b"DESC",
8797                b"COUNT",
8798                b"1"
8799            ]),
8800            "*1\r\n$7\r\nPalermo\r\n"
8801        );
8802        assert_eq!(
8803            f.run(&[
8804                b"GEORADIUS",
8805                b"Sicily",
8806                b"15",
8807                b"37",
8808                b"200",
8809                b"km",
8810                b"COUNT",
8811                b"1"
8812            ]),
8813            "*1\r\n$7\r\nCatania\r\n"
8814        );
8815        // Nothing inside a kilometre of that point, and nothing in a key that
8816        // is not there, and both are the empty array rather than an error.
8817        let empty = "*0\r\n";
8818        assert_eq!(
8819            f.run(&[
8820                b"GEOSEARCH",
8821                b"Sicily",
8822                b"FROMLONLAT",
8823                b"15",
8824                b"37",
8825                b"BYRADIUS",
8826                b"1",
8827                b"km"
8828            ]),
8829            empty
8830        );
8831        assert_eq!(
8832            f.run(&[
8833                b"GEOSEARCH",
8834                b"nokey",
8835                b"FROMLONLAT",
8836                b"15",
8837                b"37",
8838                b"BYRADIUS",
8839                b"1",
8840                b"km"
8841            ]),
8842            empty
8843        );
8844        assert_eq!(
8845            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
8846            empty
8847        );
8848    }
8849
8850    #[test]
8851    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
8852        let mut f = Fixture::new();
8853        sicily(&mut f);
8854        assert_eq!(
8855            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
8856            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8857        );
8858        // The member itself is nothing away from itself, which is where the
8859        // fixed point writer's zero shows up on the wire.
8860        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";
8861        assert_eq!(
8862            f.run(&[
8863                b"GEORADIUSBYMEMBER_RO",
8864                b"Sicily",
8865                b"Agrigento",
8866                b"100",
8867                b"km",
8868                b"WITHDIST"
8869            ]),
8870            with_dist
8871        );
8872        assert_eq!(
8873            f.run(&[
8874                b"GEOSEARCH",
8875                b"Sicily",
8876                b"FROMMEMBER",
8877                b"Agrigento",
8878                b"BYRADIUS",
8879                b"100",
8880                b"km",
8881                b"ASC",
8882                b"WITHDIST"
8883            ]),
8884            with_dist
8885        );
8886        assert_eq!(
8887            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
8888            "-ERR could not decode requested zset member\r\n"
8889        );
8890    }
8891
8892    #[test]
8893    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
8894        let mut f = Fixture::new();
8895        sicily(&mut f);
8896        // Three options asked for, so each result is a four element array of
8897        // the member, the distance, the hash and a pair. The order of the three
8898        // is Redis's and not the order they were written in the command.
8899        assert_eq!(
8900            f.run(&[
8901                b"GEOSEARCH",
8902                b"Sicily",
8903                b"FROMLONLAT",
8904                b"15",
8905                b"37",
8906                b"BYBOX",
8907                b"400",
8908                b"400",
8909                b"km",
8910                b"ASC",
8911                b"WITHCOORD",
8912                b"WITHDIST",
8913                b"WITHHASH"
8914            ]),
8915            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
8916             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
8917             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
8918             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
8919             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
8920             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
8921        );
8922    }
8923
8924    #[test]
8925    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
8926        let mut f = Fixture::new();
8927        sicily(&mut f);
8928        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
8929                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
8930                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
8931        assert_eq!(
8932            f.run(&[
8933                b"GEOSEARCHSTORE",
8934                b"dst",
8935                b"Sicily",
8936                b"FROMLONLAT",
8937                b"15",
8938                b"37",
8939                b"BYRADIUS",
8940                b"200",
8941                b"km",
8942                b"ASC"
8943            ]),
8944            ":3\r\n"
8945        );
8946        assert_eq!(
8947            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
8948            hashes
8949        );
8950        // The same again through the older spelling, which stores the same
8951        // scores, so a key written by either is a geo key.
8952        assert_eq!(
8953            f.run(&[
8954                b"GEORADIUS",
8955                b"Sicily",
8956                b"15",
8957                b"37",
8958                b"200",
8959                b"km",
8960                b"STORE",
8961                b"dst3"
8962            ]),
8963            ":3\r\n"
8964        );
8965        assert_eq!(
8966            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
8967            hashes
8968        );
8969        // STOREDIST stores the distance in the search unit instead, and those
8970        // are full doubles rather than the four places WITHDIST writes. The
8971        // numbers on the right are what 8.10.1 stored for this search, and they
8972        // are compared with a tolerance rather than byte for byte because the
8973        // last bit of a haversine is the platform's sin, cos and asin: this
8974        // machine and that one disagree in the sixteenth digit, and so do two
8975        // Redis builds. Everything a client actually reads back is four places
8976        // and is asserted exactly above.
8977        assert_eq!(
8978            f.run(&[
8979                b"GEOSEARCHSTORE",
8980                b"dst2",
8981                b"Sicily",
8982                b"FROMLONLAT",
8983                b"15",
8984                b"37",
8985                b"BYRADIUS",
8986                b"200",
8987                b"km",
8988                b"ASC",
8989                b"STOREDIST"
8990            ]),
8991            ":3\r\n"
8992        );
8993        for (member, want) in [
8994            ("Catania", 56.441_257_870_158_19),
8995            ("Agrigento", 130.423_487_067_147_14),
8996            ("Palermo", 190.442_429_847_757_92),
8997        ] {
8998            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
8999            let got: f64 = reply
9000                .trim_start_matches(|c: char| c != '\n')
9001                .trim()
9002                .parse()
9003                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
9004            assert!(
9005                (got - want).abs() < 1e-9,
9006                "{member} scored {got} not {want}"
9007            );
9008        }
9009        // The order they went in is the order the scores put them in, which is
9010        // the point of storing the distance rather than the hash.
9011        assert_eq!(
9012            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
9013            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
9014        );
9015        // A search that finds nothing takes the destination with it rather than
9016        // leaving what was there, and a source key that is not there is a
9017        // search that finds nothing.
9018        assert_eq!(
9019            f.run(&[
9020                b"GEOSEARCHSTORE",
9021                b"dst",
9022                b"nokey",
9023                b"FROMLONLAT",
9024                b"15",
9025                b"37",
9026                b"BYRADIUS",
9027                b"200",
9028                b"km"
9029            ]),
9030            ":0\r\n"
9031        );
9032        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
9033    }
9034
9035    #[test]
9036    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
9037        let mut f = Fixture::new();
9038        sicily(&mut f);
9039        // XX on a member that is already where it is changes nothing, and NX on
9040        // one that is there refuses to move it.
9041        assert_eq!(
9042            f.run(&[
9043                b"GEOADD",
9044                b"Sicily",
9045                b"XX",
9046                b"CH",
9047                b"13.361389",
9048                b"38.115556",
9049                b"Palermo"
9050            ]),
9051            ":0\r\n"
9052        );
9053        assert_eq!(
9054            f.run(&[
9055                b"GEOADD",
9056                b"Sicily",
9057                b"NX",
9058                b"13.361389",
9059                b"38.9",
9060                b"Palermo"
9061            ]),
9062            ":0\r\n"
9063        );
9064        assert_eq!(
9065            f.run(&[
9066                b"GEOADD",
9067                b"Sicily",
9068                b"CH",
9069                b"13.361389",
9070                b"38.9",
9071                b"Palermo"
9072            ]),
9073            ":1\r\n"
9074        );
9075        // Out of range, and nothing is stored: the whole call is refused rather
9076        // than the good pairs going in and the bad one stopping it.
9077        assert_eq!(
9078            f.run(&[
9079                b"GEOADD",
9080                b"new",
9081                b"13.361389",
9082                b"38.115556",
9083                b"here",
9084                b"181",
9085                b"38",
9086                b"there"
9087            ]),
9088            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
9089        );
9090        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
9091        assert_eq!(
9092            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
9093            "-ERR value is not a valid float\r\n"
9094        );
9095        // The count of triples is checked before the two gates are, and a call
9096        // with no triples at all reaches the same sentence.
9097        assert_eq!(
9098            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
9099            "-ERR syntax error\r\n"
9100        );
9101        assert_eq!(
9102            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
9103            "-ERR syntax error\r\n"
9104        );
9105        assert_eq!(
9106            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
9107            "-ERR syntax error\r\n"
9108        );
9109        assert_eq!(
9110            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
9111            "-ERR wrong number of arguments for 'geoadd' command\r\n"
9112        );
9113    }
9114
9115    /// The sentences a search answers, which are its contract as much as the
9116    /// results are.
9117    #[test]
9118    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
9119        let mut f = Fixture::new();
9120        sicily(&mut f);
9121        let cases: &[(&[&[u8]], &str)] = &[
9122            (
9123                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
9124                "-ERR need numeric radius\r\n",
9125            ),
9126            (
9127                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
9128                "-ERR radius cannot be negative\r\n",
9129            ),
9130            (
9131                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9132                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9133            ),
9134            (
9135                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9136                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9137            ),
9138            (
9139                &[
9140                    b"GEOSEARCH",
9141                    b"Sicily",
9142                    b"FROMLONLAT",
9143                    b"15",
9144                    b"37",
9145                    b"BYBOX",
9146                    b"x",
9147                    b"1",
9148                    b"km",
9149                ],
9150                "-ERR need numeric width\r\n",
9151            ),
9152            (
9153                &[
9154                    b"GEOSEARCH",
9155                    b"Sicily",
9156                    b"FROMLONLAT",
9157                    b"15",
9158                    b"37",
9159                    b"BYBOX",
9160                    b"1",
9161                    b"y",
9162                    b"km",
9163                ],
9164                "-ERR need numeric height\r\n",
9165            ),
9166            (
9167                &[
9168                    b"GEOSEARCH",
9169                    b"Sicily",
9170                    b"FROMLONLAT",
9171                    b"15",
9172                    b"37",
9173                    b"BYBOX",
9174                    b"-1",
9175                    b"1",
9176                    b"km",
9177                ],
9178                "-ERR height or width cannot be negative\r\n",
9179            ),
9180            (
9181                &[
9182                    b"GEOSEARCH",
9183                    b"Sicily",
9184                    b"FROMLONLAT",
9185                    b"15",
9186                    b"37",
9187                    b"BYRADIUS",
9188                    b"1",
9189                    b"km",
9190                    b"ANY",
9191                ],
9192                "-ERR the ANY argument requires COUNT argument\r\n",
9193            ),
9194            (
9195                &[
9196                    b"GEOSEARCH",
9197                    b"Sicily",
9198                    b"FROMLONLAT",
9199                    b"15",
9200                    b"37",
9201                    b"BYRADIUS",
9202                    b"1",
9203                    b"km",
9204                    b"COUNT",
9205                    b"0",
9206                ],
9207                "-ERR COUNT must be > 0\r\n",
9208            ),
9209            (
9210                &[
9211                    b"GEOSEARCH",
9212                    b"Sicily",
9213                    b"BYRADIUS",
9214                    b"1",
9215                    b"km",
9216                    b"BYBOX",
9217                    b"1",
9218                    b"1",
9219                    b"km",
9220                ],
9221                "-ERR syntax error\r\n",
9222            ),
9223            (
9224                &[
9225                    b"GEOSEARCH",
9226                    b"Sicily",
9227                    b"FROMMEMBER",
9228                    b"Palermo",
9229                    b"FROMLONLAT",
9230                    b"1",
9231                    b"2",
9232                    b"BYRADIUS",
9233                    b"1",
9234                    b"km",
9235                ],
9236                "-ERR syntax error\r\n",
9237            ),
9238            // The two options a GEOSEARCH cannot leave out, each with its own
9239            // sentence, and the command quoted the way the client spelled it.
9240            (
9241                &[
9242                    b"geosearch",
9243                    b"Sicily",
9244                    b"BYRADIUS",
9245                    b"1",
9246                    b"km",
9247                    b"ASC",
9248                    b"WITHDIST",
9249                ],
9250                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9251            ),
9252            (
9253                &[
9254                    b"GEOSEARCH",
9255                    b"Sicily",
9256                    b"FROMLONLAT",
9257                    b"15",
9258                    b"37",
9259                    b"ASC",
9260                    b"WITHDIST",
9261                ],
9262                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9263            ),
9264            // A store cannot also be asked for the distance, and the two
9265            // families name themselves differently in the same sentence.
9266            (
9267                &[
9268                    b"GEOSEARCHSTORE",
9269                    b"d",
9270                    b"Sicily",
9271                    b"FROMLONLAT",
9272                    b"15",
9273                    b"37",
9274                    b"BYRADIUS",
9275                    b"1",
9276                    b"km",
9277                    b"WITHCOORD",
9278                ],
9279                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9280            ),
9281            (
9282                &[
9283                    b"GEORADIUS",
9284                    b"Sicily",
9285                    b"15",
9286                    b"37",
9287                    b"1",
9288                    b"km",
9289                    b"WITHDIST",
9290                    b"STORE",
9291                    b"d",
9292                ],
9293                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9294            ),
9295            // The read only forms have no store at all, so the word is a stray
9296            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
9297            (
9298                &[
9299                    b"GEORADIUS_RO",
9300                    b"Sicily",
9301                    b"15",
9302                    b"37",
9303                    b"1",
9304                    b"km",
9305                    b"STORE",
9306                    b"d",
9307                ],
9308                "-ERR syntax error\r\n",
9309            ),
9310            (
9311                &[
9312                    b"GEOSEARCH",
9313                    b"Sicily",
9314                    b"FROMLONLAT",
9315                    b"15",
9316                    b"37",
9317                    b"BYRADIUS",
9318                    b"1",
9319                    b"km",
9320                    b"STOREDIST",
9321                ],
9322                "-ERR syntax error\r\n",
9323            ),
9324        ];
9325        for (parts, want) in cases {
9326            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9327        }
9328    }
9329
9330    /// A wrong type wins over a bad argument, because the key is looked up
9331    /// first, and every one of the ten says the same thing about it.
9332    #[test]
9333    fn every_geo_command_says_wrongtype() {
9334        let mut f = Fixture::new();
9335        f.run(&[b"SET", b"s", b"v"]);
9336        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9337        let cases: &[&[&[u8]]] = &[
9338            &[b"GEOADD", b"s", b"13", b"38", b"m"],
9339            &[b"GEOPOS", b"s", b"m"],
9340            &[b"GEOHASH", b"s", b"m"],
9341            &[b"GEODIST", b"s", b"a", b"b"],
9342            &[
9343                b"GEOSEARCH",
9344                b"s",
9345                b"FROMLONLAT",
9346                b"15",
9347                b"37",
9348                b"BYRADIUS",
9349                b"1",
9350                b"km",
9351            ],
9352            &[
9353                b"GEOSEARCHSTORE",
9354                b"d",
9355                b"s",
9356                b"FROMLONLAT",
9357                b"15",
9358                b"37",
9359                b"BYRADIUS",
9360                b"1",
9361                b"km",
9362            ],
9363            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9364            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9365            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9366            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9367        ];
9368        for case in cases {
9369            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9370        }
9371        // And it wins over an argument that will not parse, which is the whole
9372        // reason the lookup comes first.
9373        assert_eq!(
9374            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9375            wrong
9376        );
9377    }
9378
9379    // ----------------------------------------------------------------- array
9380
9381    #[test]
9382    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9383        let mut f = Fixture::new();
9384        // Three consecutive positions from a high index, and the reply is how
9385        // many of them were empty before rather than how many were written.
9386        assert_eq!(
9387            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9388            ":3\r\n"
9389        );
9390        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9391        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9392        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9393        // A hole and a key that is not there are the same answer.
9394        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9395        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9396        assert_eq!(
9397            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9398            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9399        );
9400        // Scattered pairs in one command, last write wins within it.
9401        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9402        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9403    }
9404
9405    /// The two numbers an array reports are not the same number, and one of
9406    /// them does not fit a signed integer.
9407    #[test]
9408    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9409        let mut f = Fixture::new();
9410        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9411        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9412        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9413        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9414        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9415        // Deleting in the middle leaves the high water mark where it was.
9416        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9417        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9418        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9419
9420        // The top of the space is addressable, and its length is a number with
9421        // bit sixty three set, so the reply has to be unsigned or it comes back
9422        // negative.
9423        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9424        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9425        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9426        // And one past it does not exist, so a write that would reach it fails
9427        // before any of it lands.
9428        assert_eq!(
9429            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9430            "-ERR array index overflow\r\n"
9431        );
9432        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9433    }
9434
9435    /// One reply per position and not one per element, which is the whole
9436    /// reason the range is capped.
9437    #[test]
9438    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9439        let mut f = Fixture::new();
9440        f.run(&[b"ARSET", b"a", b"1", b"x"]);
9441        assert_eq!(
9442            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9443            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9444        );
9445        // The two ends may come in either order, and the answer is reversed
9446        // rather than empty.
9447        assert_eq!(
9448            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9449            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9450        );
9451        // A key that is not there reads like an array of nothing but holes.
9452        assert_eq!(
9453            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9454            "*2\r\n$-1\r\n$-1\r\n"
9455        );
9456        // A range wider than a million positions is refused and not trimmed,
9457        // because against a missing key it is a request for as many nulls as
9458        // the range is wide.
9459        assert_eq!(
9460            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9461            "-ERR range exceeds maximum of 1000000 items\r\n"
9462        );
9463    }
9464
9465    /// Every index in the argument list is read before the key is touched, so
9466    /// a bad one at the end leaves nothing half written.
9467    #[test]
9468    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9469        let mut f = Fixture::new();
9470        assert_eq!(
9471            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9472            "-ERR invalid array index\r\n"
9473        );
9474        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9475        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9476        assert_eq!(
9477            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9478            "-ERR invalid array index\r\n"
9479        );
9480        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9481        // An index is unsigned here, so the numbers a list would take are not
9482        // the last element, they are errors.
9483        assert_eq!(
9484            f.run(&[b"ARGET", b"a", b"-1"]),
9485            "-ERR invalid array index\r\n"
9486        );
9487        // And a pair list with an odd tail is an arity error rather than a
9488        // syntax one.
9489        assert_eq!(
9490            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9491            "-ERR wrong number of arguments for 'armset' command\r\n"
9492        );
9493        assert_eq!(
9494            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9495            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9496        );
9497    }
9498
9499    #[test]
9500    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9501        let mut f = Fixture::new();
9502        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9503        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9504        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9505        // Two ranges in one command, and the second one covers the whole space
9506        // without walking it.
9507        assert_eq!(
9508            f.run(&[
9509                b"ARDELRANGE",
9510                b"a",
9511                b"100",
9512                b"200",
9513                b"0",
9514                b"18446744073709551614"
9515            ]),
9516            ":2\r\n"
9517        );
9518        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9519        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9520        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9521    }
9522
9523    /// A value goes out as the bytes it came in as, whichever of the three ways
9524    /// the array found to store it.
9525    #[test]
9526    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9527        let mut f = Fixture::new();
9528        let long = vec![b'v'; 200];
9529        f.run(&[
9530            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9531            b"short", b"5", &long, b"6", b"-0",
9532        ]);
9533        // 42 is an integer, 007 is not one because it does not print back the
9534        // same, 3.5 survives a double and 3.14 does not, and the last two are a
9535        // word packed string and a blob.
9536        assert_eq!(
9537            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9538            format!(
9539                "*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",
9540                String::from_utf8_lossy(&long)
9541            )
9542        );
9543    }
9544
9545    #[test]
9546    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9547        let mut f = Fixture::new();
9548        f.run(&[b"ARSET", b"a", b"0", b"x"]);
9549        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9550        assert_eq!(
9551            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9552            "$12\r\nsliced-array\r\n"
9553        );
9554        // And it is a body like any other, so the key commands work on it.
9555        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9556        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9557        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9558        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9559        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9560        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9561    }
9562
9563    #[test]
9564    fn every_array_command_refuses_a_key_holding_something_else() {
9565        let mut f = Fixture::new();
9566        f.run(&[b"SET", b"s", b"v"]);
9567        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9568        for cmd in [
9569            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9570            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9571            &[b"ARGET".as_ref(), b"s", b"0"][..],
9572            &[b"ARMGET".as_ref(), b"s", b"0"][..],
9573            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9574            &[b"ARLEN".as_ref(), b"s"][..],
9575            &[b"ARCOUNT".as_ref(), b"s"][..],
9576            &[b"ARDEL".as_ref(), b"s", b"0"][..],
9577            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9578            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9579            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9580            &[b"ARNEXT".as_ref(), b"s"][..],
9581            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9582            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9583            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9584            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9585            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9586            &[b"ARINFO".as_ref(), b"s"][..],
9587        ] {
9588            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9589        }
9590    }
9591
9592    /// Two of the array commands look the key up before they read the index and
9593    /// the rest read the index first, so the same broken argument gets two
9594    /// different errors depending on which command it went to.
9595    #[test]
9596    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9597        let mut f = Fixture::new();
9598        f.run(&[b"SET", b"s", b"v"]);
9599        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9600        let bad = "-ERR invalid array index\r\n";
9601        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
9602        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
9603        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
9604        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
9605        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
9606        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
9607        // And on a key that is an array the index is just an index.
9608        f.run(&[b"ARSET", b"a", b"0", b"x"]);
9609        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
9610        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
9611    }
9612
9613    #[test]
9614    fn an_append_follows_a_cursor_the_client_can_move() {
9615        let mut f = Fixture::new();
9616        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
9617        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
9618        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
9619        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
9620        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
9621
9622        // A seek says where the next one goes, and a missing key has no cursor
9623        // to move and is not created by the asking.
9624        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
9625        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
9626        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
9627        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
9628        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
9629        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
9630        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
9631
9632        // The top of the space is the one index only ARSEEK will take, and it
9633        // leaves the cursor with nowhere to go.
9634        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
9635        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
9636        assert_eq!(
9637            f.run(&[b"ARINSERT", b"a", b"x"]),
9638            "-ERR insert index overflow\r\n"
9639        );
9640        assert_eq!(
9641            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
9642            "-ERR invalid array index\r\n"
9643        );
9644    }
9645
9646    #[test]
9647    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
9648        let mut f = Fixture::new();
9649        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
9650        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
9651        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
9652        assert_eq!(
9653            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
9654            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
9655        );
9656        // Growing it after it has wrapped puts the survivors back in the order
9657        // they arrived, which is the whole point of paying for the rebuild.
9658        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
9659        assert_eq!(
9660            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
9661            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
9662        );
9663        // The size is read before the key, so a bad one is a bad size wherever
9664        // it is sent.
9665        assert_eq!(
9666            f.run(&[b"ARRING", b"r", b"0", b"x"]),
9667            "-ERR size must be positive\r\n"
9668        );
9669        assert_eq!(
9670            f.run(&[b"ARRING", b"r", b"big", b"x"]),
9671            "-ERR invalid size\r\n"
9672        );
9673    }
9674
9675    #[test]
9676    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
9677        let mut f = Fixture::new();
9678        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
9679        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
9680        assert_eq!(
9681            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
9682            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
9683        );
9684        assert_eq!(
9685            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
9686            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
9687        );
9688        assert_eq!(
9689            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
9690            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
9691            "more than there is gets what there is"
9692        );
9693        // Nothing asked for is an empty reply, and Redis answers that before it
9694        // has read the option or looked at the key.
9695        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
9696        assert_eq!(
9697            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
9698            "-ERR syntax error\r\n"
9699        );
9700        assert_eq!(
9701            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
9702            "-ERR invalid COUNT\r\n"
9703        );
9704
9705        // With no cursor the tail of the array is the anchor, and a hole inside
9706        // the window is reported as one.
9707        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
9708        assert_eq!(
9709            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
9710            "*2\r\n$-1\r\n$1\r\nz\r\n"
9711        );
9712    }
9713
9714    #[test]
9715    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
9716        let mut f = Fixture::new();
9717        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
9718        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
9719        // The whole index space, which ARGETRANGE refuses and this one answers
9720        // in three visits because holes cost nothing.
9721        assert_eq!(
9722            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
9723            "*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"
9724        );
9725        assert_eq!(
9726            f.run(&[
9727                b"ARSCAN",
9728                b"a",
9729                b"18446744073709551614",
9730                b"0",
9731                b"LIMIT",
9732                b"1"
9733            ]),
9734            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
9735        );
9736        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
9737        assert_eq!(
9738            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
9739            "-ERR LIMIT must be positive\r\n"
9740        );
9741        assert_eq!(
9742            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
9743            "-ERR syntax error\r\n"
9744        );
9745        assert_eq!(
9746            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
9747            "-ERR wrong number of arguments for 'arscan' command\r\n"
9748        );
9749    }
9750
9751    #[test]
9752    fn a_grep_answers_the_indexes_whose_elements_match() {
9753        let mut f = Fixture::new();
9754        assert_eq!(
9755            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
9756            "*0\r\n"
9757        );
9758        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
9759
9760        // The two bounds take the ends of the array as well as an index, and a
9761        // reversed range is walked backwards the way ARSCAN walks one.
9762        assert_eq!(
9763            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
9764            "*3\r\n:0\r\n:1\r\n:2\r\n"
9765        );
9766        assert_eq!(
9767            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
9768            "*3\r\n:2\r\n:1\r\n:0\r\n"
9769        );
9770        assert_eq!(
9771            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
9772            "*2\r\n:1\r\n:2\r\n"
9773        );
9774
9775        // One test each. NOCASE reaches all four of them and it may be written
9776        // after the pattern it applies to.
9777        assert_eq!(
9778            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
9779            "*1\r\n:0\r\n"
9780        );
9781        assert_eq!(
9782            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
9783            "*2\r\n:0\r\n:3\r\n"
9784        );
9785        assert_eq!(
9786            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
9787            "*1\r\n:2\r\n"
9788        );
9789        assert_eq!(
9790            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
9791            "*2\r\n:1\r\n:2\r\n"
9792        );
9793
9794        // OR is the default and AND has to be asked for, and either way the
9795        // last of a repeated option wins.
9796        let both: &[&[u8]] = &[
9797            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
9798        ];
9799        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
9800        assert_eq!(
9801            f.run(&[
9802                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
9803            ]),
9804            "*0\r\n"
9805        );
9806        assert_eq!(
9807            f.run(&[
9808                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
9809            ]),
9810            "*2\r\n:0\r\n:1\r\n"
9811        );
9812
9813        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
9814        // not the positions it had to look at.
9815        assert_eq!(
9816            f.run(&[
9817                b"ARGREP",
9818                b"a",
9819                b"-",
9820                b"+",
9821                b"MATCH",
9822                b"a",
9823                b"WITHVALUES",
9824                b"LIMIT",
9825                b"2"
9826            ]),
9827            "*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"
9828        );
9829        assert_eq!(
9830            f.run(&[
9831                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
9832            ]),
9833            "*1\r\n:3\r\n"
9834        );
9835    }
9836
9837    /// Everything ARGREP refuses, in the order it refuses it.
9838    #[test]
9839    fn a_grep_reports_a_broken_command_the_way_redis_does() {
9840        let mut f = Fixture::new();
9841        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
9842        let syntax = "-ERR syntax error\r\n";
9843
9844        // The bounds are read before the plan, so a bad index beats a bad
9845        // predicate whichever way round the two are written.
9846        assert_eq!(
9847            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
9848            "-ERR invalid array index\r\n"
9849        );
9850        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
9851        // A keyword with nothing after it, and a command that asks for nothing.
9852        assert_eq!(
9853            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
9854            syntax
9855        );
9856        assert_eq!(
9857            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
9858            syntax
9859        );
9860        assert_eq!(
9861            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
9862            syntax,
9863            "a command with no predicate in it at all"
9864        );
9865        assert_eq!(
9866            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
9867            "-ERR LIMIT must be positive\r\n"
9868        );
9869        assert_eq!(
9870            f.run(&[
9871                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
9872            ]),
9873            "-ERR value is not an integer or out of range\r\n"
9874        );
9875        assert_eq!(
9876            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
9877            "-ERR regular expression is empty\r\n"
9878        );
9879        assert_eq!(
9880            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
9881            "-ERR invalid regular expression: Missing ')'\r\n"
9882        );
9883        assert_eq!(
9884            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
9885            "-ERR regular expression backreferences are not supported\r\n"
9886        );
9887        // The arity is minus six, so a predicate keyword with no pattern after
9888        // it is short by one and never reaches the parser.
9889        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
9890        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
9891        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
9892    }
9893
9894    #[test]
9895    fn an_op_reduces_a_range_to_one_number() {
9896        let mut f = Fixture::new();
9897        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
9898        assert_eq!(
9899            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
9900            "$4\r\n-0.5\r\n"
9901        );
9902        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
9903        assert_eq!(
9904            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
9905            "$3\r\n2.5\r\n"
9906        );
9907        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
9908        assert_eq!(
9909            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
9910            ":1\r\n"
9911        );
9912        // An aggregate is written with seventeen significant digits, which is
9913        // Redis's own choice and not what a score comes back as.
9914        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
9915        assert_eq!(
9916            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
9917            "$19\r\n0.30000000000000004\r\n"
9918        );
9919        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
9920        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
9921
9922        // Nothing to work with is a null, and a missing key is a null for the
9923        // aggregates and a zero for the two that count.
9924        f.run(&[b"ARSET", b"w", b"0", b"word"]);
9925        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
9926        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
9927        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
9928
9929        assert_eq!(
9930            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
9931            "-ERR unknown operation\r\n"
9932        );
9933        assert_eq!(
9934            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
9935            "-ERR MATCH requires a value argument\r\n"
9936        );
9937        assert_eq!(
9938            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
9939            "-ERR wrong number of arguments for 'arop' command\r\n"
9940        );
9941    }
9942
9943    #[test]
9944    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
9945        let mut f = Fixture::new();
9946        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
9947        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
9948        let short = f.run(&[b"ARINFO", b"a"]);
9949        assert!(
9950            short.starts_with("*14\r\n"),
9951            "seven pairs on RESP2: {short}"
9952        );
9953        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
9954        assert!(
9955            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
9956            "{short}"
9957        );
9958        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
9959        let full = f.run(&[b"ARINFO", b"a", b"full"]);
9960        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
9961        // Two values one apart are held sparsely, so the dense count is zero and
9962        // the two dense averages have nothing to average.
9963        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
9964        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
9965        assert!(
9966            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
9967            "{full}"
9968        );
9969        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
9970
9971        // On RESP3 the same reply is a map and the averages are doubles.
9972        let mut g = Fixture::new();
9973        g.run(&[b"HELLO", b"3"]);
9974        g.run(&[b"ARINSERT", b"a", b"x"]);
9975        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
9976        assert!(map.starts_with("%12\r\n"), "{map}");
9977        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
9978        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
9979    }
9980
9981    #[test]
9982    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
9983        let mut f = Fixture::new();
9984        // Whole numbers up to two to the sixty second come back as integers,
9985        // and past that the digit generator takes over and uses an exponent.
9986        for (score, want) in [
9987            ("3", "3"),
9988            ("3.5", "3.5"),
9989            ("0.3", "0.3"),
9990            ("1e30", "1e+30"),
9991            ("1e19", "1e+19"),
9992            ("1e-7", "1e-7"),
9993            ("0.000001", "0.000001"),
9994            ("4611686018427387904", "4611686018427387904"),
9995            ("-0", "-0"),
9996        ] {
9997            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
9998            assert_eq!(
9999                f.run(&[b"ZSCORE", b"z", b"m"]),
10000                format!("${}\r\n{want}\r\n", want.len()),
10001                "score {score}"
10002            );
10003        }
10004
10005        // The same bytes on RESP3, where the reply is a double rather than a
10006        // bulk string.
10007        let mut g = Fixture::new();
10008        g.run(&[b"HELLO", b"3"]);
10009        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
10010        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
10011        // The two float increments are not this printer. They go through
10012        // ld2string in its human mode, which is a fixed point conversion with
10013        // the trailing zeros taken off, so they never write an exponent, and
10014        // they reply with a bulk string on both protocols.
10015        assert_eq!(
10016            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
10017            "$31\r\n1000000000000000000000000000000\r\n"
10018        );
10019        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
10020        assert_eq!(
10021            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
10022            "$20\r\n10000000000000000000\r\n"
10023        );
10024    }
10025
10026    // ----------------------------------------------------------------- graph
10027
10028    #[test]
10029    fn a_node_comes_back_with_the_fields_it_went_in_with() {
10030        let mut f = Fixture::new();
10031        assert_eq!(
10032            f.run(&[
10033                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
10034            ]),
10035            ":1\r\n"
10036        );
10037        // The year comes back as the four bytes that were sent and not as a
10038        // number, because every property is text and there is nothing on the
10039        // wire that says which of `1815` and `"1815"` the client meant. The
10040        // fields are in the document's order, which is sorted by name, because
10041        // that is what makes a field lookup a binary search.
10042        assert_eq!(
10043            f.run(&[b"G.NGET", b"social", b"ada"]),
10044            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10045        );
10046        // A second write to the same id replaces the document and says so with
10047        // a zero, so an ingest can count what it created.
10048        assert_eq!(
10049            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
10050            ":0\r\n"
10051        );
10052        assert_eq!(
10053            f.run(&[b"G.NGET", b"social", b"ada"]),
10054            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
10055        );
10056        // A node with no properties is an empty map and not a null, which is
10057        // how a client tells an isolated node from one that is not there.
10058        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
10059        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10060        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
10061        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
10062
10063        // A field with no value creates nothing, because the pairs are checked
10064        // before the key is touched.
10065        assert_eq!(
10066            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
10067            "-ERR syntax error\r\n"
10068        );
10069        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
10070
10071        // On RESP3 the same reply is a map.
10072        let mut g = Fixture::new();
10073        g.run(&[b"HELLO", b"3"]);
10074        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
10075        assert_eq!(
10076            g.run(&[b"G.NGET", b"social", b"ada"]),
10077            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
10078        );
10079    }
10080
10081    #[test]
10082    fn an_edge_creates_the_ends_it_needs() {
10083        let mut f = Fixture::new();
10084        assert_eq!(
10085            f.run(&[
10086                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
10087            ]),
10088            ":1\r\n"
10089        );
10090        // Neither end was written first and both are there, as empty nodes.
10091        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
10092        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
10093        assert_eq!(
10094            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
10095            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
10096        );
10097        assert_eq!(
10098            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
10099            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
10100        );
10101        // The same pair under the same label again updates the edge rather than
10102        // making a second one.
10103        assert_eq!(
10104            f.run(&[
10105                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
10106            ]),
10107            ":0\r\n"
10108        );
10109        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
10110        // A different label between the same pair is a different edge.
10111        assert_eq!(
10112            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
10113            ":1\r\n"
10114        );
10115        assert_eq!(
10116            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
10117            ":1\r\n"
10118        );
10119
10120        assert_eq!(
10121            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10122            ":1\r\n"
10123        );
10124        assert_eq!(
10125            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
10126            ":0\r\n"
10127        );
10128        // A label nothing has used, an end that is not there, and a key that is
10129        // not there are all a zero rather than an error.
10130        assert_eq!(
10131            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10132            ":0\r\n"
10133        );
10134        assert_eq!(
10135            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10136            ":0\r\n"
10137        );
10138        assert_eq!(
10139            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10140            ":0\r\n"
10141        );
10142    }
10143
10144    /// A run is paged the way `SCAN` is paged, so a client that can walk one
10145    /// can walk the other.
10146    #[test]
10147    fn a_hop_answers_a_cursor_and_a_page() {
10148        let mut f = Fixture::new();
10149        for i in 0..25u32 {
10150            let dst = format!("n{i}");
10151            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10152        }
10153        // Ten without being asked, and the cursor is where to carry on from.
10154        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10155        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10156
10157        let mut seen = 0;
10158        let mut cursor = String::from("0");
10159        loop {
10160            let page = f.run(&[
10161                b"G.OUT",
10162                b"social",
10163                b"hub",
10164                b"FOLLOWS",
10165                b"COUNT",
10166                b"7",
10167                b"CURSOR",
10168                cursor.as_bytes(),
10169            ]);
10170            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10171            cursor = head
10172                .rsplit("\r\n")
10173                .next()
10174                .expect("the cursor line")
10175                .to_string();
10176            seen += rest
10177                .split_once("\r\n")
10178                .expect("the page length")
10179                .0
10180                .parse::<usize>()
10181                .expect("a length");
10182            if cursor == "0" {
10183                break;
10184            }
10185        }
10186        assert_eq!(seen, 25, "every neighbour once across the pages");
10187
10188        // A cursor past the end is an empty page and not an error, and so is a
10189        // key or a label that is not there.
10190        assert_eq!(
10191            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10192            "*2\r\n$1\r\n0\r\n*0\r\n"
10193        );
10194        assert_eq!(
10195            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10196            "*2\r\n$1\r\n0\r\n*0\r\n"
10197        );
10198        assert_eq!(
10199            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10200            "*2\r\n$1\r\n0\r\n*0\r\n"
10201        );
10202        assert_eq!(
10203            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10204            "-ERR COUNT must be a positive integer\r\n"
10205        );
10206        assert_eq!(
10207            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10208            "-ERR syntax error\r\n"
10209        );
10210    }
10211
10212    #[test]
10213    fn a_degree_counts_one_way_or_both() {
10214        let mut f = Fixture::new();
10215        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10216        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10217        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10218        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10219        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10220        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10221        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10222        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10223        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10224        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10225        assert_eq!(
10226            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10227            "-ERR syntax error\r\n"
10228        );
10229    }
10230
10231    /// A walk answers which nodes it can reach and not by how many routes, so a
10232    /// node two ways out is in the frontier once.
10233    #[test]
10234    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10235        let mut f = Fixture::new();
10236        for (src, dst) in [
10237            ("ada", "grace"),
10238            ("ada", "alan"),
10239            ("grace", "edsger"),
10240            ("alan", "edsger"),
10241            ("edsger", "barbara"),
10242        ] {
10243            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10244        }
10245        // Two hops without being asked, the start left out, and edsger once
10246        // even though both of the first hop's nodes point at it.
10247        assert_eq!(
10248            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10249            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10250        );
10251        assert_eq!(
10252            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10253            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10254        );
10255        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10256        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10257        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10258        // COUNT stops the walk rather than trimming what it found.
10259        assert_eq!(
10260            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10261            "*1\r\n$5\r\ngrace\r\n"
10262        );
10263        // A node nothing leaves is an empty array and not an error.
10264        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10265        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10266        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10267        assert_eq!(
10268            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10269            "-ERR DEPTH must be a positive integer\r\n"
10270        );
10271        assert_eq!(
10272            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10273            "-ERR syntax error\r\n"
10274        );
10275    }
10276
10277    /// The two sided search, which is the whole reason `G.PATH` is a command
10278    /// and not something a client builds out of `G.OUT`.
10279    #[test]
10280    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10281        let mut f = Fixture::new();
10282        // A chain of six, and a shortcut that makes a shorter way round under a
10283        // second label so the search has to take either kind of hop.
10284        for i in 0..6u32 {
10285            let src = format!("n{i}");
10286            let dst = format!("n{}", i + 1);
10287            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10288        }
10289        assert_eq!(
10290            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10291            "*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"
10292        );
10293        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10294        assert_eq!(
10295            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10296            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10297        );
10298        // A node to itself is a path of one, and a depth too short to reach is
10299        // no path at all.
10300        assert_eq!(
10301            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10302            "*1\r\n$2\r\nn2\r\n"
10303        );
10304        assert_eq!(
10305            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10306            "*0\r\n"
10307        );
10308        // Direction counts: the chain only goes one way.
10309        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10310        // An unreachable node, a node that is not there, and a key that is not
10311        // there are the same empty answer.
10312        f.run(&[b"G.NADD", b"road", b"island"]);
10313        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10314        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10315        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10316        assert_eq!(
10317            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10318            "-ERR syntax error\r\n"
10319        );
10320    }
10321
10322    /// The point of the escape in the record tag: the keyspace owns a graph key
10323    /// the way it owns every other key, and none of these commands know a graph
10324    /// exists.
10325    #[test]
10326    fn the_keyspace_sees_a_graph_key_like_any_other() {
10327        let mut f = Fixture::new();
10328        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10329        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10330        assert_eq!(
10331            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10332            "$9\r\nadjacency\r\n"
10333        );
10334        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10335        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10336        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10337        // A graph is counted against the server the way every other body is,
10338        // which is what `maxmemory` will read when this key is a million nodes.
10339        // There is no `MEMORY USAGE` command yet, so this asks the server.
10340        let held = f.server.memory_bytes();
10341        for i in 0..200u32 {
10342            let dst = format!("n{i}");
10343            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10344        }
10345        assert!(
10346            f.server.memory_bytes() > held,
10347            "two hundred edges cost something: {held} then {}",
10348            f.server.memory_bytes()
10349        );
10350        f.run(&[b"DEL", b"big"]);
10351
10352        // An expiry, then a rename, then a move to another database, all of
10353        // which are the keyspace moving a record it cannot look inside.
10354        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10355        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10356        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10357        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10358        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10359        f.run(&[b"SELECT", b"1"]);
10360        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10361
10362        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10363        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10364        f.run(&[b"G.NADD", b"g", b"n"]);
10365        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10366        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10367    }
10368
10369    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
10370    /// rather than answering the way they answer for a key that is not there.
10371    #[test]
10372    fn a_graph_cannot_be_copied_or_dumped() {
10373        let mut f = Fixture::new();
10374        f.run(&[b"G.NADD", b"social", b"ada"]);
10375        assert_eq!(
10376            f.run(&[b"COPY", b"social", b"other"]),
10377            "-ERR COPY is not supported for a graph\r\n"
10378        );
10379        assert_eq!(
10380            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10381            "-ERR COPY is not supported for a graph\r\n"
10382        );
10383        assert_eq!(
10384            f.run(&[b"DUMP", b"social"]),
10385            "-ERR DUMP is not supported for a graph\r\n"
10386        );
10387        // A refused copy leaves both keys exactly as they were.
10388        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10389    }
10390
10391    /// A graph key is a key, so the commands for the other types refuse it and
10392    /// the graph commands refuse theirs.
10393    #[test]
10394    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10395        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10396        let mut f = Fixture::new();
10397        f.run(&[b"G.NADD", b"social", b"ada"]);
10398        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10399        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10400        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10401
10402        f.run(&[b"SET", b"str", b"v"]);
10403        for cmd in [
10404            vec![b"G.NADD".as_ref(), b"str", b"n"],
10405            vec![b"G.NGET".as_ref(), b"str", b"n"],
10406            vec![b"G.NDEL".as_ref(), b"str", b"n"],
10407            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10408            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10409            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10410            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10411            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10412            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10413            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10414        ] {
10415            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10416        }
10417    }
10418
10419    /// Every other collection here takes its key with it when its last member
10420    /// goes, and a graph is no different.
10421    #[test]
10422    fn a_graph_goes_when_its_last_node_does() {
10423        let mut f = Fixture::new();
10424        f.run(&[
10425            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10426        ]);
10427        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10428        // The node and the edges that hung off it are both gone.
10429        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10430        assert_eq!(
10431            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10432            ":0\r\n"
10433        );
10434        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10435        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10436
10437        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10438        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10439        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10440        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10441
10442        // The id the removed node had is not handed out again, so a client
10443        // holding an id from an earlier reply cannot have it mean another node.
10444        f.run(&[b"G.NADD", b"social", b"first"]);
10445        f.run(&[b"G.NADD", b"social", b"second"]);
10446        f.run(&[b"G.NDEL", b"social", b"first"]);
10447        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10448        assert_eq!(
10449            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10450            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10451        );
10452    }
10453
10454    // ------------------------------------------------------------------ json
10455
10456    /// The two path syntaxes answer different shapes, which is the thing a
10457    /// client is most likely to be broken by and so the thing to pin first.
10458    #[test]
10459    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10460        let mut f = Fixture::new();
10461        let doc = br#"{"a":1,"b":{"c":true}}"#;
10462        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10463        // No path at all is the legacy root and not `$`, so the document comes
10464        // back as itself rather than wrapped.
10465        assert_eq!(
10466            f.run(&[b"JSON.GET", b"doc"]),
10467            bulk(r#"{"a":1,"b":{"c":true}}"#)
10468        );
10469        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10470        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10471        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10472        // A path that matched nothing is an empty set on one syntax and an
10473        // error on the other, and the error does not quote the path.
10474        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10475        assert_eq!(
10476            f.run(&[b"JSON.GET", b"doc", b".nope"]),
10477            "-ERR Path does not exist\r\n"
10478        );
10479        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10480        // The key is a document to the rest of the keyspace, under the name
10481        // RedisJSON registers, and every generic command works on it.
10482        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10483        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10484        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10485        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10486        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10487    }
10488
10489    /// The two error lines RedisJSON sends without a prefix in front of them.
10490    ///
10491    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
10492    /// two do not, on a real server, and a differential harness compares the
10493    /// whole line.
10494    #[test]
10495    fn the_two_json_errors_that_carry_no_prefix() {
10496        let mut f = Fixture::new();
10497        f.run(&[b"SET", b"plain", b"x"]);
10498        let wrong = "-Existing key has wrong Redis type\r\n";
10499        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10500        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10501        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10502        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10503        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10504
10505        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10506        // A wildcard that matched something writes to all of it. A wildcard
10507        // that matched nothing would have to invent a place, and that is the
10508        // other unprefixed line.
10509        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10510        assert_eq!(
10511            f.run(&[b"JSON.GET", b"doc"]),
10512            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10513        );
10514        assert_eq!(
10515            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10516            "-Err wrong static path\r\n"
10517        );
10518    }
10519
10520    /// What `JSON.SET` does with a path that named nowhere.
10521    #[test]
10522    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10523        let mut f = Fixture::new();
10524        // A key that is not there can only be written whole.
10525        assert_eq!(
10526            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10527            "-ERR new objects must be created at the root\r\n"
10528        );
10529        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10530        // The root check comes before NX and XX, which is the order a real
10531        // server checks them in.
10532        assert_eq!(
10533            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10534            "-ERR new objects must be created at the root\r\n"
10535        );
10536        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10537        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10538
10539        f.run(&[
10540            b"JSON.SET",
10541            b"doc",
10542            b"$",
10543            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10544        ]);
10545        // One step past a container that is there is a place to write.
10546        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10547        // One step past something that is not, or past something that is not an
10548        // object, is not an error and is not a write either.
10549        assert_eq!(
10550            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10551            "$-1\r\n"
10552        );
10553        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10554        // An index past the end does not append. JSON.ARRAPPEND appends.
10555        assert_eq!(
10556            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10557            "-ERR array index out of range\r\n"
10558        );
10559        assert_eq!(
10560            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10561            "-ERR array index out of range\r\n"
10562        );
10563        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10564        // NX on a path that is there and XX on a path that is not are both a
10565        // nil and neither changes anything.
10566        assert_eq!(
10567            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10568            "$-1\r\n"
10569        );
10570        assert_eq!(
10571            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10572            "$-1\r\n"
10573        );
10574        assert_eq!(
10575            f.run(&[b"JSON.GET", b"doc"]),
10576            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10577        );
10578        // Text that is not JSON is refused before the key is touched. The
10579        // line has no `ERR` in front of it, which is this command's and not
10580        // every command's, and is in D-37.
10581        assert!(
10582            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10583                .starts_with("-this is not the start of a value")
10584        );
10585        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10586    }
10587
10588    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
10589    /// answers a count or a word rather than text.
10590    #[test]
10591    fn the_json_commands_that_do_not_answer_text() {
10592        let mut f = Fixture::new();
10593        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10594        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10595
10596        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10597        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10598        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10599        assert_eq!(
10600            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
10601            format!("*1\r\n{}", bulk("integer"))
10602        );
10603        // The one place a legacy path that matched nothing is a nil rather than
10604        // an error, which lines up with a key that is not there.
10605        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
10606        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
10607
10608        // A boolean flips and answers the value it now has, as an integer on
10609        // one syntax and as the word on the other.
10610        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
10611        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
10612        // Something that is not a boolean is a hole on one syntax and one
10613        // sentence covering both cases on the other.
10614        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
10615        assert_eq!(
10616            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
10617            "-ERR Path does not exist or not a bool\r\n"
10618        );
10619        assert_eq!(
10620            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
10621            "-ERR Path does not exist or not a bool\r\n"
10622        );
10623        assert_eq!(
10624            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
10625            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10626        );
10627
10628        // Clearing empties containers and zeroes numbers and leaves everything
10629        // else alone, and counts only what it changed.
10630        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
10631        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
10632        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
10633        assert_eq!(
10634            f.run(&[b"JSON.GET", b"doc"]),
10635            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
10636        );
10637
10638        // Deleting counts what it removed, and deleting the root is deleting
10639        // the key.
10640        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
10641        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
10642        // Deleting the last member of the root container deletes the key, the
10643        // same way popping the last element off a list does. It is a rule about
10644        // deleting and not about shape: a document written as an empty object
10645        // by JSON.SET stays, because nothing was removed from it.
10646        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
10647        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
10648        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10649        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
10650        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
10651        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
10652        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
10653        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
10654    }
10655
10656    /// `JSON.GET` with more than one path, and with a layout.
10657    ///
10658    /// The wrapper the reply is built in is laid out too, so what a path
10659    /// matched starts one level in for a single JSONPath and two for one of
10660    /// several, and getting that wrong is the kind of thing only a byte for
10661    /// byte comparison catches.
10662    #[test]
10663    fn json_get_lays_out_the_wrapper_it_builds() {
10664        let mut f = Fixture::new();
10665        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
10666
10667        assert_eq!(
10668            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
10669            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
10670        );
10671        // Legacy paths are not wrapped, even when there are several of them.
10672        assert_eq!(
10673            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
10674            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
10675        );
10676        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
10677        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
10678        one.extend_from_slice(fmt);
10679        one.push(b"$.b");
10680        assert_eq!(
10681            f.run(&one),
10682            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
10683        );
10684        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
10685        two.extend_from_slice(fmt);
10686        two.push(b"$.a");
10687        two.push(b"$.nope");
10688        assert_eq!(
10689            f.run(&two),
10690            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
10691        );
10692        // The options are read before the paths and in any order, and a
10693        // document with nothing to lay out is the same either way.
10694        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
10695        root.push(b".a");
10696        assert_eq!(f.run(&root), bulk("1"));
10697    }
10698
10699    /// `JSON.MGET`, which is the only command here that reads more than one key
10700    /// and so the only one whose answer has holes in it.
10701    #[test]
10702    fn json_mget_answers_once_per_key_whatever_is_under_them() {
10703        let mut f = Fixture::new();
10704        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
10705        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
10706        f.run(&[b"SET", b"plain", b"x"]);
10707        assert_eq!(
10708            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
10709            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
10710        );
10711        // A key that is not there and a key holding something else are both a
10712        // hole rather than an error, the way MGET treats a hash.
10713        assert_eq!(
10714            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
10715            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
10716        );
10717        // A legacy path that matched nothing is a hole too, because one bad
10718        // answer should not lose the others.
10719        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
10720    }
10721
10722    /// The four commands that ask how big something is, and the four different
10723    /// sets of answers they give for the same three failures.
10724    ///
10725    /// There is no pattern in this and there is no reading it off the
10726    /// documentation either. It was read off a running RedisJSON one line at a
10727    /// time, and it is written down here because the error text is what a client
10728    /// library branches on.
10729    #[test]
10730    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
10731        let mut f = Fixture::new();
10732        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
10733        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10734
10735        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
10736        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
10737        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
10738        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
10739        assert_eq!(
10740            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
10741            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
10742        );
10743        // A JSONPath answers one entry per match and a hole for a match of the
10744        // wrong kind, which is the one shape all four agree on.
10745        assert_eq!(
10746            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
10747            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
10748        );
10749
10750        // A legacy path that matched nothing. Two of them are an error and two
10751        // of them are a nil, and the two errors do not use the same sentence.
10752        assert_eq!(
10753            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
10754            "-ERR Path does not exist\r\n"
10755        );
10756        assert_eq!(
10757            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
10758            "-ERR Path does not exist\r\n"
10759        );
10760        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
10761        // A nil bulk and not an empty array, even though the answer would have
10762        // been an array, which is what RedisJSON sends here too.
10763        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
10764        // The JSONPath spelling of the same question is an empty array, since
10765        // no match is not a failure on that syntax.
10766        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
10767
10768        // A legacy path that matched the wrong kind of value. Now two of them
10769        // are an ERR and two of them are a WRONGTYPE, and it is not the same
10770        // two.
10771        assert_eq!(
10772            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
10773            "-ERR Path does not exist or not an array\r\n"
10774        );
10775        assert_eq!(
10776            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
10777            "-ERR Path does not exist or not an object\r\n"
10778        );
10779        assert_eq!(
10780            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
10781            "-WRONGTYPE wrong type of path value - expected object\r\n"
10782        );
10783        assert_eq!(
10784            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
10785            "-WRONGTYPE wrong type of path value - expected string\r\n"
10786        );
10787
10788        // A key that is not there, where the two syntaxes swap over: the legacy
10789        // path is the quiet answer and the JSONPath is the error.
10790        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
10791        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
10792        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
10793        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
10794        assert_eq!(
10795            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
10796            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10797        );
10798        // Except this one, which answers about the path instead.
10799        assert_eq!(
10800            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
10801            "-ERR Path does not exist or not an object\r\n"
10802        );
10803    }
10804
10805    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
10806    ///
10807    /// The four of them share one error line for a path that named something
10808    /// that is not an array, and they disagree about what an index outside the
10809    /// array means: insert refuses it and the other two clamp.
10810    #[test]
10811    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
10812        let mut f = Fixture::new();
10813        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
10814
10815        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
10816        assert_eq!(
10817            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
10818            "*1\r\n:6\r\n"
10819        );
10820        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
10821
10822        // A negative index counts back from the end, and the end itself is a
10823        // place to insert at, so an insert at the length is an append.
10824        assert_eq!(
10825            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
10826            ":7\r\n"
10827        );
10828        assert_eq!(
10829            f.run(&[b"JSON.GET", b"doc", b".a"]),
10830            bulk("[1,2,3,4,5,0,6]")
10831        );
10832        assert_eq!(
10833            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
10834            ":8\r\n"
10835        );
10836        // One past the end is not, and neither is one before the front.
10837        assert_eq!(
10838            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
10839            "-ERR index out of bounds\r\n"
10840        );
10841        assert_eq!(
10842            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
10843            "-ERR index out of bounds\r\n"
10844        );
10845
10846        // Trim takes both ends inclusive and clamps both of them, so a start
10847        // past the end leaves an empty array rather than an error.
10848        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
10849        assert_eq!(
10850            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
10851            ":3\r\n"
10852        );
10853        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
10854        assert_eq!(
10855            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
10856            ":2\r\n"
10857        );
10858        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
10859        assert_eq!(
10860            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
10861            ":0\r\n"
10862        );
10863        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10864
10865        // Pop clamps as well, its default is the last element, and an empty
10866        // array pops a nil rather than failing.
10867        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
10868        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
10869        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
10870        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
10871        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
10872
10873        // One sentence covers a path that matched nothing and a path that
10874        // matched the wrong kind of value, for all four of them.
10875        for call in [
10876            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
10877            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
10878            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
10879            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
10880        ] {
10881            for path in [&b".n"[..], &b".nope"[..]] {
10882                let args: Vec<&[u8]> = call
10883                    .iter()
10884                    .map(|a| if *a == b"PATH" { path } else { *a })
10885                    .collect();
10886                assert_eq!(
10887                    f.run(&args),
10888                    "-ERR Path does not exist or not an array\r\n",
10889                    "{} {}",
10890                    String::from_utf8_lossy(call[0]),
10891                    String::from_utf8_lossy(path)
10892                );
10893            }
10894        }
10895
10896        // A key that is not there is the same sentence for all four, on either
10897        // syntax, and it is about the key and not about the path.
10898        assert_eq!(
10899            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
10900            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10901        );
10902        assert_eq!(
10903            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
10904            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10905        );
10906
10907        // The values are parsed before the key is touched, so text that is not
10908        // JSON leaves the document alone.
10909        // Text that is not JSON is refused before the key is touched, and
10910        // the line has no `ERR` in front of it, which is D-37.
10911        assert!(
10912            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
10913                .starts_with("-this is not the start of a value")
10914        );
10915        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10916    }
10917
10918    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
10919    /// path matched cannot take the index, which is D-36.
10920    ///
10921    /// RedisJSON walks the matches, inserts into each one it can, and returns
10922    /// the error on the first one it cannot, leaving the earlier inserts in the
10923    /// document. A write here is one list of edits applied together, so either
10924    /// all of them happen or none of them do.
10925    #[test]
10926    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
10927        let mut f = Fixture::new();
10928        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
10929        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10930        assert_eq!(
10931            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
10932            "-ERR index out of bounds\r\n"
10933        );
10934        assert_eq!(
10935            f.run(&[b"JSON.GET", b"doc"]),
10936            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
10937        );
10938        // Every match can take the index, so every match gets it.
10939        assert_eq!(
10940            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
10941            "*3\r\n:4\r\n:3\r\n:2\r\n"
10942        );
10943        assert_eq!(
10944            f.run(&[b"JSON.GET", b"doc"]),
10945            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
10946        );
10947    }
10948
10949    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
10950    /// last element rather than to one past it.
10951    ///
10952    /// Both of those read like mistakes and both are what RedisJSON does. The
10953    /// start is the one that bites: a start of five into an array of four still
10954    /// looks at the fourth, so a search that should have run out of array comes
10955    /// back with an answer.
10956    #[test]
10957    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
10958        let mut f = Fixture::new();
10959        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
10960
10961        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
10962        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
10963        assert_eq!(
10964            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
10965            "*1\r\n:1\r\n"
10966        );
10967
10968        // Zero as the stop means the end rather than the front, so leaving it
10969        // off and passing it are the same thing.
10970        assert_eq!(
10971            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
10972            ":3\r\n"
10973        );
10974        // The stop is exclusive, so a stop of three does not look at index
10975        // three.
10976        assert_eq!(
10977            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
10978            ":-1\r\n"
10979        );
10980
10981        // The start clamps to the last element in both directions, which is why
10982        // a start of four, five or minus one all find the 1 at index three.
10983        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
10984            assert_eq!(
10985                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
10986                ":3\r\n",
10987                "{}",
10988                String::from_utf8_lossy(start)
10989            );
10990        }
10991        assert_eq!(
10992            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
10993            ":0\r\n"
10994        );
10995        // An empty array is the one case that comes back with nothing, since
10996        // the stop is zero and the loop never starts.
10997        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
10998        assert_eq!(
10999            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
11000            ":-1\r\n"
11001        );
11002
11003        // The comparison is structural rather than one of the encoded bytes,
11004        // because an object in a stored document holds its keys as intern table
11005        // ids where one parsed off the wire holds them as bytes.
11006        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
11007        assert_eq!(
11008            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
11009            ":0\r\n"
11010        );
11011        assert_eq!(
11012            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
11013            ":1\r\n"
11014        );
11015        assert_eq!(
11016            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
11017            ":-1\r\n"
11018        );
11019
11020        // Its errors are a third set again: a missing legacy path is the short
11021        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
11022        // not there is about the path on either syntax.
11023        assert_eq!(
11024            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
11025            "-ERR Path does not exist\r\n"
11026        );
11027        assert_eq!(
11028            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
11029            "-WRONGTYPE wrong type of path value - expected array\r\n"
11030        );
11031        assert_eq!(
11032            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
11033            "-ERR Path does not exist\r\n"
11034        );
11035        assert_eq!(
11036            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
11037            "-ERR Path does not exist\r\n"
11038        );
11039    }
11040
11041    /// The number family answers text and keeps an integer an integer until
11042    /// something in the sum is not one.
11043    #[test]
11044    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
11045        let mut f = Fixture::new();
11046        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
11047        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11048
11049        // A legacy path answers the new value as JSON text in a bulk string,
11050        // not as a number, which is the shape all three of them use.
11051        assert_eq!(
11052            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
11053            bulk("9").as_str()
11054        );
11055        // A JSONPath answers a bulk string holding a JSON array.
11056        assert_eq!(
11057            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
11058            bulk("[11]").as_str()
11059        );
11060        // Two integers stay an integer and a double anywhere in it makes the
11061        // answer a double, which the document then holds.
11062        assert_eq!(
11063            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
11064            bulk("13.0").as_str()
11065        );
11066        assert_eq!(
11067            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
11068            bulk("number").as_str()
11069        );
11070        assert_eq!(
11071            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
11072            bulk("3.0").as_str()
11073        );
11074        assert_eq!(
11075            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
11076            bulk("-8").as_str()
11077        );
11078        // A power of a half is a square root, and the square root of a negative
11079        // number is the error that says the answer is not a number.
11080        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
11081        assert_eq!(
11082            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
11083            bulk("1.224744871391589").as_str()
11084        );
11085        assert_eq!(
11086            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
11087            "-ERR result is not a number\r\n"
11088        );
11089        // An integer answer that does not fit is refused rather than promoted,
11090        // and a negative exponent lands in the same error because there is no
11091        // integer answer to two to the minus one.
11092        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
11093        assert_eq!(
11094            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
11095            "-ERR numeric overflow\r\n"
11096        );
11097        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
11098        assert_eq!(
11099            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
11100            "-ERR numeric overflow\r\n"
11101        );
11102        // A double that leaves the finite numbers is the other error.
11103        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
11104        assert_eq!(
11105            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
11106            "-ERR result is not a number\r\n"
11107        );
11108
11109        // A match that is not a number is a null inside the array on a
11110        // JSONPath, and a legacy path that found no number at all is the error
11111        // with the module's own typo in it.
11112        assert_eq!(
11113            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
11114            bulk("[null]").as_str()
11115        );
11116        assert_eq!(
11117            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
11118            bulk("[]").as_str()
11119        );
11120        assert_eq!(
11121            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
11122            "-ERR Path does not exist or does not contains a number\r\n"
11123        );
11124        assert_eq!(
11125            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
11126            "-ERR Path does not exist or does not contains a number\r\n"
11127        );
11128        // The operand is JSON and has to be a number. Valid JSON that is not
11129        // one is a line of its own, and it goes out without a prefix.
11130        assert_eq!(
11131            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11132            "-bad input number\r\n"
11133        );
11134        assert_eq!(
11135            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11136            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11137        );
11138        assert_eq!(
11139            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11140            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11141        );
11142    }
11143
11144    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
11145    /// which nothing else in the group does.
11146    #[test]
11147    fn json_strappend_reads_its_shape_off_the_argument_count() {
11148        let mut f = Fixture::new();
11149        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11150
11151        assert_eq!(
11152            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11153            ":3\r\n"
11154        );
11155        assert_eq!(
11156            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11157            "*1\r\n:4\r\n"
11158        );
11159        // The length is in bytes and not in characters, so one two byte letter
11160        // takes it up by two.
11161        assert_eq!(
11162            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11163            ":6\r\n"
11164        );
11165        // Three arguments means the value is the last one and the path is the
11166        // root, so this appends to a document that is a string on its own.
11167        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11168        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11169        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11170
11171        // The value is JSON and has to be a JSON string. A number is a
11172        // WRONGTYPE about a path value even though it was the value that was
11173        // wrong, which is the module's wording and not a slip here.
11174        assert_eq!(
11175            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11176            "-WRONGTYPE wrong type of path value - expected string\r\n"
11177        );
11178        assert_eq!(
11179            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11180            "*1\r\n$-1\r\n"
11181        );
11182        assert_eq!(
11183            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11184            "-ERR Path does not exist or not a string\r\n"
11185        );
11186        assert_eq!(
11187            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11188            "*0\r\n"
11189        );
11190        assert_eq!(
11191            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11192            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11193        );
11194    }
11195
11196    /// A legacy path can match more than one value, and which of them the one
11197    /// answer comes from is not the same choice twice.
11198    #[test]
11199    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11200        let mut f = Fixture::new();
11201        // Three arrays of one, two and three elements, which tells the first
11202        // match and the last match apart in a single command.
11203        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11204
11205        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11206        assert_eq!(
11207            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11208            ":4\r\n"
11209        );
11210        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11211        assert_eq!(
11212            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11213            ":2\r\n"
11214        );
11215        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11216        assert_eq!(
11217            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11218            ":1\r\n"
11219        );
11220        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11221        assert_eq!(
11222            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11223            bulk("1").as_str()
11224        );
11225        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11226        assert_eq!(
11227            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11228            bulk("13").as_str()
11229        );
11230        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11231        assert_eq!(
11232            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11233            ":4\r\n"
11234        );
11235        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11236        assert_eq!(
11237            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11238            bulk("false").as_str()
11239        );
11240        // Every one of them wrote to all three matches, whichever one it chose
11241        // to answer about.
11242        assert_eq!(
11243            f.run(&[b"JSON.GET", b"doc", b".a"]),
11244            bulk("[false,true,false]").as_str()
11245        );
11246
11247        // A match of the wrong kind is skipped rather than being the answer, so
11248        // a path that found a string and then two arrays still answers.
11249        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11250        assert_eq!(
11251            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11252            ":3\r\n"
11253        );
11254        assert_eq!(
11255            f.run(&[b"JSON.GET", b"doc", b".a"]),
11256            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11257        );
11258        // Nothing of the right kind anywhere is the error, and that is the only
11259        // case that is.
11260        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11261        assert_eq!(
11262            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11263            "-ERR Path does not exist or not an array\r\n"
11264        );
11265        assert_eq!(
11266            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11267            "-ERR Path does not exist or not a bool\r\n"
11268        );
11269        // The one array that was there and had nothing in it is an answer and
11270        // not a skip, so the pop answers about it rather than about the array
11271        // after it.
11272        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11273        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11274        assert_eq!(
11275            f.run(&[b"JSON.GET", b"doc", b".a"]),
11276            bulk("[[],[2]]").as_str()
11277        );
11278    }
11279
11280    /// A path that matched a value and something inside that value writes to
11281    /// both, which is what `$..` and a nested wildcard are for.
11282    #[test]
11283    fn a_write_reaches_a_match_that_sits_inside_another_match() {
11284        let mut f = Fixture::new();
11285        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11286
11287        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11288        assert_eq!(
11289            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11290            "*3\r\n:3\r\n:2\r\n:3\r\n"
11291        );
11292        assert_eq!(
11293            f.run(&[b"JSON.GET", b"doc", b"$"]),
11294            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11295        );
11296
11297        // The same for a trim, where the outer array keeps the two elements the
11298        // inner writes landed in.
11299        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11300        assert_eq!(
11301            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11302            "*3\r\n:1\r\n:1\r\n:1\r\n"
11303        );
11304        assert_eq!(
11305            f.run(&[b"JSON.GET", b"doc", b"$"]),
11306            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11307        );
11308
11309        // And for a number, where the first match is the object the outer array
11310        // holds and only the two inside it are numbers.
11311        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11312        assert_eq!(
11313            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11314            bulk("[null,8,8]").as_str()
11315        );
11316    }
11317
11318    /// The value a write is given is looked at only once the path has found
11319    /// something of the right kind to use it on.
11320    #[test]
11321    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11322        let mut f = Fixture::new();
11323        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11324
11325        // A string is not a number, so the path answers first and the `"x"` is
11326        // never looked at. Same for the value that is not JSON at all.
11327        assert_eq!(
11328            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11329            bulk("[null]").as_str()
11330        );
11331        assert_eq!(
11332            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11333            bulk("[null]").as_str()
11334        );
11335        assert_eq!(
11336            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11337            bulk("[]").as_str()
11338        );
11339        assert_eq!(
11340            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11341            "-ERR Path does not exist or does not contains a number\r\n"
11342        );
11343        // A number match anywhere and the value is looked at after all.
11344        assert_eq!(
11345            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11346            "-bad input number\r\n"
11347        );
11348
11349        // JSON.STRAPPEND follows the same order with its own two answers.
11350        assert_eq!(
11351            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11352            "*1\r\n$-1\r\n"
11353        );
11354        assert_eq!(
11355            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11356            "-ERR Path does not exist or not a string\r\n"
11357        );
11358        assert_eq!(
11359            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11360            "-WRONGTYPE wrong type of path value - expected string\r\n"
11361        );
11362
11363        // A key that is not there still comes before either of them.
11364        assert_eq!(
11365            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11366            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11367        );
11368        assert_eq!(
11369            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11370            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11371        );
11372    }
11373
11374    /// RFC 7386 in one test: a null deletes, everything else merges, and a
11375    /// patch that is not an object replaces what it lands on.
11376    #[test]
11377    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11378        let mut f = Fixture::new();
11379
11380        // A key that is not there is created at the root, nulls and all,
11381        // because a deletion with nothing to delete is still what the client
11382        // sent.
11383        assert_eq!(
11384            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11385            "+OK\r\n"
11386        );
11387        assert_eq!(
11388            f.run(&[b"JSON.GET", b"doc", b"$"]),
11389            bulk(r#"[{"x":null,"y":1}]"#).as_str()
11390        );
11391
11392        // Onto something that is there, a null deletes the member of that name
11393        // and the rest is merged one level at a time.
11394        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11395        assert_eq!(
11396            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11397            "+OK\r\n"
11398        );
11399        assert_eq!(
11400            f.run(&[b"JSON.GET", b"doc", b"$"]),
11401            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11402        );
11403
11404        // A patch that is not an object replaces what it is merged onto.
11405        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11406        assert_eq!(
11407            f.run(&[b"JSON.GET", b"doc", b"$"]),
11408            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11409        );
11410
11411        // A patch object onto a value that is not an object starts from an
11412        // empty object, so this time the null has nothing to delete and is
11413        // dropped rather than stored.
11414        assert_eq!(
11415            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11416            "+OK\r\n"
11417        );
11418        assert_eq!(
11419            f.run(&[b"JSON.GET", b"doc", b"$"]),
11420            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11421        );
11422
11423        // A member one level past the end of the document is created and keeps
11424        // its nulls, two levels past it is a write that did not happen, and a
11425        // path that would have to invent where it goes is the unprefixed line.
11426        assert_eq!(
11427            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11428            "+OK\r\n"
11429        );
11430        assert_eq!(
11431            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11432            bulk(r#"[{"z":null}]"#).as_str()
11433        );
11434        assert_eq!(
11435            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11436            "$-1\r\n"
11437        );
11438        assert_eq!(
11439            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11440            "-Err wrong static path\r\n"
11441        );
11442
11443        // A wildcard merges every match.
11444        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11445        assert_eq!(
11446            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11447            "+OK\r\n"
11448        );
11449        assert_eq!(
11450            f.run(&[b"JSON.GET", b"doc", b"$"]),
11451            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11452        );
11453
11454        // The three ways to get it wrong.
11455        assert_eq!(
11456            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11457            "-ERR syntax error\r\n"
11458        );
11459        assert_eq!(
11460            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11461            "-ERR new objects must be created at the root\r\n"
11462        );
11463        f.run(&[b"SET", b"str", b"x"]);
11464        assert_eq!(
11465            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11466            "-Existing key has wrong Redis type\r\n"
11467        );
11468    }
11469
11470    /// A descent is the one path that matches a value and something inside that
11471    /// same value, and the inner merge has to survive the outer one.
11472    #[test]
11473    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11474        let mut f = Fixture::new();
11475        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11476        assert_eq!(
11477            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11478            "+OK\r\n"
11479        );
11480        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
11481        // merged onto the result, so the `{"m":1}` written into `a.b` is still
11482        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
11483        assert_eq!(
11484            f.run(&[b"JSON.GET", b"doc", b"$"]),
11485            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11486        );
11487
11488        // A deletion down the same path, which is the case where the inner
11489        // merge empties the object the outer one then copies.
11490        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11491        assert_eq!(
11492            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11493            "+OK\r\n"
11494        );
11495        assert_eq!(
11496            f.run(&[b"JSON.GET", b"doc", b"$"]),
11497            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11498        );
11499    }
11500
11501    /// A filter is a selector like any other, so every command that takes a path
11502    /// takes one, reads and writes alike.
11503    #[test]
11504    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11505        let mut f = Fixture::new();
11506        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11507        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11508
11509        assert_eq!(
11510            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11511            bulk(r#"["a","c"]"#).as_str()
11512        );
11513        // `$` inside the expression is the document, so a member can be measured
11514        // against something that is not inside it.
11515        assert_eq!(
11516            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11517            bulk(r#"["a","c"]"#).as_str()
11518        );
11519        // The legacy syntax takes one too, and answers the first match.
11520        assert_eq!(
11521            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11522            bulk(r#""a""#).as_str()
11523        );
11524        assert_eq!(
11525            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11526            "*1\r\n$6\r\nobject\r\n"
11527        );
11528
11529        // A write goes through it as far as a value that is already there. A
11530        // field that is not there yet has nowhere definite to go, which is the
11531        // same refusal a wildcard gets.
11532        assert_eq!(
11533            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11534            bulk("[9,10]").as_str()
11535        );
11536        assert_eq!(
11537            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11538            "+OK\r\n"
11539        );
11540        assert_eq!(
11541            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11542            "-Err wrong static path\r\n"
11543        );
11544        assert_eq!(
11545            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11546            ":2\r\n"
11547        );
11548        assert_eq!(
11549            f.run(&[b"JSON.GET", b"doc", b"$"]),
11550            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11551        );
11552
11553        // A path that does not parse is refused before the document is read, so
11554        // a key that is not there answers the same way.
11555        assert!(
11556            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11557                .starts_with("-ERR")
11558        );
11559        assert!(
11560            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11561                .starts_with("-ERR")
11562        );
11563    }
11564
11565    /// The operators past the comparisons, over the wire rather than in the
11566    /// parser's own tests, so that a client can reach all of them.
11567    #[test]
11568    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11569        let mut f = Fixture::new();
11570        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11571        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11572
11573        for (path, want) in [
11574            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11575            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11576            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11577            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11578            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11579            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11580            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11581            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11582            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11583            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11584            (b"$.box[?(@.n~)].t", "[]"),
11585            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11586            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11587            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11588            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11589        ] {
11590            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11591        }
11592
11593        // A write goes through one of these the same way it goes through a
11594        // comparison.
11595        assert_eq!(
11596            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11597            "+OK\r\n"
11598        );
11599        assert_eq!(
11600            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
11601            bulk(r#"["b"]"#).as_str()
11602        );
11603    }
11604
11605    /// D-41. RedisJSON refuses this one, and which document it refuses is
11606    /// decided by how it happens to hold an array of numbers.
11607    #[test]
11608    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
11609        let mut f = Fixture::new();
11610        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
11611        assert_eq!(
11612            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11613            "+OK\r\n"
11614        );
11615        assert_eq!(
11616            f.run(&[b"JSON.GET", b"doc", b"$"]),
11617            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
11618        );
11619        // The same document with one element that is not an integer is the one
11620        // RedisJSON is happy with, and it goes the same way here.
11621        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
11622        assert_eq!(
11623            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11624            "+OK\r\n"
11625        );
11626        assert_eq!(
11627            f.run(&[b"JSON.GET", b"doc", b"$"]),
11628            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
11629        );
11630    }
11631
11632    /// `JSON.MSET` checks what it can before it writes anything and skips the
11633    /// one thing it cannot, which is a path with nowhere to put its value.
11634    #[test]
11635    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
11636        let mut f = Fixture::new();
11637        assert_eq!(
11638            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
11639            "+OK\r\n"
11640        );
11641        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
11642        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
11643
11644        // A repeated key takes the last write.
11645        assert_eq!(
11646            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
11647            "+OK\r\n"
11648        );
11649        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
11650
11651        // A triple whose path names nowhere is skipped, the others are still
11652        // written and the reply turns into a nil. Both ways round, because a
11653        // loop that gave up at the first skip would agree with this on one
11654        // order and not on the other.
11655        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
11656        assert_eq!(
11657            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
11658            "$-1\r\n"
11659        );
11660        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
11661        assert_eq!(
11662            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
11663            "$-1\r\n"
11664        );
11665        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11666
11667        // A value that is not JSON, a key holding something else and a path
11668        // that would have to create a document below its own root are all
11669        // checked before anything is written, so the good triple next to them
11670        // does not happen either.
11671        f.run(&[b"SET", b"str", b"x"]);
11672        assert_eq!(
11673            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
11674            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
11675        );
11676        assert_eq!(
11677            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
11678            "-Existing key has wrong Redis type\r\n"
11679        );
11680        assert_eq!(
11681            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
11682            "-ERR new objects must be created at the root\r\n"
11683        );
11684        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
11685
11686        // The two errors a path can be are checked up front as well, so the
11687        // triple before them is not written either. A wildcard that matched
11688        // nothing has nowhere to invent, and an index that is not in the array
11689        // is out of range, and both of them stop the whole command.
11690        assert_eq!(
11691            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
11692            "-Err wrong static path\r\n"
11693        );
11694        assert_eq!(
11695            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
11696            "-ERR array index out of range\r\n"
11697        );
11698        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11699
11700        // Every triple is worked out against the keyspace as the command found
11701        // it, so a second triple on the same key does not see the first one and
11702        // the last write is the one that stays.
11703        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
11704        assert_eq!(
11705            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
11706            "+OK\r\n"
11707        );
11708        assert_eq!(
11709            f.run(&[b"JSON.GET", b"c", b"$"]),
11710            bulk(r#"[{"n":3}]"#).as_str()
11711        );
11712
11713        // An argument count that is not a run of key, path and value is the
11714        // arity error rather than a syntax one.
11715        assert_eq!(
11716            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
11717            "-ERR wrong number of arguments for 'json.mset' command\r\n"
11718        );
11719    }
11720
11721    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
11722    /// an empty array and an empty object apart.
11723    #[test]
11724    fn json_resp_answers_the_document_as_resp_types() {
11725        let mut f = Fixture::new();
11726        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
11727        assert_eq!(
11728            f.run(&[b"JSON.RESP", b"doc"]),
11729            "*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"
11730        );
11731        // A JSONPath wraps the same answer in one more array.
11732        assert_eq!(
11733            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
11734            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
11735        );
11736
11737        f.run(&[
11738            b"JSON.SET",
11739            b"doc",
11740            b"$",
11741            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
11742        ]);
11743        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
11744        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
11745        // A double goes out as its text, so a client reads the same digits
11746        // `JSON.GET` would have given it.
11747        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
11748        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
11749        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
11750
11751        // A missing legacy path is an error, a missing JSONPath is an empty
11752        // array, and a key that is not there is a nil on either.
11753        assert_eq!(
11754            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
11755            "-ERR Path does not exist\r\n"
11756        );
11757        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
11758        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
11759        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
11760    }
11761
11762    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
11763    /// pins the shapes and that the two syntaxes agree rather than a number
11764    /// read off another server. That is D-42.
11765    #[test]
11766    fn json_debug_answers_a_byte_count_and_its_own_help() {
11767        let mut f = Fixture::new();
11768        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
11769        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
11770        assert!(one.starts_with(':'), "{one}");
11771        assert_eq!(
11772            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
11773            format!("*1\r\n{one}")
11774        );
11775        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
11776        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
11777
11778        // A key that is not there is a zero on a legacy path and an empty set
11779        // on a JSONPath, which is the one reader here that does not answer nil
11780        // for it.
11781        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
11782        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
11783        assert_eq!(
11784            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
11785            "-ERR Path does not exist\r\n"
11786        );
11787        assert_eq!(
11788            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
11789            "*0\r\n"
11790        );
11791
11792        assert_eq!(
11793            f.run(&[b"JSON.DEBUG", b"HELP"]),
11794            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
11795             $34\r\nHELP                - this message\r\n"
11796        );
11797        assert_eq!(
11798            f.run(&[b"JSON.DEBUG", b"NOPE"]),
11799            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
11800        );
11801        assert_eq!(
11802            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
11803            "-ERR wrong number of arguments for 'json.debug' command\r\n"
11804        );
11805    }
11806
11807    // ---------------------------------------------------------------- vector
11808
11809    /// The first `VADD` fixes the dimension and every one after it has to
11810    /// agree, because there is no create command to say it earlier.
11811    #[test]
11812    fn the_first_vadd_decides_how_wide_the_set_is() {
11813        let mut f = Fixture::new();
11814        assert_eq!(
11815            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
11816            ":1\r\n"
11817        );
11818        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
11819        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11820        // A second vector under the same name replaces it and says so with a
11821        // zero, so an ingest can count what it created.
11822        assert_eq!(
11823            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
11824            ":0\r\n"
11825        );
11826        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11827        // Three dimensions into a two dimensional set names both numbers, since
11828        // a client that gets this wrong needs to know which end is which.
11829        assert_eq!(
11830            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
11831            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
11832        );
11833        // A vector of zeros has no direction, and it is taken anyway and comes
11834        // back as the origin, because that is what a real server does with it.
11835        assert_eq!(
11836            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
11837            ":1\r\n"
11838        );
11839        assert_eq!(
11840            f.run(&[b"VEMB", b"v", b"nowhere"]),
11841            "*2\r\n$1\r\n0\r\n$1\r\n0\r\n"
11842        );
11843        // A set is made with one quantisation and keeps it, and a `VADD` that
11844        // names another is refused. Naming none names `Q8`, which is why this
11845        // set is a `Q8` one.
11846        assert_eq!(
11847            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"other", b"BIN"]),
11848            "-ERR asked quantization mismatch with existing vector set\r\n"
11849        );
11850        // Nothing above created a key, and a set that never took a vector has
11851        // no dimension to report.
11852        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
11853        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
11854        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
11855    }
11856
11857    /// What a client sent comes back out, and what a client asked for is a
11858    /// similarity and not the distance underneath it.
11859    #[test]
11860    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
11861        let mut f = Fixture::new();
11862        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
11863        // The set stored the direction and the length is multiplied back on the
11864        // way out, so this is `3 4` and not `0.6 0.8`. It is not quite `3 4`
11865        // either, because nobody named a quantisation and that means `Q8`: the
11866        // wider coordinate lands on a code exactly and the other one does not.
11867        // Both numbers are a real server's answers for the same input.
11868        assert_eq!(
11869            f.run(&[b"VEMB", b"v", b"a"]),
11870            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
11871        );
11872        // NOQUANT is the way to ask for what went in to come back out.
11873        f.run(&[b"VADD", b"n", b"VALUES", b"2", b"3", b"4", b"a", b"NOQUANT"]);
11874        assert_eq!(
11875            f.run(&[b"VEMB", b"n", b"a"]),
11876            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
11877        );
11878        // BIN keeps the signs and nothing else, and does not multiply the
11879        // length back on, since a sign has no length in it to scale.
11880        f.run(&[b"VADD", b"b", b"VALUES", b"2", b"3", b"-4", b"a", b"BIN"]);
11881        assert_eq!(
11882            f.run(&[b"VEMB", b"b", b"a"]),
11883            "*2\r\n$1\r\n1\r\n$2\r\n-1\r\n"
11884        );
11885        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
11886        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
11887
11888        // On the axes, where the unit vector is exact and so is the dot
11889        // product, both ends of the scale come out exact: the same direction is
11890        // 1 and the opposite one is 0, with a right angle at a half.
11891        let mut f = Fixture::new();
11892        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
11893        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
11894        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
11895        assert_eq!(
11896            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
11897            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
11898             $8\r\nopposite\r\n$1\r\n0\r\n"
11899        );
11900        // A search from an element leaves that element out, since it is always
11901        // its own nearest neighbour.
11902        assert_eq!(
11903            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
11904            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11905        );
11906        // An element that is not there is an empty answer and not an error,
11907        // which is what a missing key gives too.
11908        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
11909        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
11910        // COUNT bounds it and TRUTH reads every vector rather than the codes,
11911        // which has to agree with the index on a set this small.
11912        assert_eq!(
11913            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
11914            "*1\r\n$6\r\nacross\r\n"
11915        );
11916        assert_eq!(
11917            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
11918            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11919        );
11920        // EF widens how much of the index is read and does not change how many
11921        // answers come back, so a wide search still returns what COUNT asked
11922        // for.
11923        assert_eq!(
11924            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
11925            "*1\r\n$6\r\nacross\r\n"
11926        );
11927
11928        // On RESP3 a scored search is a map, which is what the vector set
11929        // module replies and is not what ZRANGE does here.
11930        let mut g = Fixture::new();
11931        g.run(&[b"HELLO", b"3"]);
11932        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11933        assert_eq!(
11934            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
11935            "%1\r\n$4\r\neast\r\n,1\r\n"
11936        );
11937    }
11938
11939    /// The attribute pair, and the one reply that means two things.
11940    #[test]
11941    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
11942        let mut f = Fixture::new();
11943        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11944        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11945        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
11946        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
11947        // Not parsed as JSON, because nothing reads into it yet and refusing a
11948        // write for a rule nothing enforces would be the wrong trade.
11949        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
11950        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
11951        // An empty string clears it, which is Redis's spelling of the removal.
11952        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
11953        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11954        // An element that is not there answers zero rather than being created,
11955        // since an attribute with no vector under it is not a thing this holds.
11956        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
11957        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
11958        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
11959        // A null for an element with no attribute and a null for one that is
11960        // not there. VISMEMBER is how a client tells the two apart.
11961        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
11962        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
11963        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
11964        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
11965
11966        // WITHATTRIBS carries it alongside the answers.
11967        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11968        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11969        assert_eq!(
11970            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
11971            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
11972        );
11973    }
11974
11975    /// The slot a removed element had is reused, and nothing that was beside it
11976    /// comes back with the next element to get it.
11977    #[test]
11978    fn vrem_takes_the_attribute_with_it() {
11979        let mut f = Fixture::new();
11980        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11981        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11982        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
11983        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
11984        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
11985        // The key went with the last element, the way every other collection
11986        // here works.
11987        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11988
11989        // The next element is given the slot the removed one had, and it comes
11990        // with no attribute on it.
11991        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11992        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11993        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11994        f.run(&[b"VREM", b"v", b"east"]);
11995        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
11996        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
11997    }
11998
11999    /// `VINFO` says what the index is before it says anything a client could
12000    /// mistake for a graph.
12001    #[test]
12002    fn vinfo_says_partition_first() {
12003        let mut f = Fixture::new();
12004        f.run(&[
12005            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
12006        ]);
12007        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
12008        let info = f.run(&[b"VINFO", b"v"]);
12009        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
12010        // What the client asked for and not what happened to the tuning, which
12011        // is `10` section 7: M is recorded and changes nothing.
12012        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
12013        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
12014        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
12015        // Nobody named a quantisation, so this set is a `Q8` one and every
12016        // element in it is stored that way.
12017        assert!(
12018            info.contains("$10\r\nquant-type\r\n$4\r\nint8\r\n"),
12019            "{info}"
12020        );
12021        let mut f = Fixture::new();
12022        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
12023        assert!(
12024            f.run(&[b"VINFO", b"v"])
12025                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
12026        );
12027        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
12028    }
12029
12030    /// A set to read ranges of names out of.
12031    fn named() -> Fixture {
12032        let mut f = Fixture::new();
12033        for (i, name) in ["alpha", "beta", "gamma", "delta", "epsilon"]
12034            .iter()
12035            .enumerate()
12036        {
12037            let x = (i + 1).to_string();
12038            f.run(&[
12039                b"VADD",
12040                b"r",
12041                b"VALUES",
12042                b"2",
12043                x.as_bytes(),
12044                b"1",
12045                name.as_bytes(),
12046            ]);
12047        }
12048        f
12049    }
12050
12051    /// `VRANGE` reads the names in the order bytes come in and pays no
12052    /// attention to where the vectors point.
12053    #[test]
12054    fn vrange_walks_the_names_and_not_the_vectors() {
12055        let mut f = named();
12056        assert_eq!(
12057            f.run(&[b"VRANGE", b"r", b"-", b"+"]),
12058            "*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"
12059        );
12060        assert_eq!(
12061            f.run(&[b"VRANGE", b"r", b"[a", b"[d"]),
12062            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n",
12063            "the high end is a name and not a prefix, so delta is past it"
12064        );
12065        assert_eq!(
12066            f.run(&[b"VRANGE", b"r", b"(alpha", b"(gamma"]),
12067            "*3\r\n$4\r\nbeta\r\n$5\r\ndelta\r\n$7\r\nepsilon\r\n"
12068        );
12069        assert_eq!(
12070            f.run(&[b"VRANGE", b"r", b"[beta", b"[beta"]),
12071            "*1\r\n$4\r\nbeta\r\n"
12072        );
12073        assert_eq!(f.run(&[b"VRANGE", b"r", b"[z", b"+"]), "*0\r\n");
12074        // Bytes and not letters, so an upper case name sorts before every lower
12075        // case one rather than beside its own spelling.
12076        f.run(&[b"VADD", b"r", b"VALUES", b"2", b"1", b"1", b"Beta"]);
12077        assert_eq!(
12078            f.run(&[b"VRANGE", b"r", b"-", b"[beta"]),
12079            "*3\r\n$4\r\nBeta\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12080        );
12081        assert_eq!(f.run(&[b"VRANGE", b"nokey", b"-", b"+"]), "*0\r\n");
12082    }
12083
12084    /// The count cuts the answer after the range is decided, and zero is not
12085    /// the same as leaving it out.
12086    #[test]
12087    fn a_vrange_count_of_zero_asks_for_nothing() {
12088        let mut f = named();
12089        assert_eq!(
12090            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2"]),
12091            "*2\r\n$5\r\nalpha\r\n$4\r\nbeta\r\n"
12092        );
12093        assert_eq!(f.run(&[b"VRANGE", b"r", b"-", b"+", b"0"]), "*0\r\n");
12094        assert!(
12095            f.run(&[b"VRANGE", b"r", b"-", b"+", b"-1"])
12096                .starts_with("*5\r\n"),
12097            "a negative count is no limit at all"
12098        );
12099    }
12100
12101    /// Both ends are read before either is placed, and the count is read before
12102    /// either end.
12103    #[test]
12104    fn vrange_says_which_end_it_could_not_read() {
12105        let mut f = named();
12106        assert_eq!(
12107            f.run(&[b"VRANGE", b"r", b"x", b"y"]),
12108            "-ERR invalid start range format\r\n"
12109        );
12110        assert_eq!(
12111            f.run(&[b"VRANGE", b"r", b"+", b"x"]),
12112            "-ERR invalid end range format\r\n",
12113            "the high end is spelled wrong, which is worth saying before the \
12114             low end being on the wrong side"
12115        );
12116        assert_eq!(
12117            f.run(&[b"VRANGE", b"r", b"+", b"-"]),
12118            "-ERR '-' can only be used as first argument, '+' only as second\r\n"
12119        );
12120        // A bracket with nothing after it is not the empty name here, though an
12121        // element really can be called that.
12122        assert_eq!(
12123            f.run(&[b"VRANGE", b"r", b"[", b"+"]),
12124            "-ERR invalid start range format\r\n"
12125        );
12126        assert_eq!(
12127            f.run(&[b"VRANGE", b"r", b"x", b"+", b"z"]),
12128            "-ERR invalid COUNT value\r\n"
12129        );
12130        assert_eq!(
12131            f.run(&[b"VRANGE", b"r", b"-", b"+", b"2", b"extra"]),
12132            "-ERR wrong number of arguments for 'VRANGE' command\r\n"
12133        );
12134        f.run(&[b"SET", b"s", b"x"]);
12135        assert!(
12136            f.run(&[b"VRANGE", b"s", b"-", b"+"])
12137                .starts_with("-WRONGTYPE")
12138        );
12139    }
12140
12141    /// The option that asks for something this index does not have says so
12142    /// rather than doing something else quietly.
12143    #[test]
12144    fn reduce_is_refused_and_not_ignored() {
12145        let mut f = Fixture::new();
12146        let reduce = f.run(&[
12147            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
12148        ]);
12149        assert!(
12150            reduce.starts_with("-ERR REDUCE is not supported."),
12151            "{reduce}"
12152        );
12153        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12154    }
12155
12156    /// A filtered search answers with the nearest elements that match, and an
12157    /// expression that is not one is an error before the key is looked at.
12158    #[test]
12159    fn vsim_filter_reads_the_attributes() {
12160        let mut f = Fixture::new();
12161        for (name, x, y, attr) in [
12162            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
12163            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
12164            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
12165            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
12166        ] {
12167            f.run(&[
12168                b"VADD",
12169                b"v",
12170                b"VALUES",
12171                b"2",
12172                x.as_bytes(),
12173                y.as_bytes(),
12174                name.as_bytes(),
12175                b"SETATTR",
12176                attr.as_bytes(),
12177            ]);
12178        }
12179        // `b` is the nearest to the query and is the one the filter drops, so
12180        // this is the answer a filter applied afterwards would have got wrong.
12181        assert_eq!(
12182            f.run(&[
12183                b"VSIM",
12184                b"v",
12185                b"VALUES",
12186                b"2",
12187                b"9",
12188                b"1",
12189                b"COUNT",
12190                b"2",
12191                b"FILTER",
12192                b".lang == \"en\"",
12193            ]),
12194            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
12195        );
12196        // A number is compared as a number, and the two halves of an `and` both
12197        // have to hold.
12198        assert_eq!(
12199            f.run(&[
12200                b"VSIM",
12201                b"v",
12202                b"VALUES",
12203                b"2",
12204                b"9",
12205                b"1",
12206                b"FILTER",
12207                b".lang == 'en' and .year > 1980",
12208            ]),
12209            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
12210        );
12211        // A list, and a field an element does not have.
12212        assert_eq!(
12213            f.run(&[
12214                b"VSIM",
12215                b"v",
12216                b"VALUES",
12217                b"2",
12218                b"9",
12219                b"1",
12220                b"FILTER",
12221                b".lang in ['fr', 'de']",
12222            ]),
12223            "*1\r\n$1\r\nb\r\n"
12224        );
12225        assert_eq!(
12226            f.run(&[
12227                b"VSIM",
12228                b"v",
12229                b"VALUES",
12230                b"2",
12231                b"9",
12232                b"1",
12233                b"FILTER",
12234                b".rating > 3"
12235            ]),
12236            "*0\r\n"
12237        );
12238        // TRUTH measures every vector, and the filter still decides which ones
12239        // are measured.
12240        assert_eq!(
12241            f.run(&[
12242                b"VSIM",
12243                b"v",
12244                b"VALUES",
12245                b"2",
12246                b"9",
12247                b"1",
12248                b"TRUTH",
12249                b"FILTER",
12250                b".year < 1980",
12251            ]),
12252            "*1\r\n$1\r\nc\r\n"
12253        );
12254        // VSETATTR moves an element in and out of a filter, which means the tag
12255        // beside its code was rewritten and not just the string.
12256        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
12257        assert_eq!(
12258            f.run(&[
12259                b"VSIM",
12260                b"v",
12261                b"VALUES",
12262                b"2",
12263                b"9",
12264                b"1",
12265                b"COUNT",
12266                b"1",
12267                b"FILTER",
12268                b".lang == \"en\"",
12269            ]),
12270            "*1\r\n$1\r\nb\r\n"
12271        );
12272        // And a VADD that replaces the vector keeps the attribute and the tag,
12273        // which is the same rewrite from the other end.
12274        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12275        assert_eq!(
12276            f.run(&[
12277                b"VSIM",
12278                b"v",
12279                b"VALUES",
12280                b"2",
12281                b"9",
12282                b"1",
12283                b"COUNT",
12284                b"1",
12285                b"FILTER",
12286                b".lang == \"en\"",
12287            ]),
12288            "*1\r\n$1\r\nb\r\n"
12289        );
12290
12291        // The expression is parsed before the key is read, so a bad one is an
12292        // error whether or not the key is there.
12293        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12294        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12295        assert_eq!(
12296            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12297            "-ERR invalid FILTER expression\r\n"
12298        );
12299        // FILTER-EF raises the effort rather than capping it, and zero is
12300        // Redis's word for no limit, so neither is an error.
12301        assert_eq!(
12302            f.run(&[
12303                b"VSIM",
12304                b"v",
12305                b"VALUES",
12306                b"2",
12307                b"9",
12308                b"1",
12309                b"COUNT",
12310                b"1",
12311                b"FILTER-EF",
12312                b"500",
12313                b"FILTER",
12314                b".lang == 'en'",
12315            ]),
12316            "*1\r\n$1\r\nb\r\n"
12317        );
12318        assert_eq!(
12319            f.run(&[
12320                b"VSIM",
12321                b"v",
12322                b"VALUES",
12323                b"2",
12324                b"9",
12325                b"1",
12326                b"COUNT",
12327                b"1",
12328                b"FILTER-EF",
12329                b"0"
12330            ]),
12331            "*1\r\n$1\r\nb\r\n"
12332        );
12333        assert_eq!(
12334            f.run(&[
12335                b"VSIM",
12336                b"v",
12337                b"VALUES",
12338                b"2",
12339                b"9",
12340                b"1",
12341                b"FILTER-EF",
12342                b"lots"
12343            ]),
12344            "-ERR EF must be a positive integer\r\n"
12345        );
12346    }
12347
12348    /// A vector set key is a key, so the keyspace owns it the way it owns every
12349    /// other one and none of those commands know what is inside it.
12350    #[test]
12351    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12352        let mut f = Fixture::new();
12353        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12354        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12355        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12356        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12357        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12358        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12359        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12360        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12361        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12362        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12363        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12364
12365        // And the wrong type is the wrong type in both directions.
12366        f.run(&[b"SET", b"s", b"1"]);
12367        assert_eq!(
12368            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12369            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12370        );
12371        assert_eq!(
12372            f.run(&[b"VCARD", b"s"]),
12373            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12374        );
12375        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12376        assert_eq!(
12377            f.run(&[b"GET", b"v"]),
12378            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12379        );
12380        // A graph and a vector set share the escape in the record tag and are
12381        // still two different types, which is the case the tag alone cannot
12382        // decide.
12383        f.run(&[b"G.NADD", b"social", b"ada"]);
12384        assert_eq!(
12385            f.run(&[b"VCARD", b"social"]),
12386            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12387        );
12388        assert_eq!(
12389            f.run(&[b"G.NGET", b"v", b"ada"]),
12390            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12391        );
12392    }
12393
12394    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
12395    /// shapes, off the database's own generator.
12396    #[test]
12397    fn vrandmember_has_the_two_shapes_srandmember_has() {
12398        let mut f = Fixture::new();
12399        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12400            let x = (i + 1).to_string();
12401            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12402        }
12403        // One element is a bulk string and not an array of one.
12404        let one = f.run(&[b"VRANDMEMBER", b"v"]);
12405        assert!(one.starts_with("$1\r\n"), "{one}");
12406        // A positive count is distinct and stops at the size of the set.
12407        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12408        assert!(all.starts_with("*3\r\n"), "{all}");
12409        for name in ["a", "b", "c"] {
12410            assert!(all.contains(name), "{all} is missing {name}");
12411        }
12412        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12413        assert!(all.starts_with("*2\r\n"), "{all}");
12414        // A negative one draws that many and allows repeats.
12415        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12416        assert!(many.starts_with("*5\r\n"), "{many}");
12417        // A key that is not there answers the shape that was asked for.
12418        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12419        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12420    }
12421
12422    /// `VLINKS` answers about the index that is here rather than the graph that
12423    /// is not, which is D-2.
12424    #[test]
12425    fn vlinks_reports_one_layer_of_partition_neighbours() {
12426        let mut f = Fixture::new();
12427        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12428        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12429        // One layer deep, because the index is one layer deep, so a client
12430        // walking layers gets a short list and not a shape it cannot parse.
12431        assert_eq!(
12432            f.run(&[b"VLINKS", b"v", b"east"]),
12433            "*1\r\n*1\r\n$5\r\nnorth\r\n"
12434        );
12435        assert_eq!(
12436            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12437            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12438        );
12439        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12440        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12441    }
12442
12443    /// A vector arrives either as digits or as bytes, and the two have to mean
12444    /// the same thing.
12445    #[test]
12446    fn fp32_and_values_are_the_same_vector() {
12447        let mut f = Fixture::new();
12448        let mut blob = Vec::new();
12449        for x in [3.0f32, 4.0] {
12450            blob.extend_from_slice(&x.to_le_bytes());
12451        }
12452        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12453        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12454        assert_eq!(
12455            f.run(&[b"VEMB", b"v", b"a"]),
12456            "*2\r\n$17\r\n2.992125988006592\r\n$1\r\n4\r\n"
12457        );
12458        // RAW is the stored bytes and the numbers that turn them back into the
12459        // client's vector, which for `Q8` is a code a coordinate, the length the
12460        // vector arrived with and the scale the codes are measured against. The
12461        // name of the form is a simple string, which is a real server's shape,
12462        // and all four of these are a real server's answers.
12463        assert_eq!(
12464            f.run(&[b"VEMB", b"v", b"a", b"RAW"]),
12465            "*4\r\n+int8\r\n$2\r\n_\x7f\r\n$1\r\n5\r\n$17\r\n0.800000011920929\r\n"
12466        );
12467        // A blob that is not a whole number of floats is not a vector.
12468        assert_eq!(
12469            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12470            "-ERR invalid vector specification\r\n"
12471        );
12472        // Neither is a count that promises more than arrived.
12473        assert_eq!(
12474            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12475            "-ERR syntax error\r\n"
12476        );
12477        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12478    }
12479
12480    // ----------------------------------------------------------------- bloom
12481
12482    /// The filter a client gets when it does not describe one, and the two
12483    /// answers an add can give.
12484    #[test]
12485    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12486        let mut f = Fixture::new();
12487        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12488        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12489        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12490        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12491        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12492        // The defaults are the module's configs and not anything the command
12493        // said, which is 100 entries at a hundredth and a growth of 2.
12494        assert_eq!(
12495            f.run(&[b"BF.INFO", b"b"]),
12496            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12497             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12498             +Expansion rate\r\n:2\r\n"
12499        );
12500        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12501        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12502        // A key that is not there has no filter to report on, and answers two
12503        // different ways about it depending on which command asked.
12504        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12505        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12506    }
12507
12508    /// `BF.EXISTS` on a key holding something else answers a miss, and
12509    /// everything else in the family answers `WRONGTYPE`.
12510    ///
12511    /// The two halves of a check and set disagree about what that key is, which
12512    /// is the module's behaviour and not a decision taken here.
12513    #[test]
12514    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12515        let mut f = Fixture::new();
12516        f.run(&[b"SET", b"s", b"text"]);
12517        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12518        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12519        for cmd in [
12520            vec![&b"BF.ADD"[..], b"s", b"x"],
12521            vec![&b"BF.MADD"[..], b"s", b"x"],
12522            vec![&b"BF.CARD"[..], b"s"],
12523            vec![&b"BF.INFO"[..], b"s"],
12524            vec![&b"BF.DEBUG"[..], b"s"],
12525            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12526        ] {
12527            let name = String::from_utf8_lossy(cmd[0]).into_owned();
12528            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12529        }
12530        // The arguments are read before the key is, so a reserve with a bad
12531        // error rate complains about the rate and never learns about the string.
12532        assert_eq!(
12533            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12534            "-ERR bad error rate\r\n"
12535        );
12536        assert!(
12537            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12538                .starts_with("-WRONGTYPE")
12539        );
12540    }
12541
12542    /// A chain grows by its expansion factor and each link is half as wrong as
12543    /// the one before, which is what makes the whole filter hold its rate.
12544    #[test]
12545    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12546        let mut f = Fixture::new();
12547        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12548        for i in 0..10u32 {
12549            assert_eq!(
12550                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12551                ":1\r\n"
12552            );
12553        }
12554        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12555        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12556        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12557        // Capacity is the sum of every link and not the number that was asked
12558        // for, so it is 10 and then 10 plus 20.
12559        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12560        assert_eq!(
12561            f.run(&[b"BF.DEBUG", b"g"]),
12562            "*3\r\n$7\r\nsize:11\r\n\
12563             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12564             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12565        );
12566
12567        // The same filter told not to grow fills instead.
12568        assert_eq!(
12569            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12570            "+OK\r\n"
12571        );
12572        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12573        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12574        assert_eq!(
12575            f.run(&[b"BF.ADD", b"n", b"c"]),
12576            "-ERR non scaling filter is full\r\n"
12577        );
12578        // And an item that is already in it still answers, because membership
12579        // is checked before fullness.
12580        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12581        // A filter that will not grow has no expansion rate to report, in
12582        // either of the two spellings that make one.
12583        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12584        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12585        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12586        // Asking for both at once is refused, which is one of the module's
12587        // errors that carries no prefix at all.
12588        assert_eq!(
12589            f.run(&[
12590                b"BF.RESERVE",
12591                b"q",
12592                b"0.01",
12593                b"2",
12594                b"NONSCALING",
12595                b"EXPANSION",
12596                b"2"
12597            ]),
12598            "-Nonscaling filters cannot expand\r\n"
12599        );
12600    }
12601
12602    /// A multi add stops where the filter did, so the reply can be shorter than
12603    /// the argument list.
12604    #[test]
12605    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
12606        let mut f = Fixture::new();
12607        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
12608        assert_eq!(
12609            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
12610            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
12611        );
12612        assert_eq!(
12613            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
12614            "*2\r\n:1\r\n:0\r\n"
12615        );
12616    }
12617
12618    /// `BF.INSERT` describes a filter and fills it in one command, with its own
12619    /// spelling of every complaint.
12620    #[test]
12621    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
12622        let mut f = Fixture::new();
12623        assert_eq!(
12624            f.run(&[
12625                b"BF.INSERT",
12626                b"i",
12627                b"CAPACITY",
12628                b"50",
12629                b"ERROR",
12630                b"0.001",
12631                b"ITEMS",
12632                b"a",
12633                b"b"
12634            ]),
12635            "*2\r\n:1\r\n:1\r\n"
12636        );
12637        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
12638        // NOCREATE is the only way to add without making the key.
12639        assert_eq!(
12640            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12641            "-ERR not found\r\n"
12642        );
12643        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12644        // The same mistakes as BF.RESERVE, in the sentences this command uses
12645        // for them, and one sentence where BF.RESERVE has two.
12646        assert_eq!(
12647            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12648            "-Bad capacity\r\n"
12649        );
12650        assert_eq!(
12651            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
12652            "-Bad error rate\r\n"
12653        );
12654        assert_eq!(
12655            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
12656            "-Bad expansion\r\n"
12657        );
12658        // An option is matched on its first letter and not on the word, so a
12659        // token nobody meant as an option is one anyway if it starts with the
12660        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
12661        // builds says so.
12662        assert_eq!(
12663            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
12664            "*1\r\n:1\r\n"
12665        );
12666        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
12667        // Only E and N need a second look, one for ERROR against EXPANSION and
12668        // the other for NOCREATE against NONSCALING, and both stop as soon as
12669        // they can tell the two apart.
12670        assert_eq!(
12671            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
12672            "*1\r\n:1\r\n"
12673        );
12674        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
12675        assert_eq!(
12676            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
12677            "*1\r\n:1\r\n"
12678        );
12679        assert_eq!(
12680            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
12681            "-ERR not found\r\n"
12682        );
12683        // A letter that starts nothing is the one case that is refused.
12684        assert_eq!(
12685            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12686            "-Unknown argument received\r\n"
12687        );
12688        // Everything after ITEMS is an item, even when it spells an option.
12689        assert_eq!(
12690            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12691            "*1\r\n:1\r\n"
12692        );
12693        // And ITEMS with nothing after it is the same as leaving it out.
12694        assert!(
12695            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12696                .contains("wrong number of arguments")
12697        );
12698    }
12699
12700    /// A filter dumped a chunk at a time and put back into another key is the
12701    /// same filter.
12702    #[test]
12703    fn a_dump_replays_into_a_filter_that_answers_the_same() {
12704        let mut f = Fixture::new();
12705        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
12706        for i in 0..25u32 {
12707            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
12708        }
12709        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
12710
12711        // Iterator zero asks for the header and every one after it is a running
12712        // byte offset, and a chunk never spans two links.
12713        let mut iter = b"0".to_vec();
12714        let mut chunks = 0;
12715        loop {
12716            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
12717            let text = String::from_utf8_lossy(&raw).into_owned();
12718            let next = text
12719                .split("\r\n")
12720                .nth(1)
12721                .and_then(|n| n.strip_prefix(':'))
12722                .expect("a two element reply of an iterator and a chunk")
12723                .to_owned();
12724            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12725            let data = &body[body
12726                .windows(2)
12727                .position(|w| w == b"\r\n")
12728                .expect("a length line")
12729                + 2..body.len() - 2];
12730            if next == "0" {
12731                assert!(data.is_empty(), "the last chunk is empty");
12732                break;
12733            }
12734            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
12735            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
12736            iter = next.into_bytes();
12737            chunks += 1;
12738        }
12739        assert_eq!(chunks, 3, "a header and one chunk per link");
12740
12741        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
12742        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
12743        for i in 0..25u32 {
12744            assert_eq!(
12745                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
12746                ":1\r\n"
12747            );
12748        }
12749
12750        // A header on top of a filter is refused rather than merged, and so is
12751        // one that no filter wrote.
12752        assert_eq!(
12753            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
12754            "-ERR received bad data\r\n"
12755        );
12756        assert_eq!(
12757            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
12758            "-ERR received bad data\r\n"
12759        );
12760        // An offset past the end of the filter names itself.
12761        assert_eq!(
12762            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
12763            "-ERR invalid offset - no link found\r\n"
12764        );
12765        assert_eq!(
12766            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
12767            "-ERR Second argument must be numeric\r\n"
12768        );
12769        // The same complaint without the prefix on the way out, which is the
12770        // module's inconsistency and not a slip here.
12771        assert_eq!(
12772            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
12773            "-Second argument must be numeric\r\n"
12774        );
12775    }
12776
12777    /// The argument checks, which have a sentence each and read numbers the way
12778    /// Redis reads them everywhere else.
12779    #[test]
12780    fn reserve_reads_its_numbers_the_way_string2ll_does() {
12781        let mut f = Fixture::new();
12782        for (args, want) in [
12783            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
12784            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
12785            (
12786                vec![&b"0"[..], b"10"],
12787                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12788            ),
12789            (
12790                vec![&b"1"[..], b"10"],
12791                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12792            ),
12793            (
12794                vec![&b"inf"[..], b"10"],
12795                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12796            ),
12797            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
12798            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
12799            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
12800            (
12801                vec![&b"0.01"[..], b"0"],
12802                "-ERR capacity must be in the range [1, 1073741824]\r\n",
12803            ),
12804            (
12805                vec![&b"0.01"[..], b"1073741825"],
12806                "-ERR capacity must be in the range [1, 1073741824]\r\n",
12807            ),
12808        ] {
12809            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
12810            cmd.extend(args.iter().copied());
12811            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
12812        }
12813        assert_eq!(
12814            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
12815            "-ERR no expansion\r\n"
12816        );
12817        assert_eq!(
12818            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
12819            "-ERR bad expansion\r\n"
12820        );
12821        assert_eq!(
12822            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
12823            "-ERR expansion must be in the range [0, 32768]\r\n"
12824        );
12825        // Trailing rubbish after the capacity is ignored rather than refused.
12826        assert_eq!(
12827            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
12828            "+OK\r\n"
12829        );
12830        assert_eq!(
12831            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
12832            "-ERR item exists\r\n"
12833        );
12834        assert_eq!(
12835            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
12836            "-Invalid information value\r\n"
12837        );
12838        assert!(
12839            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
12840                .contains("wrong number of arguments")
12841        );
12842    }
12843
12844    /// The RESP3 shapes, which are where this family differs most from RESP2.
12845    #[test]
12846    fn the_bloom_family_answers_in_resp3_spelling_too() {
12847        let mut f = Fixture::new();
12848        f.out.set_proto(Proto::Resp3);
12849        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
12850        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
12851        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
12852        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
12853        assert_eq!(
12854            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
12855            "*2\r\n#t\r\n#f\r\n"
12856        );
12857        // The count stays an integer, because it counts rather than answers.
12858        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
12859        assert_eq!(
12860            f.run(&[b"BF.INFO", b"b"]),
12861            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12862             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
12863             +Expansion rate\r\n:2\r\n"
12864        );
12865        // One field is a map of one here and a bare array of one on RESP2, so
12866        // this is the reply where the two protocols carry different facts.
12867        assert_eq!(
12868            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
12869            "%1\r\n+Capacity\r\n:100\r\n"
12870        );
12871    }
12872
12873    // ---------------------------------------------------------------- cuckoo
12874
12875    /// A dump header, which is the four counts and the three widths a filter
12876    /// writes in front of its fingerprints.
12877    ///
12878    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
12879    /// tests below want out of it is the states a filter cannot be put into
12880    /// from the wire.
12881    fn cf_header(
12882        items: u64,
12883        buckets: u64,
12884        deletes: u64,
12885        filters: u64,
12886        geometry: [u16; 3],
12887    ) -> Vec<u8> {
12888        let mut out = Vec::with_capacity(38);
12889        for n in [items, buckets, deletes, filters] {
12890            out.extend_from_slice(&n.to_le_bytes());
12891        }
12892        for n in geometry {
12893            out.extend_from_slice(&n.to_le_bytes());
12894        }
12895        out
12896    }
12897
12898    /// The filter a client gets when it does not describe one, and the thing a
12899    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
12900    /// take them out again.
12901    #[test]
12902    fn cf_add_makes_the_filter_and_counts_the_copies() {
12903        let mut f = Fixture::new();
12904        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12905        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12906        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
12907        // The NX form is the one that looks first, which is why it is a command
12908        // of its own rather than an option.
12909        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
12910        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
12911        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
12912        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
12913        assert_eq!(
12914            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
12915            "*2\r\n:1\r\n:0\r\n"
12916        );
12917        // The defaults are the module's configs: 1024 entries over buckets of
12918        // two, twenty kicks and a chain that grows by one.
12919        assert_eq!(
12920            f.run(&[b"CF.INFO", b"d"]),
12921            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
12922             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
12923             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
12924             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12925        );
12926        assert_eq!(
12927            f.run(&[b"CF.DEBUG", b"d"]),
12928            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
12929             max_iterations:20 expansion:1\r\n"
12930        );
12931        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
12932        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
12933
12934        // A delete takes one copy, so the same item goes twice and then stops.
12935        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12936        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
12937        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12938        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
12939        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
12940
12941        // A key with no filter under it gets three different sentences and one
12942        // plain miss, depending on which command asked.
12943        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
12944        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
12945        assert_eq!(
12946            f.run(&[b"CF.COMPACT", b"gone"]),
12947            "-Cuckoo filter was not found\r\n"
12948        );
12949        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
12950        // And `CF.COMPACT` is declared as taking any number of keys and takes
12951        // exactly one, which is the module's own arity being wrong rather than
12952        // this table's.
12953        assert!(
12954            f.run(&[b"CF.COMPACT", b"a", b"b"])
12955                .contains("wrong number of arguments")
12956        );
12957    }
12958
12959    /// The four that only read fingerprints treat a key holding something else
12960    /// as a key with no filter, and everything else answers `WRONGTYPE`.
12961    #[test]
12962    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
12963        let mut f = Fixture::new();
12964        f.run(&[b"SET", b"s", b"text"]);
12965        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
12966        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12967        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
12968        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
12969        // and is declared read only, so neither of the two halves of the family
12970        // is the same set as the flags say.
12971        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
12972        assert_eq!(
12973            f.run(&[b"CF.COMPACT", b"s"]),
12974            "-Cuckoo filter was not found\r\n"
12975        );
12976        for cmd in [
12977            vec![&b"CF.ADD"[..], b"s", b"x"],
12978            vec![&b"CF.ADDNX"[..], b"s", b"x"],
12979            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
12980            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
12981            vec![&b"CF.INFO"[..], b"s"],
12982            vec![&b"CF.DEBUG"[..], b"s"],
12983            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
12984            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
12985            vec![&b"CF.RESERVE"[..], b"s", b"64"],
12986        ] {
12987            let name = String::from_utf8_lossy(cmd[0]).into_owned();
12988            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12989        }
12990    }
12991
12992    /// `CF.RESERVE` reads its options by name in an order of its own, and the
12993    /// first pair with a given name is the only one it looks at.
12994    #[test]
12995    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
12996        let mut f = Fixture::new();
12997        assert_eq!(
12998            f.run(&[
12999                b"CF.RESERVE",
13000                b"r",
13001                b"64",
13002                b"BUCKETSIZE",
13003                b"1",
13004                b"MAXITERATIONS",
13005                b"7",
13006                b"EXPANSION",
13007                b"4"
13008            ]),
13009            "+OK\r\n"
13010        );
13011        assert_eq!(
13012            f.run(&[b"CF.DEBUG", b"r"]),
13013            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
13014             max_iterations:7 expansion:4\r\n"
13015        );
13016        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
13017
13018        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
13019        assert_eq!(
13020            f.run(&[b"CF.RESERVE", b"q", b"1"]),
13021            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13022        );
13023        // The range is the bucket size's and not a constant, so a capacity that
13024        // was fine at two slots a bucket is not at four.
13025        assert_eq!(
13026            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
13027            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
13028        );
13029        assert_eq!(
13030            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
13031            "+OK\r\n"
13032        );
13033
13034        // The capacity is checked last, so a command that is wrong twice
13035        // answers about the option. Which option it answers about is the order
13036        // the module looks for them in and not the order they were written, so
13037        // a bad kick budget wins over a bad bucket size wherever the two sit.
13038        assert_eq!(
13039            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
13040            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13041        );
13042        assert_eq!(
13043            f.run(&[
13044                b"CF.RESERVE",
13045                b"q2",
13046                b"64",
13047                b"EXPANSION",
13048                b"xx",
13049                b"BUCKETSIZE",
13050                b"0"
13051            ]),
13052            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
13053        );
13054        assert_eq!(
13055            f.run(&[
13056                b"CF.RESERVE",
13057                b"q2",
13058                b"64",
13059                b"MAXITERATIONS",
13060                b"0",
13061                b"BUCKETSIZE",
13062                b"0"
13063            ]),
13064            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
13065        );
13066        // A second pair with a name that has already been read is not looked at
13067        // at all, so this one is a filter with buckets of one rather than an
13068        // error about a bucket size of zero.
13069        assert_eq!(
13070            f.run(&[
13071                b"CF.RESERVE",
13072                b"q3",
13073                b"64",
13074                b"BUCKETSIZE",
13075                b"1",
13076                b"BUCKETSIZE",
13077                b"0"
13078            ]),
13079            "+OK\r\n"
13080        );
13081        // A pair nobody knows is dropped, which is the opposite of what
13082        // `CF.INSERT` does with the same mistake.
13083        assert_eq!(
13084            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
13085            "+OK\r\n"
13086        );
13087        assert_eq!(
13088            f.run(&[b"CF.DEBUG", b"q4"]),
13089            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
13090             max_iterations:20 expansion:1\r\n"
13091        );
13092        // And an option with nothing after it leaves an odd number of them,
13093        // which is an arity error rather than a complaint about the option.
13094        assert!(
13095            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
13096                .contains("wrong number of arguments")
13097        );
13098    }
13099
13100    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
13101    /// with `CF.RESERVE` about nothing.
13102    #[test]
13103    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
13104        let mut f = Fixture::new();
13105        assert_eq!(
13106            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
13107            "*2\r\n:1\r\n:1\r\n"
13108        );
13109        assert_eq!(
13110            f.run(&[b"CF.DEBUG", b"i"]),
13111            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
13112             max_iterations:20 expansion:1\r\n"
13113        );
13114        // The NX form has three answers rather than two, which is why it stays
13115        // integers on both protocols.
13116        assert_eq!(
13117            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
13118            "*2\r\n:0\r\n:1\r\n"
13119        );
13120        assert_eq!(
13121            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
13122            "-ERR not found\r\n"
13123        );
13124        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
13125
13126        assert_eq!(
13127            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
13128            "-Bad capacity\r\n"
13129        );
13130        // The bucket size cannot be given here, so the range names the config
13131        // that holds it instead of the option `CF.RESERVE` names.
13132        assert_eq!(
13133            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
13134            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13135        );
13136        // Every occurrence is checked, which is where this differs from
13137        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
13138        // one is the one that would have been used.
13139        assert_eq!(
13140            f.run(&[
13141                b"CF.INSERT",
13142                b"i",
13143                b"CAPACITY",
13144                b"8",
13145                b"CAPACITY",
13146                b"2",
13147                b"ITEMS",
13148                b"a"
13149            ]),
13150            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
13151        );
13152        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
13153        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
13154        // refused.
13155        assert_eq!(
13156            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
13157            "*1\r\n:1\r\n"
13158        );
13159        assert_eq!(
13160            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
13161            "*1\r\n:1\r\n"
13162        );
13163        assert_eq!(
13164            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
13165            "-Unknown argument received\r\n"
13166        );
13167        // Everything after ITEMS is an item, even when it spells an option.
13168        assert_eq!(
13169            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
13170            "*1\r\n:1\r\n"
13171        );
13172        // And the two ways of sending no items at all are the same complaint.
13173        assert!(
13174            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
13175                .contains("wrong number of arguments")
13176        );
13177        assert!(
13178            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
13179                .contains("wrong number of arguments")
13180        );
13181    }
13182
13183    /// The two walls a filter can hit, which say different things and are not
13184    /// the same wall.
13185    #[test]
13186    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
13187        let mut f = Fixture::new();
13188        f.run(&[
13189            b"CF.RESERVE",
13190            b"s",
13191            b"4",
13192            b"BUCKETSIZE",
13193            b"1",
13194            b"EXPANSION",
13195            b"0",
13196        ]);
13197        for i in 0..4u32 {
13198            assert_eq!(
13199                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
13200                ":1\r\n"
13201            );
13202        }
13203        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
13204        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
13205        // The add commands say it in a sentence and the insert commands say it
13206        // in the array, one value per item, and the array is never short.
13207        assert_eq!(
13208            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
13209            "*2\r\n:-1\r\n:-1\r\n"
13210        );
13211        assert_eq!(
13212            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
13213            "*2\r\n:0\r\n:-1\r\n"
13214        );
13215
13216        // A chain that is allowed to grow stops for a different reason, and the
13217        // count it stops at is the filter limit rather than the room: this one
13218        // gives up with three slots free. Loading a chain that already has
13219        // every filter it is allowed shows why, since it refuses an item
13220        // straight into an empty one.
13221        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
13222        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
13223        assert_eq!(
13224            f.run(&[b"CF.ADD", b"g", b"q"]),
13225            "-Maximum expansions reached\r\n"
13226        );
13227        assert_eq!(
13228            f.run(&[b"CF.INFO", b"g"]),
13229            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
13230             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
13231             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
13232             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13233        );
13234    }
13235
13236    /// A filter dumped a chunk at a time and put back under another key is the
13237    /// same filter, and the headers that describe one nobody could build are
13238    /// refused on the way in.
13239    #[test]
13240    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
13241        let mut f = Fixture::new();
13242        f.run(&[
13243            b"CF.RESERVE",
13244            b"src",
13245            b"8",
13246            b"BUCKETSIZE",
13247            b"2",
13248            b"EXPANSION",
13249            b"2",
13250        ]);
13251        for i in 0..40u32 {
13252            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
13253        }
13254        // Position zero asks for the header and every one after it is a byte
13255        // offset across every filter laid end to end, and the walk ends on a
13256        // zero and a nil rather than an empty chunk.
13257        let mut pos = b"0".to_vec();
13258        let mut chunks = 0;
13259        loop {
13260            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
13261            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
13262            let next = head
13263                .split("\r\n")
13264                .nth(1)
13265                .and_then(|n| n.strip_prefix(':'))
13266                .expect("a two element reply of a position and a chunk")
13267                .to_owned();
13268            if next == "0" {
13269                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
13270                break;
13271            }
13272            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
13273            let at = body
13274                .windows(2)
13275                .position(|w| w == b"\r\n")
13276                .expect("a length line")
13277                + 2;
13278            let data = &body[at..body.len() - 2];
13279            assert_eq!(
13280                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13281                "+OK\r\n",
13282                "loading chunk {chunks}"
13283            );
13284            pos = next.into_bytes();
13285            chunks += 1;
13286        }
13287        assert!(chunks >= 2, "a header and at least one chunk");
13288
13289        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13290        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13291        for i in 0..40u32 {
13292            assert_eq!(
13293                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13294                ":1\r\n"
13295            );
13296        }
13297
13298        // A filter with nothing in it hands out no header at all, so a client
13299        // that dumps one has nothing to load back.
13300        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13301        assert_eq!(
13302            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13303            "*2\r\n:0\r\n$-1\r\n"
13304        );
13305
13306        // The positions this end will not take, which are not the same set at
13307        // both ends: a dump refuses a negative one and a load takes it as an
13308        // offset and fails to find anything there.
13309        assert_eq!(
13310            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13311            "-Invalid position\r\n"
13312        );
13313        assert_eq!(
13314            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13315            "-Invalid position\r\n"
13316        );
13317        assert_eq!(
13318            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13319            "-Invalid position\r\n"
13320        );
13321        assert_eq!(
13322            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13323            "-Couldn't load chunk!\r\n"
13324        );
13325        // A header on top of a filter is refused rather than merged.
13326        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13327        assert_eq!(
13328            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13329            "-ERR item exists\r\n"
13330        );
13331        // A chunk that is not the size of a header where a header should have
13332        // been is one sentence, and one that is the size of a header and
13333        // describes a filter nobody could build is another.
13334        assert_eq!(
13335            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13336            "-Invalid header\r\n"
13337        );
13338        for (why, bad) in [
13339            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13340            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13341            (
13342                "a bucket count that is not a power of two",
13343                cf_header(0, 3, 0, 1, [2, 20, 1]),
13344            ),
13345            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13346            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13347            (
13348                "a growth nobody could reach",
13349                cf_header(0, 8, 0, 1, [2, 20, 32769]),
13350            ),
13351            (
13352                "a chain that cannot grow and did",
13353                cf_header(0, 8, 0, 2, [2, 20, 0]),
13354            ),
13355            // The count is written in eight bytes and read into two, so a
13356            // number that is a multiple of the second arrives as none.
13357            (
13358                "a filter count that wraps",
13359                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13360            ),
13361        ] {
13362            assert_eq!(
13363                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13364                "-Couldn't create filter!\r\n",
13365                "{why}"
13366            );
13367        }
13368    }
13369
13370    /// The RESP3 shapes, which are where this family differs most from RESP2
13371    /// and where one of its answers stops being readable.
13372    #[test]
13373    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13374        let mut f = Fixture::new();
13375        f.out.set_proto(Proto::Resp3);
13376        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13377        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13378        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13379        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13380        assert_eq!(
13381            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13382            "*2\r\n#t\r\n#f\r\n"
13383        );
13384        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13385        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13386        // The count stays an integer, because it counts rather than answers.
13387        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13388        assert_eq!(
13389            f.run(&[b"CF.INFO", b"c"]),
13390            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13391             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13392             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13393             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13394        );
13395
13396        // `CF.INSERT` writes a boolean per item here and an integer per item on
13397        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
13398        // client cannot tell an item that did not fit from one that is already
13399        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
13400        f.run(&[
13401            b"CF.RESERVE",
13402            b"s",
13403            b"4",
13404            b"BUCKETSIZE",
13405            b"1",
13406            b"EXPANSION",
13407            b"0",
13408        ]);
13409        assert_eq!(
13410            f.run(&[
13411                b"CF.INSERT",
13412                b"s",
13413                b"ITEMS",
13414                b"a",
13415                b"b",
13416                b"c",
13417                b"d",
13418                b"e",
13419                b"f"
13420            ]),
13421            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13422        );
13423        assert_eq!(
13424            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13425            "*2\r\n:0\r\n:-1\r\n"
13426        );
13427        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13428        // The end of a dump is a nil and not an empty chunk, which is one
13429        // underscore here and a negative length on RESP2.
13430        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13431    }
13432
13433    // ------------------------------------------------------------------- cms
13434
13435    /// A sketch is made from either end, and both constructors look at the key
13436    /// before they look at their arguments.
13437    #[test]
13438    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13439        let mut f = Fixture::new();
13440        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13441        assert_eq!(
13442            f.run(&[b"CMS.INFO", b"d"]),
13443            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13444        );
13445        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13446        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13447        // Two over the error rounded up, and the log of the probability over the
13448        // log of a half rounded up, which for these two is 200 by 6.
13449        assert_eq!(
13450            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13451            "+OK\r\n"
13452        );
13453        assert_eq!(
13454            f.run(&[b"CMS.INFO", b"p"]),
13455            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13456        );
13457        // The key is checked first, so a width of zero at a key that is already
13458        // there is about the key and not about the width.
13459        assert_eq!(
13460            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13461            "-CMS: key already exists\r\n"
13462        );
13463        assert_eq!(
13464            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13465            "-CMS: invalid width\r\n"
13466        );
13467        assert_eq!(
13468            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13469            "-CMS: invalid depth\r\n"
13470        );
13471        assert_eq!(
13472            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13473            "-CMS: invalid overestimation value\r\n"
13474        );
13475        assert_eq!(
13476            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13477            "-CMS: invalid prob value\r\n"
13478        );
13479        // A probability whose float conversion is zero has no depth, and a width
13480        // past a signed sixty four bit integer has no width, and both are the
13481        // same sentence.
13482        assert_eq!(
13483            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13484            "-CMS: invalid init arguments\r\n"
13485        );
13486        // And a sketch bigger than a gibibyte of counters is refused here where
13487        // the reference reserves address space nobody has touched, which is
13488        // D-47.
13489        assert_eq!(
13490            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13491            "-CMS: Insufficient memory to create the key\r\n"
13492        );
13493        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13494    }
13495
13496    /// Every pair is parsed before any of them lands, the counters saturate,
13497    /// and the count is a signed total of what was asked for.
13498    #[test]
13499    fn increments_are_parsed_whole_and_the_counters_saturate() {
13500        let mut f = Fixture::new();
13501        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13502        assert_eq!(
13503            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13504            "*2\r\n:3\r\n:4\r\n"
13505        );
13506        // An item that is incremented twice in one call sees its own first
13507        // increment in the reply to the second.
13508        assert_eq!(
13509            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13510            "*2\r\n:4\r\n:5\r\n"
13511        );
13512        // A bad number anywhere means nothing at all is applied.
13513        assert_eq!(
13514            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13515            "-CMS: Cannot parse number\r\n"
13516        );
13517        assert_eq!(
13518            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13519            "-CMS: Number cannot be negative\r\n"
13520        );
13521        assert_eq!(
13522            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13523            "*2\r\n:5\r\n:4\r\n"
13524        );
13525        // The counters stop at four billion and the item that stopped says so in
13526        // its own slot while the one beside it answers a number.
13527        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13528        assert_eq!(
13529            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13530            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13531        );
13532        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13533        // The count is what was asked for rather than what landed, and it is
13534        // signed, so a big enough total comes back negative.
13535        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13536        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13537        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13538        assert_eq!(
13539            f.run(&[b"CMS.INFO", b"w"]),
13540            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13541        );
13542        // An odd number of arguments after the key is an arity error and not a
13543        // syntax one.
13544        assert!(
13545            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13546                .contains("wrong number of arguments")
13547        );
13548        assert_eq!(
13549            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13550            "-CMS: key does not exist\r\n"
13551        );
13552        assert_eq!(
13553            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13554            "-CMS: key does not exist\r\n"
13555        );
13556    }
13557
13558    /// A merge overwrites its destination, and it is worked out in full before
13559    /// any of it is written.
13560    #[test]
13561    fn a_merge_lands_whole_or_not_at_all() {
13562        let mut f = Fixture::new();
13563        for name in [&b"m1"[..], b"m2", b"dst"] {
13564            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13565        }
13566        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13567        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13568        assert_eq!(
13569            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13570            "+OK\r\n"
13571        );
13572        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13573        // Overwritten and not added to, so the same merge twice is the same
13574        // answer twice.
13575        assert_eq!(
13576            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13577            "+OK\r\n"
13578        );
13579        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13580        assert_eq!(
13581            f.run(&[
13582                b"CMS.MERGE",
13583                b"dst",
13584                b"2",
13585                b"m1",
13586                b"m2",
13587                b"WEIGHTS",
13588                b"2",
13589                b"3"
13590            ]),
13591            "+OK\r\n"
13592        );
13593        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13594        // A cell times a weight is checked wide rather than wrapped, so this is
13595        // a refusal and the destination is left exactly as it was.
13596        assert_eq!(
13597            f.run(&[
13598                b"CMS.MERGE",
13599                b"dst",
13600                b"1",
13601                b"m1",
13602                b"WEIGHTS",
13603                b"4611686018427387904"
13604            ]),
13605            "-CMS: MERGE overflow\r\n"
13606        );
13607        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13608        // The destination comes first, then the count, then the layout, then the
13609        // weights, then the sources one at a time.
13610        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
13611        assert_eq!(
13612            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
13613            "-CMS: key does not exist\r\n"
13614        );
13615        assert_eq!(
13616            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
13617            "-CMS: Number of keys must be positive\r\n"
13618        );
13619        assert_eq!(
13620            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
13621            "-CMS: wrong number of keys\r\n"
13622        );
13623        assert_eq!(
13624            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
13625            "-CMS: wrong number of keys/weights\r\n"
13626        );
13627        assert_eq!(
13628            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
13629            "-CMS: width/depth is not equal\r\n"
13630        );
13631        assert_eq!(
13632            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
13633            "-CMS: key does not exist\r\n"
13634        );
13635    }
13636
13637    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
13638    /// a sketch is refused by the two commands that would have to serialise it.
13639    #[test]
13640    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13641        let mut f = Fixture::new();
13642        f.run(&[b"SET", b"s", b"text"]);
13643        for cmd in [
13644            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
13645            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
13646            vec![&b"CMS.QUERY"[..], b"s", b"a"],
13647            vec![&b"CMS.INFO"[..], b"s"],
13648            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
13649        ] {
13650            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13651            let reply = f.run(&cmd);
13652            // The two constructors see the key before anything else and say so
13653            // in the module's own words, and the rest are `WRONGTYPE`.
13654            assert!(
13655                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
13656                "{name}: {reply}"
13657            );
13658        }
13659        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
13660        // Redis refuses to copy a module key that has no copy callback, and
13661        // these are its words rather than ours. `DUMP` is the other half of
13662        // D-48: the reference has a payload for one of these and we do not.
13663        assert_eq!(
13664            f.run(&[b"COPY", b"c", b"c2"]),
13665            "-ERR not supported for this module key\r\n"
13666        );
13667        assert_eq!(
13668            f.run(&[b"DUMP", b"c"]),
13669            "-ERR DUMP is not supported for this module key\r\n"
13670        );
13671        // A graph is nobody's module and keeps its own sentence.
13672        f.run(&[b"G.NADD", b"g", b"a"]);
13673        assert_eq!(
13674            f.run(&[b"COPY", b"g", b"g2"]),
13675            "-ERR COPY is not supported for a graph\r\n"
13676        );
13677        assert_eq!(
13678            f.run(&[b"DUMP", b"g"]),
13679            "-ERR DUMP is not supported for a graph\r\n"
13680        );
13681        // Everything that does not need a byte shape works on a sketch key the
13682        // way it works on any other.
13683        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
13684        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
13685        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
13686        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
13687        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
13688    }
13689
13690    // ------------------------------------------------------------------ topk
13691
13692    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
13693    /// it looks at any of them.
13694    #[test]
13695    fn a_reserve_takes_three_arguments_or_six() {
13696        let mut f = Fixture::new();
13697        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
13698        assert_eq!(
13699            f.run(&[b"TOPK.INFO", b"t"]),
13700            "*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"
13701        );
13702        // Four arguments and five are an arity error rather than a defaulted
13703        // depth or decay.
13704        for cmd in [
13705            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
13706            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
13707        ] {
13708            assert!(f.run(&cmd).contains("wrong number of arguments"));
13709        }
13710        assert_eq!(
13711            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
13712            "+OK\r\n"
13713        );
13714        // The key is checked first, so a reserve with nothing else right at a
13715        // key that is taken still says the key is taken.
13716        assert_eq!(
13717            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
13718            "-TopK: key already exists\r\n"
13719        );
13720        assert_eq!(
13721            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
13722            "-TopK: invalid k\r\n"
13723        );
13724        assert_eq!(
13725            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
13726            "-TopK: invalid width\r\n"
13727        );
13728        assert_eq!(
13729            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
13730            "-TopK: invalid depth\r\n"
13731        );
13732        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
13733        assert_eq!(
13734            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
13735            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
13736        );
13737        assert_eq!(
13738            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
13739            "+OK\r\n"
13740        );
13741        // Past the cap, with the one sentence in the family that has a prefix.
13742        assert_eq!(
13743            f.run(&[
13744                b"TOPK.RESERVE",
13745                b"w",
13746                b"1",
13747                b"4294967295",
13748                b"4294967295",
13749                b"0.9"
13750            ]),
13751            "-ERR Insufficient memory to create topk data structure\r\n"
13752        );
13753    }
13754
13755    /// What the sketch keeps, and the three ways of asking about it.
13756    #[test]
13757    fn the_kept_set_is_what_query_and_list_answer_from() {
13758        let mut f = Fixture::new();
13759        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
13760        // A null an item while there is room, then the name of whatever was
13761        // pushed out.
13762        assert_eq!(
13763            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
13764            "*2\r\n$-1\r\n$-1\r\n"
13765        );
13766        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
13767        // Two slots are full and `c` arrives with a count of one, which is not
13768        // under the smallest kept count, so it takes that slot straight away.
13769        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
13770        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
13771        assert_eq!(
13772            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
13773            "*3\r\n:1\r\n:0\r\n:1\r\n"
13774        );
13775        // The table still counts what the kept set let go of.
13776        assert_eq!(
13777            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13778            "*3\r\n:11\r\n:1\r\n:6\r\n"
13779        );
13780        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
13781        assert_eq!(
13782            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
13783            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
13784        );
13785        // Any prefix of the keyword turns the counts on, the empty string
13786        // included, and only a longer word or a different one is refused.
13787        assert_eq!(
13788            f.run(&[b"TOPK.LIST", b"t", b"w"]),
13789            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13790        );
13791        assert_eq!(
13792            f.run(&[b"TOPK.LIST", b"t", b""]),
13793            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13794        );
13795        assert_eq!(
13796            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
13797            "-WITHCOUNT keyword expected\r\n"
13798        );
13799        // And the keyword is looked at before the key, so a missing key with a
13800        // bad keyword complains about the keyword.
13801        assert_eq!(
13802            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
13803            "-WITHCOUNT keyword expected\r\n"
13804        );
13805        assert_eq!(
13806            f.run(&[b"TOPK.LIST", b"missing"]),
13807            "-TopK: key does not exist\r\n"
13808        );
13809        // An item counted zero times is kept and not listed.
13810        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
13811        assert_eq!(
13812            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
13813            "*1\r\n$-1\r\n"
13814        );
13815        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
13816        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
13817    }
13818
13819    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
13820    /// before it counted, and the reply counts what it wrote.
13821    #[test]
13822    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
13823        let mut f = Fixture::new();
13824        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
13825        // Three pairs, the middle one bad: two elements come back, one of them
13826        // the error, and the array header says two rather than three. That last
13827        // part is D-51 and it is why a client here stays in step.
13828        assert_eq!(
13829            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
13830            format!(
13831                "*2\r\n$-1\r\n-{}\r\n",
13832                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
13833            )
13834        );
13835        assert_eq!(
13836            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13837            "*3\r\n:3\r\n:0\r\n:0\r\n"
13838        );
13839        // A hundred thousand is in and one more is out.
13840        assert_eq!(
13841            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
13842            "*1\r\n$-1\r\n"
13843        );
13844        assert!(
13845            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
13846                .contains("smaller or equal to 100,000")
13847        );
13848        // Pairs have to be pairs.
13849        assert!(
13850            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
13851                .contains("wrong number of arguments")
13852        );
13853        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
13854    }
13855
13856    /// The RESP3 shapes, which are the two the protocols disagree about.
13857    #[test]
13858    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
13859        let mut f = Fixture::new();
13860        f.run(&[b"HELLO", b"3"]);
13861        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
13862        f.run(&[b"TOPK.ADD", b"t", b"a"]);
13863        assert_eq!(
13864            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
13865            "*2\r\n#t\r\n#f\r\n"
13866        );
13867        // The count stays an integer on both protocols.
13868        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
13869        assert_eq!(
13870            f.run(&[b"TOPK.INFO", b"t"]),
13871            "%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"
13872        );
13873        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
13874    }
13875
13876    /// A top k key answers the module sentences the other sketch families
13877    /// answer, and its own word for its type.
13878    #[test]
13879    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13880        let mut f = Fixture::new();
13881        f.run(&[b"SET", b"s", b"text"]);
13882        for cmd in [
13883            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
13884            vec![&b"TOPK.ADD"[..], b"s", b"a"],
13885            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
13886            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
13887            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
13888            vec![&b"TOPK.LIST"[..], b"s"],
13889            vec![&b"TOPK.INFO"[..], b"s"],
13890        ] {
13891            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13892            let reply = f.run(&cmd);
13893            assert!(
13894                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
13895                "{name}: {reply}"
13896            );
13897        }
13898        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
13899        assert_eq!(
13900            f.run(&[b"COPY", b"t", b"t2"]),
13901            "-ERR not supported for this module key\r\n"
13902        );
13903        assert_eq!(
13904            f.run(&[b"DUMP", b"t"]),
13905            "-ERR DUMP is not supported for this module key\r\n"
13906        );
13907        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
13908        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
13909        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
13910        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
13911        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
13912        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
13913        // Every one of the six that is not the constructor says the same thing
13914        // about a key that is not there.
13915        assert_eq!(
13916            f.run(&[b"TOPK.INFO", b"t3"]),
13917            "-TopK: key does not exist\r\n"
13918        );
13919    }
13920
13921    // --------------------------------------------------------------- tdigest
13922
13923    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
13924    /// search rather than a lookup.
13925    #[test]
13926    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
13927        let mut f = Fixture::new();
13928        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
13929        // A hundred is the default and the capacity is six times it plus ten.
13930        assert_eq!(
13931            f.run(&[b"TDIGEST.INFO", b"t"]),
13932            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
13933             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
13934             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
13935        );
13936        assert_eq!(
13937            f.run(&[b"TDIGEST.CREATE", b"t"]),
13938            "-ERR T-Digest: key already exists\r\n"
13939        );
13940        // Three arguments is an arity error and not a missing keyword.
13941        assert!(
13942            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
13943                .contains("wrong number of arguments")
13944        );
13945        assert_eq!(
13946            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
13947            "+OK\r\n"
13948        );
13949        assert_eq!(
13950            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
13951            "+OK\r\n"
13952        );
13953        // The word is looked for across both trailing arguments and the number
13954        // is then read out of the last one whatever was found, so this looks for
13955        // a number inside the word `COMPRESSION` and does not find one.
13956        assert_eq!(
13957            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
13958            "-ERR T-Digest: error parsing compression parameter\r\n"
13959        );
13960        assert_eq!(
13961            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
13962            "-ERR T-Digest: wrong keyword\r\n"
13963        );
13964        assert_eq!(
13965            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
13966            "-ERR T-Digest: error parsing compression parameter\r\n"
13967        );
13968        assert_eq!(
13969            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
13970            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
13971        );
13972        // The reference's own ceiling, which is where the capacity stops fitting
13973        // in an int, and one past it.
13974        assert_eq!(
13975            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
13976            "-ERR T-Digest: allocation failed\r\n"
13977        );
13978        // And ours, which is a gibibyte of centroids and is D-52.
13979        assert_eq!(
13980            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
13981            "-ERR T-Digest: allocation failed\r\n"
13982        );
13983        // The key is checked before the arguments, so a bad compression at a key
13984        // that is already a digest still says the key is taken.
13985        assert_eq!(
13986            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
13987            "-ERR T-Digest: key already exists\r\n"
13988        );
13989    }
13990
13991    /// The four samples every note about this family is written against, and the
13992    /// answers a real 8.10.1 gives for them.
13993    #[test]
13994    fn the_quantile_family_answers_what_the_module_answers() {
13995        let mut f = Fixture::new();
13996        f.run(&[b"TDIGEST.CREATE", b"s"]);
13997        assert_eq!(
13998            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
13999            "+OK\r\n"
14000        );
14001        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
14002        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
14003        // The cdf of a sample is the weight below it plus half its own.
14004        assert_eq!(
14005            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
14006            "*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"
14007        );
14008        assert_eq!(
14009            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
14010            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
14011        );
14012        // Out of order, the walk restarts, and 0.5 answers 3 either way while
14013        // the two after it are read from the front again.
14014        assert_eq!(
14015            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
14016            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
14017        );
14018        assert_eq!(
14019            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14020            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
14021        );
14022        assert_eq!(
14023            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
14024            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
14025        );
14026        assert_eq!(
14027            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
14028            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
14029        );
14030        assert_eq!(
14031            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
14032            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
14033        );
14034        assert_eq!(
14035            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
14036            "$3\r\n2.5\r\n"
14037        );
14038        assert_eq!(
14039            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
14040            "$3\r\n2.5\r\n"
14041        );
14042        // The ranges, which are separate sentences from the parse failures.
14043        assert_eq!(
14044            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
14045            "-ERR T-Digest: quantile should be in [0,1]\r\n"
14046        );
14047        assert_eq!(
14048            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
14049            "-ERR T-Digest: error parsing quantile\r\n"
14050        );
14051        assert_eq!(
14052            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
14053            "-ERR T-Digest: error parsing cdf\r\n"
14054        );
14055        assert_eq!(
14056            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
14057            "-ERR T-Digest: error parsing value\r\n"
14058        );
14059        assert_eq!(
14060            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
14061            "-ERR T-Digest: rank needs to be non negative\r\n"
14062        );
14063        assert_eq!(
14064            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
14065            "-ERR T-Digest: error parsing rank\r\n"
14066        );
14067        // Both cuts have their own parse sentence and share the range one, and
14068        // equal cuts are refused rather than answering nothing.
14069        assert_eq!(
14070            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
14071            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
14072        );
14073        assert_eq!(
14074            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
14075            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
14076        );
14077        assert_eq!(
14078            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
14079            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
14080        );
14081        assert_eq!(
14082            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
14083            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
14084        );
14085    }
14086
14087    /// An empty digest answers every question, and answers most of them with
14088    /// something that is not a number.
14089    #[test]
14090    fn an_empty_digest_has_an_answer_for_everything() {
14091        let mut f = Fixture::new();
14092        f.run(&[b"TDIGEST.CREATE", b"e"]);
14093        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14094        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
14095        assert_eq!(
14096            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
14097            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14098        );
14099        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
14100        assert_eq!(
14101            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
14102            "$3\r\nnan\r\n"
14103        );
14104        // Minus two, which is a number no rank on a digest with samples in it
14105        // can ever be.
14106        assert_eq!(
14107            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
14108            "*2\r\n:-2\r\n:-2\r\n"
14109        );
14110        assert_eq!(
14111            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
14112            "*2\r\n:-2\r\n:-2\r\n"
14113        );
14114        assert_eq!(
14115            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
14116            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
14117        );
14118        // A reset puts a digest with samples back into exactly this state.
14119        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
14120        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
14121        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
14122        // Down to the compression count, so a reset digest and a fresh one of
14123        // the same compression report the same nine numbers.
14124        f.run(&[b"TDIGEST.CREATE", b"e2"]);
14125        assert_eq!(
14126            f.run(&[b"TDIGEST.INFO", b"e"]),
14127            f.run(&[b"TDIGEST.INFO", b"e2"])
14128        );
14129    }
14130
14131    /// The double parser is Redis's and not this engine's, and the two disagree
14132    /// at both ends of the range.
14133    #[test]
14134    fn a_sample_is_read_the_way_redis_reads_a_double() {
14135        let mut f = Fixture::new();
14136        f.run(&[b"TDIGEST.CREATE", b"a"]);
14137        // Overflow and underflow are parse failures rather than an infinity and
14138        // a zero, which is where this parts company with the rest of the engine.
14139        for bad in [
14140            &b"nan"[..],
14141            b"1e400",
14142            b"-1e400",
14143            b"1e309",
14144            b"1e-400",
14145            b"",
14146            b" 1",
14147            b"1 ",
14148            b"1e",
14149            b"--1",
14150        ] {
14151            assert_eq!(
14152                f.run(&[b"TDIGEST.ADD", b"a", bad]),
14153                "-ERR T-Digest: error parsing val parameter\r\n",
14154                "{}",
14155                String::from_utf8_lossy(bad)
14156            );
14157        }
14158        // An infinity spelled out parses and is then refused for being one, with
14159        // a different sentence.
14160        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
14161            assert_eq!(
14162                f.run(&[b"TDIGEST.ADD", b"a", word]),
14163                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
14164                "{}",
14165                String::from_utf8_lossy(word)
14166            );
14167        }
14168        // These all parse: hex, a bare point either side, and the smallest
14169        // subnormal the reference will take.
14170        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
14171            assert_eq!(
14172                f.run(&[b"TDIGEST.ADD", b"a", good]),
14173                "+OK\r\n",
14174                "{}",
14175                String::from_utf8_lossy(good)
14176            );
14177        }
14178        // Nothing landed from the failures, so six samples is what there is.
14179        assert!(
14180            f.run(&[b"TDIGEST.INFO", b"a"])
14181                .contains("Observations\r\n:6\r\n")
14182        );
14183        // Every value is parsed before any is added, so this whole command is a
14184        // no op.
14185        assert_eq!(
14186            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
14187            "-ERR T-Digest: error parsing val parameter\r\n"
14188        );
14189        assert!(
14190            f.run(&[b"TDIGEST.INFO", b"a"])
14191                .contains("Observations\r\n:6\r\n")
14192        );
14193    }
14194
14195    /// What a merge does to its destination, to its inputs and to the buffer
14196    /// split `TDIGEST.INFO` reports.
14197    #[test]
14198    fn a_merge_sweeps_the_destination_between_its_inputs() {
14199        let mut f = Fixture::new();
14200        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
14201        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
14202        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
14203        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
14204        assert_eq!(
14205            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
14206            "+OK\r\n"
14207        );
14208        // The destination did not exist, so the compression is the largest of
14209        // the inputs. The three from the first input were swept in before the
14210        // three from the second arrived, which is the one visible effect of the
14211        // reference folding one input at a time.
14212        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14213        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14214        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
14215        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
14216        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
14217        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
14218        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
14219        // Reading a source sweeps it too, so a merge writes to keys it only
14220        // reads from.
14221        assert!(
14222            f.run(&[b"TDIGEST.INFO", b"m1"])
14223                .contains("Merged nodes\r\n:3\r\n")
14224        );
14225        // Without OVERRIDE the destination joins its own inputs, so this takes
14226        // it to nine observations and keeps its own compression.
14227        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
14228        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14229        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
14230        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
14231        // With OVERRIDE the old destination is dropped and the compression goes
14232        // back to the largest of the inputs.
14233        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
14234        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
14235        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
14236        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
14237        // And COMPRESSION beats both.
14238        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
14239        assert!(
14240            f.run(&[b"TDIGEST.INFO", b"d"])
14241                .contains("Compression\r\n:500\r\n")
14242        );
14243        // Naming the destination as a source folds it in twice.
14244        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
14245        assert!(
14246            f.run(&[b"TDIGEST.INFO", b"d"])
14247                .contains("Observations\r\n:12\r\n")
14248        );
14249        // The arguments, in the order the reference checks them.
14250        assert_eq!(
14251            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
14252            "-ERR T-Digest: error parsing numkeys\r\n"
14253        );
14254        assert_eq!(
14255            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
14256            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
14257        );
14258        assert!(
14259            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
14260                .contains("wrong number of arguments")
14261        );
14262        assert!(
14263            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
14264                .contains("wrong number of arguments")
14265        );
14266        assert_eq!(
14267            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
14268            "-ERR T-Digest: wrong keyword\r\n"
14269        );
14270        // A source that is not there stops the whole thing, and the destination
14271        // is left as it was.
14272        assert_eq!(
14273            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
14274            "-ERR T-Digest: key does not exist\r\n"
14275        );
14276        assert!(
14277            f.run(&[b"TDIGEST.INFO", b"d"])
14278                .contains("Observations\r\n:12\r\n")
14279        );
14280        // A destination that is not there and is also named as a source is the
14281        // same sentence rather than an empty merge.
14282        assert_eq!(
14283            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14284            "-ERR T-Digest: key does not exist\r\n"
14285        );
14286    }
14287
14288    /// The RESP3 shapes, which are the two the protocols disagree about.
14289    #[test]
14290    fn a_digest_answers_doubles_and_a_map_on_resp3() {
14291        let mut f = Fixture::new();
14292        f.run(&[b"HELLO", b"3"]);
14293        f.run(&[b"TDIGEST.CREATE", b"s"]);
14294        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14295        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14296        assert_eq!(
14297            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14298            "*2\r\n,1\r\n,4\r\n"
14299        );
14300        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14301        // The two infinities and the NaN go out as the bare words.
14302        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14303        assert_eq!(
14304            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14305            "*1\r\n,-inf\r\n"
14306        );
14307        f.run(&[b"TDIGEST.CREATE", b"e"]);
14308        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14309        // The ranks stay integers on both protocols.
14310        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14311        // Every question above swept the buffer in, so the four samples are all
14312        // merged by now and the compression count says it happened once.
14313        assert_eq!(
14314            f.run(&[b"TDIGEST.INFO", b"s"]),
14315            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14316             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14317             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14318        );
14319    }
14320
14321    /// A t digest key answers the module sentences the other sketch families
14322    /// answer, and its own word for its type.
14323    #[test]
14324    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14325        let mut f = Fixture::new();
14326        f.run(&[b"SET", b"s", b"text"]);
14327        for cmd in [
14328            vec![&b"TDIGEST.CREATE"[..], b"s"],
14329            vec![&b"TDIGEST.RESET"[..], b"s"],
14330            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14331            vec![&b"TDIGEST.MIN"[..], b"s"],
14332            vec![&b"TDIGEST.MAX"[..], b"s"],
14333            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14334            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14335            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14336            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14337            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14338            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14339            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14340            vec![&b"TDIGEST.INFO"[..], b"s"],
14341        ] {
14342            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14343            let reply = f.run(&cmd);
14344            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14345        }
14346        // The merge checks its destination the same way, and its sources too.
14347        f.run(&[b"TDIGEST.CREATE", b"t"]);
14348        assert!(
14349            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14350                .starts_with("-WRONGTYPE")
14351        );
14352        assert!(
14353            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14354                .starts_with("-WRONGTYPE")
14355        );
14356        assert_eq!(
14357            f.run(&[b"COPY", b"t", b"t2"]),
14358            "-ERR not supported for this module key\r\n"
14359        );
14360        assert_eq!(
14361            f.run(&[b"DUMP", b"t"]),
14362            "-ERR DUMP is not supported for this module key\r\n"
14363        );
14364        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14365        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14366        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14367        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14368        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14369        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14370        // An empty digest is still a key, so the twelve that are not the
14371        // constructor all say the same thing once it is gone.
14372        assert_eq!(
14373            f.run(&[b"TDIGEST.INFO", b"t3"]),
14374            "-ERR T-Digest: key does not exist\r\n"
14375        );
14376        // The key is looked at before the arguments, so a bad argument at a key
14377        // that is not there still says the key is not there.
14378        assert_eq!(
14379            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14380            "-ERR T-Digest: key does not exist\r\n"
14381        );
14382    }
14383
14384    // -------------------------------------------------------------------- ts
14385
14386    /// A `TS.INFO` reply with the memory usage taken out of it.
14387    ///
14388    /// That number is what a series costs here rather than what one costs in the
14389    /// module, which is D-53, and it moves whenever the layout of a chunk does.
14390    /// Everything either side of it is the wire contract and is worth pinning
14391    /// down exactly, so the tests below check the whole reply with the one
14392    /// number lifted out.
14393    fn without_memory(reply: &str) -> String {
14394        let head = "+memoryUsage\r\n:";
14395        let at = reply.find(head).expect("every TS.INFO reports memory");
14396        let rest = &reply[at + head.len()..];
14397        let end = rest.find("\r\n").expect("and it is a whole number");
14398        format!("{}{}", &reply[..at + head.len()], &rest[end..])
14399    }
14400
14401    /// A series is made empty and still says it has a chunk, and the options are
14402    /// read before the key is looked at.
14403    #[test]
14404    fn a_series_is_made_empty_and_reports_on_itself() {
14405        let mut f = Fixture::new();
14406        assert_eq!(f.run(&[b"TS.CREATE", b"t"]), "+OK\r\n");
14407        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14408        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t"]), "$3\r\nraw\r\n");
14409        // Fourteen fields, so twenty eight elements. An empty series reports one
14410        // chunk and zero at both ends, and neither the chunk type nor the
14411        // duplicate policy is ever a nil.
14412        assert_eq!(
14413            without_memory(&f.run(&[b"TS.INFO", b"t"])),
14414            "*28\r\n\
14415             +totalSamples\r\n:0\r\n\
14416             +memoryUsage\r\n:\r\n\
14417             +firstTimestamp\r\n:0\r\n\
14418             +lastTimestamp\r\n:0\r\n\
14419             +retentionTime\r\n:0\r\n\
14420             +chunkCount\r\n:1\r\n\
14421             +chunkSize\r\n:4096\r\n\
14422             +chunkType\r\n+compressed\r\n\
14423             +duplicatePolicy\r\n+block\r\n\
14424             +labels\r\n*0\r\n\
14425             +sourceKey\r\n$-1\r\n\
14426             +rules\r\n*0\r\n\
14427             +ignoreMaxTimeDiff\r\n:0\r\n\
14428             +ignoreMaxValDiff\r\n$1\r\n0\r\n"
14429        );
14430        // A key that is already there is about the key whatever it holds, and
14431        // the existence is what is checked rather than the type.
14432        assert_eq!(
14433            f.run(&[b"TS.CREATE", b"t"]),
14434            "-ERR TSDB: key already exists\r\n"
14435        );
14436        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14437        assert_eq!(
14438            f.run(&[b"TS.CREATE", b"str"]),
14439            "-ERR TSDB: key already exists\r\n"
14440        );
14441        // But the arguments are read first, so a bad one at a key that is there
14442        // answers about the argument.
14443        assert_eq!(
14444            f.run(&[b"TS.CREATE", b"t", b"RETENTION", b"abc"]),
14445            "-ERR TSDB: Couldn't parse RETENTION\r\n"
14446        );
14447        // The seven that will not make a series say WRONGTYPE about a key
14448        // holding something else, where the two that would say a sentence.
14449        // The word is inside the sentence and not in front of it, because the
14450        // module writes its own error text and Redis puts ERR on the front of
14451        // anything a module writes.
14452        let wrong = "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
14453        assert_eq!(f.run(&[b"TS.INFO", b"str"]), wrong);
14454        assert_eq!(f.run(&[b"TS.GET", b"str"]), wrong);
14455        assert_eq!(f.run(&[b"TS.ALTER", b"str"]), wrong);
14456        assert_eq!(f.run(&[b"TS.DEL", b"str", b"0", b"1"]), wrong);
14457        assert_eq!(f.run(&[b"TS.INCRBY", b"str", b"1"]), wrong);
14458        assert_eq!(
14459            f.run(&[b"TS.ADD", b"str", b"1", b"1"]),
14460            "-ERR TSDB: the key is not a TSDB key\r\n"
14461        );
14462        // And the ones that will not make one say so about a key that is gone.
14463        assert_eq!(
14464            f.run(&[b"TS.INFO", b"nope"]),
14465            "-ERR TSDB: the key does not exist\r\n"
14466        );
14467        assert_eq!(
14468            f.run(&[b"TS.GET", b"nope"]),
14469            "-ERR TSDB: the key does not exist\r\n"
14470        );
14471        assert_eq!(
14472            f.run(&[b"TS.ALTER", b"nope"]),
14473            "-ERR TSDB: the key does not exist\r\n"
14474        );
14475        assert_eq!(
14476            f.run(&[b"TS.DEL", b"nope", b"1", b"2"]),
14477            "-ERR TSDB: the key does not exist\r\n"
14478        );
14479    }
14480
14481    /// Every option word, including the ones that are wrong, and the scan that
14482    /// finds them.
14483    #[test]
14484    fn the_options_are_a_keyword_scan_and_not_a_grammar() {
14485        let mut f = Fixture::new();
14486        assert_eq!(
14487            f.run(&[
14488                b"TS.CREATE",
14489                b"t",
14490                b"RETENTION",
14491                b"5000",
14492                b"ENCODING",
14493                b"UNCOMPRESSED",
14494                b"CHUNK_SIZE",
14495                b"128",
14496                b"DUPLICATE_POLICY",
14497                b"LAST",
14498                b"IGNORE",
14499                b"10",
14500                b"0.5",
14501                b"LABELS",
14502                b"room",
14503                b"kitchen"
14504            ]),
14505            "+OK\r\n"
14506        );
14507        let info = f.run(&[b"TS.INFO", b"t"]);
14508        assert!(info.contains("+retentionTime\r\n:5000\r\n"), "{info}");
14509        assert!(info.contains("+chunkSize\r\n:128\r\n"), "{info}");
14510        assert!(info.contains("+chunkType\r\n+uncompressed\r\n"), "{info}");
14511        assert!(info.contains("+duplicatePolicy\r\n+last\r\n"), "{info}");
14512        assert!(info.contains("+ignoreMaxTimeDiff\r\n:10\r\n"), "{info}");
14513        // A plain double here, where a sample value out of TS.GET is the
14514        // shortest digits that read back as the same number.
14515        assert!(
14516            info.contains("+ignoreMaxValDiff\r\n$3\r\n0.5\r\n"),
14517            "{info}"
14518        );
14519        assert!(
14520            info.contains("+labels\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n"),
14521            "{info}"
14522        );
14523
14524        // A word that is not an option is read past rather than refused.
14525        assert_eq!(f.run(&[b"TS.CREATE", b"junk", b"FOO"]), "+OK\r\n");
14526        // LABELS eats everything after it in pairs, and the later scans still
14527        // look inside what it ate, so this sets a retention and stores a label
14528        // called RETENTION at the same time.
14529        assert_eq!(
14530            f.run(&[
14531                b"TS.CREATE",
14532                b"g",
14533                b"LABELS",
14534                b"a",
14535                b"b",
14536                b"RETENTION",
14537                b"5"
14538            ]),
14539            "+OK\r\n"
14540        );
14541        let greedy = f.run(&[b"TS.INFO", b"g"]);
14542        assert!(greedy.contains("+retentionTime\r\n:5\r\n"), "{greedy}");
14543        assert!(
14544            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"),
14545            "{greedy}"
14546        );
14547
14548        // Every way an option can be wrong, in the order the module reads them.
14549        assert_eq!(
14550            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"a", b"b(c"]),
14551            "-ERR TSDB: Couldn't parse LABELS\r\n"
14552        );
14553        assert_eq!(
14554            f.run(&[b"TS.CREATE", b"e", b"LABELS", b"", b"b"]),
14555            "-ERR TSDB: Couldn't parse LABELS\r\n"
14556        );
14557        assert_eq!(
14558            f.run(&[b"TS.CREATE", b"e", b"RETENTION"]),
14559            "-ERR TSDB: Couldn't parse RETENTION\r\n"
14560        );
14561        // A retention below zero is one of the two the module writes with no
14562        // ERR in front of it, where one that is not a number gets one.
14563        assert_eq!(
14564            f.run(&[b"TS.CREATE", b"e", b"RETENTION", b"-1"]),
14565            "-TSDB: Couldn't parse RETENTION\r\n"
14566        );
14567        assert_eq!(
14568            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"abc"]),
14569            "-ERR TSDB: Couldn't parse CHUNK_SIZE\r\n"
14570        );
14571        assert_eq!(
14572            f.run(&[b"TS.CREATE", b"e", b"CHUNK_SIZE", b"100"]),
14573            "-ERR TSDB: CHUNK_SIZE value must be a multiple of 8 in the range [48 .. 1048576]\r\n"
14574        );
14575        assert_eq!(
14576            f.run(&[b"TS.CREATE", b"e", b"ENCODING", b"nope"]),
14577            "-ERR TSDB: unknown ENCODING parameter\r\n"
14578        );
14579        // And an ENCODING with nothing behind it is an arity error where every
14580        // other keyword in the same spot is a sentence.
14581        assert!(
14582            f.run(&[b"TS.CREATE", b"e", b"ENCODING"])
14583                .contains("wrong number of arguments for 'ts.create' command")
14584        );
14585        assert_eq!(
14586            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY"]),
14587            "-ERR TSDB: Couldn't parse DUPLICATE_POLICY\r\n"
14588        );
14589        assert_eq!(
14590            f.run(&[b"TS.CREATE", b"e", b"DUPLICATE_POLICY", b"nope"]),
14591            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14592        );
14593        assert_eq!(
14594            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"10"]),
14595            "-ERR TSDB: Couldn't parse IGNORE\r\n"
14596        );
14597        assert_eq!(
14598            f.run(&[b"TS.CREATE", b"e", b"IGNORE", b"-1", b"1"]),
14599            "-ERR TSDB: IGNORE arguments cannot be negative\r\n"
14600        );
14601        assert_eq!(f.run(&[b"EXISTS", b"e"]), ":0\r\n");
14602
14603        // An alter changes what was named and leaves the rest alone, and reads
14604        // an encoding only far enough to refuse a bad one.
14605        assert_eq!(f.run(&[b"TS.ALTER", b"t", b"RETENTION", b"9"]), "+OK\r\n");
14606        let after = f.run(&[b"TS.INFO", b"t"]);
14607        assert!(after.contains("+retentionTime\r\n:9\r\n"), "{after}");
14608        assert!(after.contains("+chunkSize\r\n:128\r\n"), "{after}");
14609        assert!(after.contains("+duplicatePolicy\r\n+last\r\n"), "{after}");
14610        assert_eq!(
14611            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"nope"]),
14612            "-ERR TSDB: unknown ENCODING parameter\r\n"
14613        );
14614        // An encoding it does take is still not applied.
14615        assert_eq!(
14616            f.run(&[b"TS.ALTER", b"t", b"ENCODING", b"COMPRESSED"]),
14617            "+OK\r\n"
14618        );
14619        assert!(
14620            f.run(&[b"TS.INFO", b"t"])
14621                .contains("+chunkType\r\n+uncompressed\r\n")
14622        );
14623    }
14624
14625    /// Samples go in, come back out and are refused for the reasons the module
14626    /// refuses them.
14627    #[test]
14628    fn samples_land_where_they_are_put_and_the_newest_comes_back() {
14629        let mut f = Fixture::new();
14630        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1.5"]), ":100\r\n");
14631        // The series was made on the way in.
14632        assert_eq!(f.run(&[b"TYPE", b"t"]), "+TSDB-TYPE\r\n");
14633        assert_eq!(f.run(&[b"TS.ADD", b"t", b"200", b"2"]), ":200\r\n");
14634        // A sample value goes out as a simple string of the shortest digits
14635        // that read back as the same number.
14636        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+2\r\n");
14637        assert_eq!(f.run(&[b"TS.ADD", b"t", b"300", b"1e300"]), ":300\r\n");
14638        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+1E300\r\n");
14639        // An empty series has no newest sample and answers an empty array
14640        // rather than a nil.
14641        assert_eq!(f.run(&[b"TS.CREATE", b"empty"]), "+OK\r\n");
14642        assert_eq!(f.run(&[b"TS.GET", b"empty"]), "*0\r\n");
14643
14644        // The value is read before the key, so a bad one against a key holding
14645        // a string is about the value.
14646        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14647        assert_eq!(
14648            f.run(&[b"TS.ADD", b"str", b"1", b".5"]),
14649            "-ERR TSDB: invalid value\r\n"
14650        );
14651        // The grammar is tighter than the one a number argument usually gets:
14652        // no leading plus, no bare fraction, no infinity and nothing that does
14653        // not fit.
14654        for bad in [
14655            &b".5"[..],
14656            b"1.",
14657            b"+1",
14658            b" 1",
14659            b"0x10",
14660            b"inf",
14661            b"1e400",
14662            b"--1",
14663            b"1e",
14664        ] {
14665            assert_eq!(
14666                f.run(&[b"TS.ADD", b"v", b"1", bad]),
14667                "-ERR TSDB: invalid value\r\n",
14668                "{}",
14669                String::from_utf8_lossy(bad)
14670            );
14671        }
14672        // And a reading that is not a number is one of three words.
14673        assert_eq!(f.run(&[b"TS.ADD", b"v", b"1", b"NaN"]), ":1\r\n");
14674
14675        // A timestamp that is not a number, and one that is and is below zero,
14676        // are two different sentences.
14677        assert_eq!(
14678            f.run(&[b"TS.ADD", b"t", b"abc", b"1"]),
14679            "-ERR TSDB: invalid timestamp\r\n"
14680        );
14681        assert_eq!(
14682            f.run(&[b"TS.ADD", b"t", b"-1", b"1"]),
14683            "-ERR TSDB: invalid timestamp, must be a nonnegative integer\r\n"
14684        );
14685
14686        // A repeated timestamp is blocked by default, and ON_DUPLICATE on the
14687        // command beats what the series was told.
14688        assert_eq!(
14689            f.run(&[b"TS.ADD", b"t", b"300", b"7"]),
14690            "-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"
14691        );
14692        assert_eq!(
14693            f.run(&[b"TS.ADD", b"t", b"300", b"7", b"ON_DUPLICATE", b"LAST"]),
14694            ":300\r\n"
14695        );
14696        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:300\r\n+7\r\n");
14697        // ON_DUPLICATE is only read when the key was already there, which is
14698        // why a policy word that is not a policy passes on a fresh key.
14699        assert_eq!(
14700            f.run(&[b"TS.ADD", b"fresh", b"1", b"1", b"ON_DUPLICATE", b"nope"]),
14701            ":1\r\n"
14702        );
14703        assert_eq!(
14704            f.run(&[b"TS.ADD", b"fresh", b"2", b"1", b"ON_DUPLICATE", b"nope"]),
14705            "-ERR TSDB: Unknown DUPLICATE_POLICY\r\n"
14706        );
14707
14708        // Retention is exact and it is checked before anything else happens, so
14709        // a sample landing behind the window is refused rather than trimmed.
14710        assert_eq!(f.run(&[b"TS.CREATE", b"r", b"RETENTION", b"50"]), "+OK\r\n");
14711        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1000", b"1"]), ":1000\r\n");
14712        assert_eq!(f.run(&[b"TS.ADD", b"r", b"960", b"1"]), ":960\r\n");
14713        assert_eq!(
14714            f.run(&[b"TS.ADD", b"r", b"940", b"1"]),
14715            "-ERR TSDB: Timestamp is older than retention\r\n"
14716        );
14717        // And the window trims as it moves.
14718        assert_eq!(f.run(&[b"TS.ADD", b"r", b"1100", b"1"]), ":1100\r\n");
14719        assert!(
14720            f.run(&[b"TS.INFO", b"r"])
14721                .contains("+totalSamples\r\n:1\r\n")
14722        );
14723
14724        // An ignore window drops a sample close enough to the newest one to be
14725        // uninteresting, and answers the newest timestamp so a client can tell.
14726        assert_eq!(
14727            f.run(&[
14728                b"TS.CREATE",
14729                b"i",
14730                b"DUPLICATE_POLICY",
14731                b"LAST",
14732                b"IGNORE",
14733                b"10",
14734                b"0.5"
14735            ]),
14736            "+OK\r\n"
14737        );
14738        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1000", b"1"]), ":1000\r\n");
14739        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"1.2"]), ":1000\r\n");
14740        assert_eq!(f.run(&[b"TS.ADD", b"i", b"1005", b"9"]), ":1005\r\n");
14741    }
14742
14743    /// Every triple in a `TS.MADD` is answered on its own, and none of them
14744    /// makes a series.
14745    #[test]
14746    fn a_madd_answers_each_triple_and_creates_nothing() {
14747        let mut f = Fixture::new();
14748        assert_eq!(f.run(&[b"TS.CREATE", b"a"]), "+OK\r\n");
14749        assert_eq!(f.run(&[b"TS.CREATE", b"b"]), "+OK\r\n");
14750        assert_eq!(
14751            f.run(&[
14752                b"TS.MADD", b"a", b"100", b"1", b"b", b"100", b"2", b"a", b"200", b"3"
14753            ]),
14754            "*3\r\n:100\r\n:100\r\n:200\r\n"
14755        );
14756        // A key that is not a series is an error in its own slot and the ones
14757        // after it still land.
14758        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14759        assert_eq!(
14760            f.run(&[
14761                b"TS.MADD", b"gone", b"1", b"1", b"str", b"1", b"1", b"a", b"300", b"4"
14762            ]),
14763            "*3\r\n\
14764             -ERR TSDB: the key is not a TSDB key\r\n\
14765             -ERR TSDB: the key is not a TSDB key\r\n\
14766             :300\r\n"
14767        );
14768        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
14769        // A bad value and a bad timestamp are answered in their slots too.
14770        assert_eq!(
14771            f.run(&[b"TS.MADD", b"a", b"400", b"zzz", b"a", b"abc", b"1"]),
14772            "*2\r\n-ERR TSDB: invalid value\r\n-ERR TSDB: invalid timestamp\r\n"
14773        );
14774        // And a list that is not made of triples is an arity error.
14775        assert!(
14776            f.run(&[b"TS.MADD", b"a", b"1", b"1", b"a"])
14777                .contains("wrong number of arguments for 'ts.madd' command")
14778        );
14779    }
14780
14781    /// The two increments, which only ever write forwards.
14782    #[test]
14783    fn an_increment_walks_the_newest_value_up_and_down() {
14784        let mut f = Fixture::new();
14785        assert_eq!(
14786            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14787            ":100\r\n"
14788        );
14789        assert_eq!(
14790            f.run(&[b"TS.INCRBY", b"t", b"5", b"TIMESTAMP", b"100"]),
14791            ":100\r\n"
14792        );
14793        // Two on one timestamp add up rather than collide, because the sample
14794        // goes in under the last policy whatever the series says.
14795        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n+10\r\n");
14796        assert_eq!(
14797            f.run(&[b"TS.DECRBY", b"t", b"3", b"TIMESTAMP", b"200"]),
14798            ":200\r\n"
14799        );
14800        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:200\r\n+7\r\n");
14801        // A timestamp behind the newest sample is the other of the two errors
14802        // the module writes with no ERR in front of it.
14803        assert_eq!(
14804            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP", b"150"]),
14805            "-TSDB: timestamp must be equal to or higher than the maximum existing timestamp\r\n"
14806        );
14807        // The increment goes through the ordinary number reader, so it takes
14808        // what a sample value will not and refuses a NaN that a sample value
14809        // takes.
14810        assert_eq!(
14811            f.run(&[b"TS.INCRBY", b"p", b"+5", b"TIMESTAMP", b"1"]),
14812            ":1\r\n"
14813        );
14814        assert_eq!(
14815            f.run(&[b"TS.INCRBY", b"q", b".5", b"TIMESTAMP", b"1"]),
14816            ":1\r\n"
14817        );
14818        assert_eq!(
14819            f.run(&[b"TS.INCRBY", b"t", b"nan"]),
14820            "-ERR TSDB: invalid increase/decrease value\r\n"
14821        );
14822        assert_eq!(
14823            f.run(&[b"TS.INCRBY", b"t", b"zzz"]),
14824            "-ERR TSDB: invalid increase/decrease value\r\n"
14825        );
14826        // A key holding something else is WRONGTYPE and is answered before the
14827        // number is looked at.
14828        assert_eq!(f.run(&[b"SET", b"str", b"x"]), "+OK\r\n");
14829        assert_eq!(
14830            f.run(&[b"TS.INCRBY", b"str", b"zzz"]),
14831            "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
14832        );
14833        // A TIMESTAMP keyword with nothing behind it is about the timestamp.
14834        // The reference reads one past the end of its own arguments here and
14835        // answers whatever was in that memory, so there is nothing to copy and
14836        // this answers the same thing every time.
14837        assert_eq!(
14838            f.run(&[b"TS.INCRBY", b"t", b"1", b"TIMESTAMP"]),
14839            "-ERR TSDB: invalid timestamp\r\n"
14840        );
14841        // And one behind a LABELS is a label name rather than the keyword, so
14842        // this lands at the clock rather than at 5.
14843        assert_eq!(
14844            f.run(&[b"TS.INCRBY", b"lab", b"1", b"LABELS", b"TIMESTAMP", b"5"]),
14845            format!(":{}\r\n", f.server.now_ms())
14846        );
14847        // Adding to a series whose newest value is not a number has no answer.
14848        assert_eq!(f.run(&[b"TS.ADD", b"n", b"1", b"nan"]), ":1\r\n");
14849        assert_eq!(
14850            f.run(&[b"TS.INCRBY", b"n", b"1", b"TIMESTAMP", b"2"]),
14851            "-ERR TSDB: cannot increment/decrement NaN value\r\n"
14852        );
14853    }
14854
14855    /// Deleting a span, both ends included.
14856    #[test]
14857    fn deleting_takes_out_a_span_and_answers_how_many_went() {
14858        let mut f = Fixture::new();
14859        for at in [b"100".as_slice(), b"200", b"300", b"400"] {
14860            f.run(&[b"TS.ADD", b"t", at, b"1"]);
14861        }
14862        assert_eq!(f.run(&[b"TS.DEL", b"t", b"200", b"300"]), ":2\r\n");
14863        assert!(
14864            f.run(&[b"TS.INFO", b"t"])
14865                .contains("+totalSamples\r\n:2\r\n")
14866        );
14867        // Ends the wrong way round take nothing out rather than being an error.
14868        assert_eq!(f.run(&[b"TS.DEL", b"t", b"400", b"100"]), ":0\r\n");
14869        // The two open ends.
14870        assert_eq!(f.run(&[b"TS.DEL", b"t", b"-", b"+"]), ":2\r\n");
14871        // A series everything has been deleted from keeps its chunk and reports
14872        // zero at both ends again.
14873        let empty = f.run(&[b"TS.INFO", b"t"]);
14874        assert!(empty.contains("+totalSamples\r\n:0\r\n"), "{empty}");
14875        assert!(empty.contains("+chunkCount\r\n:1\r\n"), "{empty}");
14876        assert!(empty.contains("+firstTimestamp\r\n:0\r\n"), "{empty}");
14877        assert!(empty.contains("+lastTimestamp\r\n:0\r\n"), "{empty}");
14878        assert_eq!(f.run(&[b"TS.DEL", b"t", b"0", b"1000"]), ":0\r\n");
14879        // The two ends have their own sentences.
14880        assert_eq!(
14881            f.run(&[b"TS.DEL", b"t", b"abc", b"5"]),
14882            "-ERR TSDB: wrong fromTimestamp\r\n"
14883        );
14884        assert_eq!(
14885            f.run(&[b"TS.DEL", b"t", b"5", b"abc"]),
14886            "-ERR TSDB: wrong toTimestamp\r\n"
14887        );
14888        assert_eq!(
14889            f.run(&[b"TS.DEL", b"t", b"-5", b"5"]),
14890            "-ERR TSDB: wrong fromTimestamp\r\n"
14891        );
14892    }
14893
14894    /// What RESP3 changes, which is the two places a number is written and the
14895    /// shape of `TS.INFO`.
14896    #[test]
14897    fn resp3_writes_a_sample_as_a_double_and_the_info_as_a_map() {
14898        let mut f = Fixture::new();
14899        f.out = Out::new(Proto::Resp3);
14900        assert_eq!(
14901            f.run(&[b"TS.CREATE", b"t", b"LABELS", b"room", b"kitchen"]),
14902            "+OK\r\n"
14903        );
14904        assert_eq!(f.run(&[b"TS.ADD", b"t", b"100", b"1e300"]), ":100\r\n");
14905        // A double rather than the simple string RESP2 gets.
14906        assert_eq!(f.run(&[b"TS.GET", b"t"]), "*2\r\n:100\r\n,1e+300\r\n");
14907        assert_eq!(
14908            without_memory(&f.run(&[b"TS.INFO", b"t"])),
14909            "%14\r\n\
14910             +totalSamples\r\n:1\r\n\
14911             +memoryUsage\r\n:\r\n\
14912             +firstTimestamp\r\n:100\r\n\
14913             +lastTimestamp\r\n:100\r\n\
14914             +retentionTime\r\n:0\r\n\
14915             +chunkCount\r\n:1\r\n\
14916             +chunkSize\r\n:4096\r\n\
14917             +chunkType\r\n+compressed\r\n\
14918             +duplicatePolicy\r\n+block\r\n\
14919             +labels\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
14920             +sourceKey\r\n_\r\n\
14921             +rules\r\n%0\r\n\
14922             +ignoreMaxTimeDiff\r\n:0\r\n\
14923             +ignoreMaxValDiff\r\n,0\r\n"
14924        );
14925    }
14926
14927    /// Reading a span back, both ways round, with the two ends and the three
14928    /// things that trim what comes out.
14929    #[test]
14930    fn a_range_walks_a_span_and_a_revrange_walks_it_backwards() {
14931        let mut f = Fixture::new();
14932        for (at, v) in [
14933            (b"100".as_slice(), b"1".as_slice()),
14934            (b"200", b"2"),
14935            (b"300", b"3"),
14936            (b"400", b"4"),
14937        ] {
14938            f.run(&[b"TS.ADD", b"t", at, v]);
14939        }
14940        assert_eq!(
14941            f.run(&[b"TS.RANGE", b"t", b"-", b"+"]),
14942            "*4\r\n*2\r\n:100\r\n+1\r\n*2\r\n:200\r\n+2\r\n\
14943             *2\r\n:300\r\n+3\r\n*2\r\n:400\r\n+4\r\n"
14944        );
14945        // Both ends are included.
14946        assert_eq!(
14947            f.run(&[b"TS.RANGE", b"t", b"150", b"350"]),
14948            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14949        );
14950        // Backwards, and the count takes from the front of what comes out, so
14951        // backwards it takes the newest.
14952        assert_eq!(
14953            f.run(&[b"TS.REVRANGE", b"t", b"-", b"+", b"COUNT", b"2"]),
14954            "*2\r\n*2\r\n:400\r\n+4\r\n*2\r\n:300\r\n+3\r\n"
14955        );
14956        // Ends the wrong way round are empty rather than an error.
14957        assert_eq!(f.run(&[b"TS.RANGE", b"t", b"400", b"100"]), "*0\r\n");
14958        // The two filters.
14959        assert_eq!(
14960            f.run(&[
14961                b"TS.RANGE",
14962                b"t",
14963                b"-",
14964                b"+",
14965                b"FILTER_BY_VALUE",
14966                b"2",
14967                b"3"
14968            ]),
14969            "*2\r\n*2\r\n:200\r\n+2\r\n*2\r\n:300\r\n+3\r\n"
14970        );
14971        assert_eq!(
14972            f.run(&[
14973                b"TS.RANGE",
14974                b"t",
14975                b"-",
14976                b"+",
14977                b"FILTER_BY_TS",
14978                b"100",
14979                b"400"
14980            ]),
14981            "*2\r\n*2\r\n:100\r\n+1\r\n*2\r\n:400\r\n+4\r\n"
14982        );
14983        // A word that is not an option is ignored wherever it sits.
14984        assert_eq!(
14985            f.run(&[
14986                b"TS.RANGE",
14987                b"t",
14988                b"-",
14989                b"+",
14990                b"ZZZ",
14991                b"FILTER_BY_TS",
14992                b"400"
14993            ]),
14994            "*1\r\n*2\r\n:400\r\n+4\r\n"
14995        );
14996        // `LATEST` means nothing until there is a compaction rule to follow.
14997        assert_eq!(
14998            f.run(&[b"TS.RANGE", b"t", b"-", b"+", b"LATEST", b"COUNT", b"1"]),
14999            "*1\r\n*2\r\n:100\r\n+1\r\n"
15000        );
15001    }
15002
15003    /// The bucketing, which is one column a reduction and a flat row.
15004    #[test]
15005    fn aggregation_puts_one_column_a_reduction_in_a_flat_row() {
15006        let mut f = Fixture::new();
15007        for (at, v) in [
15008            (b"100".as_slice(), b"1".as_slice()),
15009            (b"200", b"2"),
15010            (b"300", b"3"),
15011            (b"400", b"4"),
15012        ] {
15013            f.run(&[b"TS.ADD", b"t", at, v]);
15014        }
15015        assert_eq!(
15016            f.run(&[
15017                b"TS.RANGE",
15018                b"t",
15019                b"-",
15020                b"+",
15021                b"AGGREGATION",
15022                b"avg",
15023                b"200"
15024            ]),
15025            "*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"
15026        );
15027        // Three reductions is a row of four and not a row of two with a nested
15028        // three in it.
15029        assert_eq!(
15030            f.run(&[
15031                b"TS.RANGE",
15032                b"t",
15033                b"-",
15034                b"+",
15035                b"AGGREGATION",
15036                b"min,max,count",
15037                b"200"
15038            ]),
15039            "*3\r\n\
15040             *4\r\n:0\r\n+1\r\n+1\r\n+1\r\n\
15041             *4\r\n:200\r\n+2\r\n+3\r\n+2\r\n\
15042             *4\r\n:400\r\n+4\r\n+4\r\n+1\r\n"
15043        );
15044        // The timestamp a bucket is reported under.
15045        assert_eq!(
15046            f.run(&[
15047                b"TS.RANGE",
15048                b"t",
15049                b"-",
15050                b"+",
15051                b"AGGREGATION",
15052                b"avg",
15053                b"200",
15054                b"BUCKETTIMESTAMP",
15055                b"+"
15056            ]),
15057            "*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"
15058        );
15059        // An alignment moves where the bucket edges land.
15060        assert_eq!(
15061            f.run(&[
15062                b"TS.RANGE",
15063                b"t",
15064                b"100",
15065                b"400",
15066                b"ALIGN",
15067                b"100",
15068                b"AGGREGATION",
15069                b"sum",
15070                b"200"
15071            ]),
15072            "*2\r\n*2\r\n:100\r\n+3\r\n*2\r\n:300\r\n+7\r\n"
15073        );
15074        // A `COUNT` sitting where the reduction name belongs is that name, and
15075        // the scan for a real one starts again two words later.
15076        assert_eq!(
15077            f.run(&[
15078                b"TS.RANGE",
15079                b"t",
15080                b"-",
15081                b"+",
15082                b"AGGREGATION",
15083                b"count",
15084                b"200"
15085            ]),
15086            "*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"
15087        );
15088        assert_eq!(
15089            f.run(&[
15090                b"TS.RANGE",
15091                b"t",
15092                b"-",
15093                b"+",
15094                b"AGGREGATION",
15095                b"count",
15096                b"200",
15097                b"COUNT",
15098                b"1"
15099            ]),
15100            "*1\r\n*2\r\n:0\r\n+1\r\n"
15101        );
15102    }
15103
15104    /// `EMPTY` fills the gaps between readings and nothing else, and `last`
15105    /// carries two different things depending on which kind of empty it is.
15106    #[test]
15107    fn empty_fills_a_gap_and_last_carries_the_reading_before_it() {
15108        let mut f = Fixture::new();
15109        for (at, v) in [
15110            (b"0".as_slice(), b"1".as_slice()),
15111            (b"100", b"2"),
15112            (b"500", b"nan"),
15113            (b"600", b"3"),
15114        ] {
15115            f.run(&[b"TS.ADD", b"g", at, v]);
15116        }
15117        // Without `EMPTY` the buckets with nothing in them are not there at all,
15118        // and neither is the one holding only a reading that is not a number.
15119        assert_eq!(
15120            f.run(&[
15121                b"TS.RANGE",
15122                b"g",
15123                b"-",
15124                b"+",
15125                b"AGGREGATION",
15126                b"avg",
15127                b"100"
15128            ]),
15129            "*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"
15130        );
15131        // The sum of nothing is zero rather than not a number.
15132        assert_eq!(
15133            f.run(&[
15134                b"TS.RANGE",
15135                b"g",
15136                b"-",
15137                b"+",
15138                b"AGGREGATION",
15139                b"sum",
15140                b"100",
15141                b"EMPTY"
15142            ]),
15143            "*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\
15144             *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\
15145             *2\r\n:600\r\n+3\r\n"
15146        );
15147        // Buckets 200 through 400 have no readings at all and carry the reading
15148        // before the gap either way round. Bucket 500 has a reading that is not
15149        // a number, so it carries whatever the bucket before it in the reading
15150        // direction answered, which is 2 forwards and 3 backwards.
15151        assert_eq!(
15152            f.run(&[
15153                b"TS.RANGE",
15154                b"g",
15155                b"-",
15156                b"+",
15157                b"AGGREGATION",
15158                b"last",
15159                b"100",
15160                b"EMPTY"
15161            ]),
15162            "*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\
15163             *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\
15164             *2\r\n:600\r\n+3\r\n"
15165        );
15166        assert_eq!(
15167            f.run(&[
15168                b"TS.REVRANGE",
15169                b"g",
15170                b"-",
15171                b"+",
15172                b"AGGREGATION",
15173                b"last",
15174                b"100",
15175                b"EMPTY"
15176            ]),
15177            "*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\
15178             *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\
15179             *2\r\n:0\r\n+1\r\n"
15180        );
15181        // And a window that opens on that bucket has nothing in range before it
15182        // to carry, so it answers not a number.
15183        assert_eq!(
15184            f.run(&[
15185                b"TS.RANGE",
15186                b"g",
15187                b"500",
15188                b"600",
15189                b"AGGREGATION",
15190                b"last",
15191                b"100",
15192                b"EMPTY"
15193            ]),
15194            "*2\r\n*2\r\n:500\r\n+NaN\r\n*2\r\n:600\r\n+3\r\n"
15195        );
15196    }
15197
15198    /// The sentences a read answers when its options do not add up, which are
15199    /// the module's own word for word.
15200    #[test]
15201    fn a_range_says_what_the_module_says_when_the_options_do_not_add_up() {
15202        let mut f = Fixture::new();
15203        f.run(&[b"TS.ADD", b"t", b"100", b"1"]);
15204        f.run(&[b"SET", b"str", b"x"]);
15205        let cases: &[(&[&[u8]], &str)] = &[
15206            (
15207                &[b"TS.RANGE", b"t"],
15208                "-ERR wrong number of arguments for 'ts.range' command\r\n",
15209            ),
15210            // The key is resolved before a single option is read.
15211            (
15212                &[b"TS.RANGE", b"gone", b"-", b"+", b"COUNT", b"x"],
15213                "-ERR TSDB: the key does not exist\r\n",
15214            ),
15215            (
15216                &[b"TS.RANGE", b"str", b"-", b"+"],
15217                "-ERR WRONGTYPE Operation against a key holding the wrong kind of value\r\n",
15218            ),
15219            (
15220                &[b"TS.RANGE", b"t", b"abc", b"+"],
15221                "-ERR TSDB: wrong fromTimestamp\r\n",
15222            ),
15223            (
15224                &[b"TS.RANGE", b"t", b"-", b"abc"],
15225                "-ERR TSDB: wrong toTimestamp\r\n",
15226            ),
15227            (
15228                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT"],
15229                "-ERR TSDB: COUNT argument is missing\r\n",
15230            ),
15231            (
15232                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"x"],
15233                "-ERR TSDB: Couldn't parse COUNT\r\n",
15234            ),
15235            (
15236                &[b"TS.RANGE", b"t", b"-", b"+", b"COUNT", b"0"],
15237                "-ERR TSDB: Invalid COUNT value\r\n",
15238            ),
15239            (
15240                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg"],
15241                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15242            ),
15243            (
15244                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"x"],
15245                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15246            ),
15247            (
15248                &[
15249                    b"TS.RANGE",
15250                    b"t",
15251                    b"-",
15252                    b"+",
15253                    b"AGGREGATION",
15254                    b"nope",
15255                    b"100",
15256                ],
15257                "-ERR TSDB: Unknown aggregation type\r\n",
15258            ),
15259            (
15260                &[
15261                    b"TS.RANGE",
15262                    b"t",
15263                    b"-",
15264                    b"+",
15265                    b"AGGREGATION",
15266                    b"avg,,min",
15267                    b"100",
15268                ],
15269                "-ERR TSDB: Empty aggregation type in list\r\n",
15270            ),
15271            // The list of names is read before the width is looked at.
15272            (
15273                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"nope", b"0"],
15274                "-ERR TSDB: Unknown aggregation type\r\n",
15275            ),
15276            (
15277                &[b"TS.RANGE", b"t", b"-", b"+", b"AGGREGATION", b"avg", b"0"],
15278                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15279            ),
15280            (
15281                &[
15282                    b"TS.RANGE",
15283                    b"t",
15284                    b"-",
15285                    b"+",
15286                    b"AGGREGATION",
15287                    b"avg",
15288                    b"100",
15289                    b"X",
15290                    b"EMPTY",
15291                ],
15292                "-ERR TSDB: EMPTY flag should be the 3rd or 5th flag after AGGREGATION flag\r\n",
15293            ),
15294            (
15295                &[
15296                    b"TS.RANGE",
15297                    b"t",
15298                    b"-",
15299                    b"+",
15300                    b"AGGREGATION",
15301                    b"avg",
15302                    b"100",
15303                    b"BUCKETTIMESTAMP",
15304                    b"z",
15305                ],
15306                "-ERR TSDB: unknown BUCKETTIMESTAMP parameter\r\n",
15307            ),
15308            (
15309                &[
15310                    b"TS.RANGE",
15311                    b"t",
15312                    b"-",
15313                    b"+",
15314                    b"AGGREGATION",
15315                    b"avg",
15316                    b"100",
15317                    b"X",
15318                    b"Y",
15319                    b"BUCKETTIMESTAMP",
15320                    b"-",
15321                ],
15322                "-ERR TSDB: BUCKETTIMESTAMP flag should be the 3rd or 4th flag after \
15323                 AGGREGATION flag\r\n",
15324            ),
15325            (
15326                &[
15327                    b"TS.RANGE",
15328                    b"t",
15329                    b"-",
15330                    b"+",
15331                    b"ALIGN",
15332                    b"z",
15333                    b"AGGREGATION",
15334                    b"avg",
15335                    b"100",
15336                ],
15337                "-ERR TSDB: unknown ALIGN parameter\r\n",
15338            ),
15339            (
15340                &[b"TS.RANGE", b"t", b"-", b"+", b"ALIGN", b"5"],
15341                "-ERR TSDB: ALIGN parameter can only be used with AGGREGATION\r\n",
15342            ),
15343            (
15344                &[
15345                    b"TS.RANGE",
15346                    b"t",
15347                    b"-",
15348                    b"+",
15349                    b"ALIGN",
15350                    b"-",
15351                    b"AGGREGATION",
15352                    b"avg",
15353                    b"100",
15354                ],
15355                "-ERR TSDB: start alignment can only be used with explicit start timestamp\r\n",
15356            ),
15357            (
15358                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_VALUE", b"1"],
15359                "-ERR TSDB: FILTER_BY_VALUE one or more arguments are missing\r\n",
15360            ),
15361            (
15362                &[
15363                    b"TS.RANGE",
15364                    b"t",
15365                    b"-",
15366                    b"+",
15367                    b"FILTER_BY_VALUE",
15368                    b"x",
15369                    b"2",
15370                ],
15371                "-ERR TSDB: Couldn't parse MIN\r\n",
15372            ),
15373            (
15374                &[
15375                    b"TS.RANGE",
15376                    b"t",
15377                    b"-",
15378                    b"+",
15379                    b"FILTER_BY_VALUE",
15380                    b"1",
15381                    b"y",
15382                ],
15383                "-ERR TSDB: Couldn't parse MAX\r\n",
15384            ),
15385            (
15386                &[b"TS.RANGE", b"t", b"-", b"+", b"FILTER_BY_TS"],
15387                "-ERR TSDB: FILTER_BY_TS one or more arguments are missing\r\n",
15388            ),
15389        ];
15390        for (argv, want) in cases {
15391            let got = f.run(argv);
15392            assert_eq!(&got, want, "{:?}", argv.last());
15393        }
15394        // The one sentence here that is yo's own rather than the module's, which
15395        // is D-54. A read that would build more rows than yo will build is
15396        // refused instead of attempted.
15397        f.run(&[b"TS.ADD", b"wide", b"0", b"1"]);
15398        f.run(&[b"TS.ADD", b"wide", b"1000000000000", b"2"]);
15399        assert_eq!(
15400            f.run(&[
15401                b"TS.RANGE",
15402                b"wide",
15403                b"-",
15404                b"+",
15405                b"AGGREGATION",
15406                b"avg",
15407                b"1",
15408                b"EMPTY"
15409            ]),
15410            "-ERR TSDB: the requested range holds too many empty buckets\r\n"
15411        );
15412    }
15413
15414    /// What RESP3 changes on a read, which is only how a number is written.
15415    #[test]
15416    fn resp3_writes_a_read_value_as_a_double() {
15417        let mut f = Fixture::new();
15418        f.out = Out::new(Proto::Resp3);
15419        for (at, v) in [
15420            (b"0".as_slice(), b"1".as_slice()),
15421            (b"100", b"2"),
15422            (b"500", b"nan"),
15423            (b"600", b"3"),
15424        ] {
15425            f.run(&[b"TS.ADD", b"g", at, v]);
15426        }
15427        assert_eq!(
15428            f.run(&[
15429                b"TS.RANGE",
15430                b"g",
15431                b"0",
15432                b"100",
15433                b"AGGREGATION",
15434                b"avg,min",
15435                b"200"
15436            ]),
15437            "*1\r\n*3\r\n:0\r\n,1.5\r\n,1\r\n"
15438        );
15439        assert_eq!(
15440            f.run(&[
15441                b"TS.RANGE",
15442                b"g",
15443                b"500",
15444                b"600",
15445                b"AGGREGATION",
15446                b"last",
15447                b"100",
15448                b"EMPTY"
15449            ]),
15450            "*2\r\n*2\r\n:500\r\n,nan\r\n*2\r\n:600\r\n,3\r\n"
15451        );
15452    }
15453
15454    /// Two series with an overlap and a gap each, plus a third holding nothing,
15455    /// which is what the joined reads are measured against.
15456    fn joined() -> Fixture {
15457        let mut f = Fixture::new();
15458        f.run(&[b"TS.CREATE", b"z"]);
15459        for (at, v) in [
15460            (b"10".as_slice(), b"1".as_slice()),
15461            (b"20", b"2"),
15462            (b"40", b"4"),
15463            (b"50", b"5"),
15464        ] {
15465            f.run(&[b"TS.ADD", b"x", at, v]);
15466        }
15467        for (at, v) in [
15468            (b"20".as_slice(), b"20".as_slice()),
15469            (b"30", b"30"),
15470            (b"50", b"50"),
15471            (b"60", b"60"),
15472        ] {
15473            f.run(&[b"TS.ADD", b"y", at, v]);
15474        }
15475        f
15476    }
15477
15478    /// The joined read lines its keys up on the timestamp and writes a row as
15479    /// the timestamp and then a nested array of the columns, which is the one
15480    /// shape in the family that is not the flat pair.
15481    #[test]
15482    fn an_nrange_joins_its_keys_on_the_timestamp() {
15483        let mut f = joined();
15484        // One key still nests, so the shape does not depend on the count.
15485        assert_eq!(
15486            f.run(&[b"TS.NRANGE", b"1", b"x", b"-", b"+"]),
15487            "*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\
15488             *2\r\n:40\r\n*1\r\n+4\r\n*2\r\n:50\r\n*1\r\n+5\r\n"
15489        );
15490        // A key with no reading where another key has one writes NaN there.
15491        assert_eq!(
15492            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+"]),
15493            "*6\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n\
15494             *2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15495             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15496             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15497             *2\r\n:50\r\n*2\r\n+5\r\n+50\r\n\
15498             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15499        );
15500        // A series holding nothing is a column of NaN and never a row of its
15501        // own, and the same key twice answers twice.
15502        assert_eq!(
15503            f.run(&[b"TS.NRANGE", b"2", b"x", b"z", b"20", b"40"]),
15504            "*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"
15505        );
15506        assert_eq!(
15507            f.run(&[b"TS.NRANGE", b"2", b"x", b"x", b"40", b"50"]),
15508            "*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"
15509        );
15510        // COUNT is applied to the joined rows and not to each key, so backwards
15511        // it gives the newest joined row rather than the newest of each.
15512        assert_eq!(
15513            f.run(&[
15514                b"TS.NREVRANGE",
15515                b"2",
15516                b"x",
15517                b"y",
15518                b"-",
15519                b"+",
15520                b"COUNT",
15521                b"1"
15522            ]),
15523            "*1\r\n*2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15524        );
15525        assert_eq!(
15526            f.run(&[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"+", b"COUNT", b"1"]),
15527            "*1\r\n*2\r\n:10\r\n*2\r\n+1\r\n+NaN\r\n"
15528        );
15529        // The two sample filters are settled a key at a time, before the join.
15530        assert_eq!(
15531            f.run(&[
15532                b"TS.NRANGE",
15533                b"2",
15534                b"x",
15535                b"y",
15536                b"-",
15537                b"+",
15538                b"FILTER_BY_VALUE",
15539                b"2",
15540                b"30"
15541            ]),
15542            "*4\r\n*2\r\n:20\r\n*2\r\n+2\r\n+20\r\n\
15543             *2\r\n:30\r\n*2\r\n+NaN\r\n+30\r\n\
15544             *2\r\n:40\r\n*2\r\n+4\r\n+NaN\r\n\
15545             *2\r\n:50\r\n*2\r\n+5\r\n+NaN\r\n"
15546        );
15547    }
15548
15549    /// The aggregation on a joined read names one reduction a key and then the
15550    /// one bucket width, and each name may be a comma list, so a row can be
15551    /// wider than the key count.
15552    #[test]
15553    fn an_nrange_aggregation_names_one_reduction_a_key() {
15554        let mut f = joined();
15555        assert_eq!(
15556            f.run(&[
15557                b"TS.NRANGE",
15558                b"2",
15559                b"x",
15560                b"y",
15561                b"-",
15562                b"+",
15563                b"AGGREGATION",
15564                b"sum",
15565                b"sum",
15566                b"20"
15567            ]),
15568            "*4\r\n*2\r\n:0\r\n*2\r\n+1\r\n+NaN\r\n\
15569             *2\r\n:20\r\n*2\r\n+2\r\n+50\r\n\
15570             *2\r\n:40\r\n*2\r\n+9\r\n+50\r\n\
15571             *2\r\n:60\r\n*2\r\n+NaN\r\n+60\r\n"
15572        );
15573        // A comma list on the first key widens the row to three columns.
15574        assert_eq!(
15575            f.run(&[
15576                b"TS.NRANGE",
15577                b"2",
15578                b"x",
15579                b"y",
15580                b"-",
15581                b"+",
15582                b"AGGREGATION",
15583                b"sum,count",
15584                b"avg",
15585                b"20"
15586            ]),
15587            "*4\r\n*2\r\n:0\r\n*3\r\n+1\r\n+1\r\n+NaN\r\n\
15588             *2\r\n:20\r\n*3\r\n+2\r\n+1\r\n+25\r\n\
15589             *2\r\n:40\r\n*3\r\n+9\r\n+2\r\n+50\r\n\
15590             *2\r\n:60\r\n*3\r\n+NaN\r\n+NaN\r\n+60\r\n"
15591        );
15592        // Everything behind the width moves along with it, so BUCKETTIMESTAMP
15593        // sits one or two past the width whatever the key count is.
15594        assert_eq!(
15595            f.run(&[
15596                b"TS.NRANGE",
15597                b"2",
15598                b"x",
15599                b"y",
15600                b"-",
15601                b"+",
15602                b"AGGREGATION",
15603                b"avg",
15604                b"sum",
15605                b"100",
15606                b"EMPTY",
15607                b"BUCKETTIMESTAMP",
15608                b"end"
15609            ]),
15610            "*1\r\n*2\r\n:100\r\n*2\r\n+3\r\n+160\r\n"
15611        );
15612        // A COUNT landing in one of the name slots is a reduction name and not
15613        // the keyword, and the read then has no count at all.
15614        assert_eq!(
15615            f.run(&[
15616                b"TS.NRANGE",
15617                b"2",
15618                b"x",
15619                b"y",
15620                b"-",
15621                b"+",
15622                b"AGGREGATION",
15623                b"avg",
15624                b"COUNT",
15625                b"100"
15626            ]),
15627            "*1\r\n*2\r\n:0\r\n*2\r\n+3\r\n+4\r\n"
15628        );
15629    }
15630
15631    /// The sentences a joined read answers when it does not add up, which are
15632    /// the module's own and come out in the module's own order.
15633    #[test]
15634    fn an_nrange_says_what_the_module_says_when_it_does_not_add_up() {
15635        let mut f = joined();
15636        f.run(&[b"SET", b"str", b"hi"]);
15637        let bad_keys = "-ERR TSDB: numkeys must be a positive integer\r\n";
15638        let numkeys = "-ERR TSDB: the number of AGGREGATION arguments \
15639                       must be equal to numkeys\r\n";
15640        let cases: &[(&[&[u8]], &str)] = &[
15641            (&[b"TS.NRANGE", b"0", b"x", b"-", b"+"], bad_keys),
15642            (&[b"TS.NRANGE", b"-1", b"x", b"-", b"+"], bad_keys),
15643            (&[b"TS.NRANGE", b"abc", b"x", b"-", b"+"], bad_keys),
15644            // Not enough words behind the count for the keys and both ends of
15645            // the span, which is an arity error however many keys were named.
15646            (
15647                &[b"TS.NRANGE", b"2", b"x", b"-", b"+"],
15648                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15649            ),
15650            (
15651                &[b"TS.NRANGE", b"99", b"x", b"-", b"+"],
15652                "-ERR wrong number of arguments for 'ts.nrange' command\r\n",
15653            ),
15654            // The reduction names are read before the two ends of the span,
15655            // which no other option is.
15656            (
15657                &[
15658                    b"TS.NRANGE",
15659                    b"2",
15660                    b"x",
15661                    b"y",
15662                    b"abc",
15663                    b"+",
15664                    b"AGGREGATION",
15665                    b"nope",
15666                    b"sum",
15667                    b"100",
15668                ],
15669                "-ERR TSDB: Unknown aggregation type\r\n",
15670            ),
15671            (
15672                &[b"TS.NRANGE", b"2", b"x", b"y", b"abc", b"+"],
15673                "-ERR TSDB: wrong fromTimestamp\r\n",
15674            ),
15675            (
15676                &[b"TS.NRANGE", b"2", b"x", b"y", b"-", b"abc"],
15677                "-ERR TSDB: wrong toTimestamp\r\n",
15678            ),
15679            // A name slot that is missing or holds a number is the count
15680            // sentence, and a width slot that is itself a reduction name is
15681            // that sentence as well.
15682            (
15683                &[
15684                    b"TS.NRANGE",
15685                    b"2",
15686                    b"x",
15687                    b"y",
15688                    b"-",
15689                    b"+",
15690                    b"AGGREGATION",
15691                    b"avg",
15692                ],
15693                numkeys,
15694            ),
15695            (
15696                &[
15697                    b"TS.NRANGE",
15698                    b"2",
15699                    b"x",
15700                    b"y",
15701                    b"-",
15702                    b"+",
15703                    b"AGGREGATION",
15704                    b"100",
15705                    b"sum",
15706                    b"100",
15707                ],
15708                numkeys,
15709            ),
15710            (
15711                &[
15712                    b"TS.NRANGE",
15713                    b"2",
15714                    b"x",
15715                    b"y",
15716                    b"-",
15717                    b"+",
15718                    b"AGGREGATION",
15719                    b"avg",
15720                    b"sum",
15721                    b"sum",
15722                    b"100",
15723                ],
15724                numkeys,
15725            ),
15726            (
15727                &[
15728                    b"TS.NRANGE",
15729                    b"2",
15730                    b"x",
15731                    b"y",
15732                    b"-",
15733                    b"+",
15734                    b"AGGREGATION",
15735                    b"avg",
15736                    b"sum",
15737                    b"abc",
15738                ],
15739                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15740            ),
15741            (
15742                &[
15743                    b"TS.NRANGE",
15744                    b"2",
15745                    b"x",
15746                    b"y",
15747                    b"-",
15748                    b"+",
15749                    b"AGGREGATION",
15750                    b"avg",
15751                    b"sum",
15752                    b"0",
15753                ],
15754                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
15755            ),
15756            // With one key none of that applies and the plain parser runs, so a
15757            // lone width is a missing width rather than a count mismatch.
15758            (
15759                &[b"TS.NRANGE", b"1", b"x", b"-", b"+", b"AGGREGATION", b"100"],
15760                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
15761            ),
15762            (
15763                &[
15764                    b"TS.NRANGE",
15765                    b"1",
15766                    b"x",
15767                    b"-",
15768                    b"+",
15769                    b"AGGREGATION",
15770                    b"100",
15771                    b"200",
15772                ],
15773                "-ERR TSDB: Unknown aggregation type\r\n",
15774            ),
15775            // The keys come last and in the order they were named.
15776            (
15777                &[b"TS.NRANGE", b"2", b"x", b"nope", b"-", b"+"],
15778                "-ERR TSDB: the key does not exist\r\n",
15779            ),
15780            (
15781                &[b"TS.NRANGE", b"2", b"str", b"nope", b"-", b"+"],
15782                "-ERR WRONGTYPE Operation against a key \
15783                 holding the wrong kind of value\r\n",
15784            ),
15785        ];
15786        for (argv, want) in cases {
15787            let got = f.run(argv);
15788            assert_eq!(&got, want, "{argv:?}");
15789        }
15790    }
15791
15792    /// `TS.READ`, which is a key, one timestamp and everything from there on.
15793    #[test]
15794    fn a_read_walks_from_a_timestamp_to_the_end_of_the_series() {
15795        let mut f = joined();
15796        assert_eq!(
15797            f.run(&[b"TS.READ", b"x", b"-"]),
15798            "*4\r\n*2\r\n:10\r\n+1\r\n*2\r\n:20\r\n+2\r\n\
15799             *2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15800        );
15801        // A plus is the last sample on its own, and a timestamp between two
15802        // samples starts at the one behind it.
15803        assert_eq!(
15804            f.run(&[b"TS.READ", b"x", b"+"]),
15805            "*1\r\n*2\r\n:50\r\n+5\r\n"
15806        );
15807        assert_eq!(
15808            f.run(&[b"TS.READ", b"x", b"25"]),
15809            "*2\r\n*2\r\n:40\r\n+4\r\n*2\r\n:50\r\n+5\r\n"
15810        );
15811        // Past the end, a series holding nothing and a key that is not there
15812        // are all the empty array rather than an error.
15813        assert_eq!(f.run(&[b"TS.READ", b"x", b"99"]), "*0\r\n");
15814        assert_eq!(f.run(&[b"TS.READ", b"z", b"-"]), "*0\r\n");
15815        assert_eq!(f.run(&[b"TS.READ", b"z", b"+"]), "*0\r\n");
15816        assert_eq!(f.run(&[b"TS.READ", b"nope", b"-"]), "*0\r\n");
15817        // The timestamp refusal goes out with nothing in front of it, and a key
15818        // holding something else answers the bare WRONGTYPE rather than the
15819        // module's prefixed one, both unlike the rest of the family.
15820        assert_eq!(
15821            f.run(&[b"TS.READ", b"x", b"abc"]),
15822            "-TSDB: invalid timestamp\r\n"
15823        );
15824        assert_eq!(
15825            f.run(&[b"TS.READ", b"x", b"-1"]),
15826            "-TSDB: invalid timestamp\r\n"
15827        );
15828        f.run(&[b"SET", b"str", b"hi"]);
15829        assert_eq!(
15830            f.run(&[b"TS.READ", b"str", b"-"]),
15831            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
15832        );
15833        // Anything other than exactly three words is an arity error, so there
15834        // is nowhere to put an option even though the table says minus three.
15835        assert_eq!(
15836            f.run(&[b"TS.READ", b"x"]),
15837            "-ERR wrong number of arguments for 'ts.read' command\r\n"
15838        );
15839        assert_eq!(
15840            f.run(&[b"TS.READ", b"x", b"-", b"COUNT", b"1"]),
15841            "-ERR wrong number of arguments for 'ts.read' command\r\n"
15842        );
15843    }
15844
15845    /// The keys of a joined read sit behind a count, so `COMMAND GETKEYS` has
15846    /// to read the count to find them.
15847    #[test]
15848    fn getkeys_reads_the_count_of_a_joined_read() {
15849        let mut f = Fixture::new();
15850        assert_eq!(
15851            f.run(&[
15852                b"COMMAND",
15853                b"GETKEYS",
15854                b"TS.NRANGE",
15855                b"2",
15856                b"a",
15857                b"b",
15858                b"-",
15859                b"+"
15860            ]),
15861            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
15862        );
15863        assert_eq!(
15864            f.run(&[
15865                b"COMMAND",
15866                b"GETKEYS",
15867                b"TS.NREVRANGE",
15868                b"1",
15869                b"a",
15870                b"-",
15871                b"+"
15872            ]),
15873            "*1\r\n$1\r\na\r\n"
15874        );
15875        // A count of zero, or one too large for the words that follow it, is
15876        // the server's own refusal and not the module's.
15877        for n in [b"0".as_slice(), b"9", b"abc"] {
15878            assert_eq!(
15879                f.run(&[b"COMMAND", b"GETKEYS", b"TS.NRANGE", n, b"a", b"-", b"+"]),
15880                "-ERR Invalid arguments specified for command\r\n"
15881            );
15882        }
15883    }
15884
15885    /// The five series every test of the label surface works against.
15886    fn labelled() -> Fixture {
15887        let mut f = Fixture::new();
15888        f.run(&[
15889            b"TS.CREATE",
15890            b"a",
15891            b"LABELS",
15892            b"room",
15893            b"kitchen",
15894            b"x",
15895            b"1",
15896        ]);
15897        f.run(&[
15898            b"TS.CREATE",
15899            b"b",
15900            b"LABELS",
15901            b"room",
15902            b"bedroom",
15903            b"x",
15904            b"2",
15905        ]);
15906        f.run(&[b"TS.CREATE", b"c", b"LABELS", b"room", b"kitchen"]);
15907        f.run(&[b"TS.CREATE", b"d"]);
15908        f.run(&[b"TS.CREATE", b"e", b"LABELS", b"r", b"bb", b"r", b"b"]);
15909        f.run(&[b"TS.ADD", b"a", b"100", b"1.5"]);
15910        f.run(&[b"TS.ADD", b"b", b"200", b"2"]);
15911        f
15912    }
15913
15914    /// The filter grammar, which is four steps and a `strtok` rather than a
15915    /// grammar, and which every command that searches on labels shares.
15916    #[test]
15917    fn a_filter_is_taken_apart_the_way_the_module_takes_one_apart() {
15918        let mut f = labelled();
15919        let cases: &[(&[&[u8]], &str)] = &[
15920            // The plain forms, and the order the answer comes back in, which is
15921            // by key name and not by anything the series remembers.
15922            (
15923                &[b"TS.QUERYINDEX", b"room=kitchen"],
15924                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15925            ),
15926            (
15927                &[b"TS.QUERYINDEX", b"room=kitchen", b"x=1"],
15928                "*1\r\n$1\r\na\r\n",
15929            ),
15930            (
15931                &[b"TS.QUERYINDEX", b"room=(kitchen,bedroom)"],
15932                "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n",
15933            ),
15934            // An empty list still counts as something that says which series to
15935            // take, it just never takes any.
15936            (&[b"TS.QUERYINDEX", b"room=()"], "*0\r\n"),
15937            // Absent and present, neither of which stands on its own.
15938            (
15939                &[b"TS.QUERYINDEX", b"x=", b"room=kitchen"],
15940                "*1\r\n$1\r\nc\r\n",
15941            ),
15942            (
15943                &[b"TS.QUERYINDEX", b"room=kitchen", b"x!="],
15944                "*1\r\n$1\r\na\r\n",
15945            ),
15946            (
15947                &[b"TS.QUERYINDEX", b"room!=kitchen", b"x!="],
15948                "-ERR TSDB: please provide at least one matcher\r\n",
15949            ),
15950            // A run of separators is one separator and everything past the
15951            // second field is dropped, so all three of these ask one question.
15952            (
15953                &[b"TS.QUERYINDEX", b"room==kitchen"],
15954                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15955            ),
15956            (
15957                &[b"TS.QUERYINDEX", b"room=kitchen=zz"],
15958                "*2\r\n$1\r\na\r\n$1\r\nc\r\n",
15959            ),
15960            (&[b"TS.QUERYINDEX", b"room!!=kitchen", b"x=1"], "*0\r\n"),
15961            // A bracket is only a list when it sits straight behind the
15962            // separator, and then the label in front of it has to be there.
15963            (&[b"TS.QUERYINDEX", b"()=1"], "*0\r\n"),
15964            (
15965                &[b"TS.QUERYINDEX", b"=(1)"],
15966                "-ERR TSDB: failed parsing labels\r\n",
15967            ),
15968            (
15969                &[b"TS.QUERYINDEX", b"room=(kitchen,)"],
15970                "-ERR TSDB: failed parsing labels\r\n",
15971            ),
15972            (
15973                &[b"TS.QUERYINDEX", b"room=(kitchen"],
15974                "-ERR TSDB: failed parsing labels\r\n",
15975            ),
15976            (&[b"TS.QUERYINDEX", b"room=x()"], "*0\r\n"),
15977            (
15978                &[b"TS.QUERYINDEX", b"nonsense"],
15979                "-ERR TSDB: failed parsing labels\r\n",
15980            ),
15981            // Nothing here says which series to take.
15982            (
15983                &[b"TS.QUERYINDEX", b"room!=kitchen"],
15984                "-ERR TSDB: please provide at least one matcher\r\n",
15985            ),
15986            // Names and values are both compared byte for byte.
15987            (&[b"TS.QUERYINDEX", b"ROOM=kitchen"], "*0\r\n"),
15988            (&[b"TS.QUERYINDEX", b"room=KITCHEN"], "*0\r\n"),
15989            (
15990                &[b"TS.QUERYINDEX"],
15991                "-ERR wrong number of arguments for 'ts.queryindex' command\r\n",
15992            ),
15993        ];
15994        for (argv, want) in cases {
15995            let got = f.run(argv);
15996            assert_eq!(&got, want, "{:?}", argv.last());
15997        }
15998    }
15999
16000    /// `TS.QUERYLABELS`, whose filter is the one that is allowed to be missing.
16001    #[test]
16002    fn querylabels_says_which_names_are_worn_and_what_they_are_set_to() {
16003        let mut f = labelled();
16004        let cases: &[(&[&[u8]], &str)] = &[
16005            (
16006                &[b"TS.QUERYLABELS", b"LABELS"],
16007                "*3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16008            ),
16009            (
16010                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=kitchen"],
16011                "*2\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16012            ),
16013            (
16014                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16015                "*2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16016            ),
16017            // The series wearing `r` twice contributes the smaller of the two
16018            // here, which is not the one it was written down as first.
16019            (&[b"TS.QUERYLABELS", b"VALUES", b"r"], "*1\r\n$1\r\nb\r\n"),
16020            (&[b"TS.QUERYLABELS", b"VALUES", b"nolabel"], "*0\r\n"),
16021            (
16022                &[b"TS.QUERYLABELS", b"VALUES"],
16023                "-ERR wrong number of arguments for 'ts.querylabels' command\r\n",
16024            ),
16025            (
16026                &[b"TS.QUERYLABELS", b"ZZZ"],
16027                "-ERR TSDB: unknown subtype, must be one of LABELS|VALUES\r\n",
16028            ),
16029            (
16030                &[b"TS.QUERYLABELS", b"LABELS", b"ZZZ"],
16031                "-ERR TSDB: unknown argument, expected FILTER\r\n",
16032            ),
16033            (
16034                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER"],
16035                "-ERR TSDB: FILTER given with no filter expressions\r\n",
16036            ),
16037            // With no filter at all every series is taken, which is why the
16038            // first case here answers about `r` as well. A filter that is there
16039            // still has to say which series to take.
16040            (
16041                &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room!=kitchen"],
16042                "-ERR TSDB: please provide at least one matcher\r\n",
16043            ),
16044            (
16045                &[
16046                    b"TS.QUERYLABELS",
16047                    b"LABELS",
16048                    b"FILTER",
16049                    b"room=kitchen",
16050                    b"x=",
16051                ],
16052                "*1\r\n$4\r\nroom\r\n",
16053            ),
16054        ];
16055        for (argv, want) in cases {
16056            let got = f.run(argv);
16057            assert_eq!(&got, want, "{:?}", argv.last());
16058        }
16059    }
16060
16061    /// `TS.MGET`, the newest sample of every series a filter takes, and the two
16062    /// ways of asking for the labels back alongside it.
16063    #[test]
16064    fn mget_writes_the_newest_sample_and_the_labels_that_were_asked_for() {
16065        let mut f = labelled();
16066        let cases: &[(&[&[u8]], &str)] = &[
16067            // A series with no samples writes an empty array where the sample
16068            // goes rather than dropping out of the reply.
16069            (
16070                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16071                "*2\r\n*3\r\n$1\r\na\r\n*0\r\n*2\r\n:100\r\n+1.5\r\n\
16072                 *3\r\n$1\r\nc\r\n*0\r\n*0\r\n",
16073            ),
16074            (
16075                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16076                "*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\
16077                 *2\r\n$1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n+1.5\r\n\
16078                 *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",
16079            ),
16080            // A selected label the series does not wear is a nil, not a gap.
16081            (
16082                &[
16083                    b"TS.MGET",
16084                    b"SELECTED_LABELS",
16085                    b"x",
16086                    b"FILTER",
16087                    b"room=kitchen",
16088                ],
16089                "*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\
16090                 *2\r\n:100\r\n+1.5\r\n\
16091                 *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",
16092            ),
16093            // The other half of the duplicated name rule. This one takes the
16094            // first written down where `TS.QUERYLABELS` takes the smallest.
16095            (
16096                &[b"TS.MGET", b"SELECTED_LABELS", b"r", b"FILTER", b"r=b"],
16097                "*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",
16098            ),
16099            (
16100                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16101                "*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\
16102                 *2\r\n$1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16103            ),
16104            // A word that is not an option is ignored, but a missing `FILTER`
16105            // is an arity error whatever else was written.
16106            (
16107                &[b"TS.MGET", b"ZZZ", b"FILTER", b"room=bedroom"],
16108                "*1\r\n*3\r\n$1\r\nb\r\n*0\r\n*2\r\n:200\r\n+2\r\n",
16109            ),
16110            (
16111                &[b"TS.MGET", b"a", b"b", b"c"],
16112                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16113            ),
16114            (
16115                &[b"TS.MGET", b"FILTER"],
16116                "-ERR wrong number of arguments for 'ts.mget' command\r\n",
16117            ),
16118            // Both keyword checks happen before the filter is read, and the two
16119            // sentences spell the second keyword without its `ED`.
16120            (
16121                &[
16122                    b"TS.MGET",
16123                    b"WITHLABELS",
16124                    b"SELECTED_LABELS",
16125                    b"x",
16126                    b"FILTER",
16127                    b"bad",
16128                ],
16129                "-ERR TSDB: cannot accept WITHLABELS and SELECT_LABELS together\r\n",
16130            ),
16131            (
16132                &[b"TS.MGET", b"SELECTED_LABELS", b"FILTER", b"bad"],
16133                "-ERR TSDB: SELECT_LABELS should have at least 1 parameter\r\n",
16134            ),
16135        ];
16136        for (argv, want) in cases {
16137            let got = f.run(argv);
16138            assert_eq!(&got, want, "{:?}", argv.last());
16139        }
16140    }
16141
16142    /// What RESP3 changes across the label surface, which is a set where there
16143    /// was an array and a map where there was a pair of them.
16144    #[test]
16145    fn resp3_writes_the_label_surface_as_sets_and_maps() {
16146        let mut f = labelled();
16147        f.out = Out::new(Proto::Resp3);
16148        let cases: &[(&[&[u8]], &str)] = &[
16149            (
16150                &[b"TS.QUERYINDEX", b"room=kitchen"],
16151                "~2\r\n$1\r\na\r\n$1\r\nc\r\n",
16152            ),
16153            (
16154                &[b"TS.QUERYLABELS", b"LABELS"],
16155                "~3\r\n$1\r\nr\r\n$4\r\nroom\r\n$1\r\nx\r\n",
16156            ),
16157            (
16158                &[b"TS.QUERYLABELS", b"VALUES", b"room"],
16159                "~2\r\n$7\r\nbedroom\r\n$7\r\nkitchen\r\n",
16160            ),
16161            // The key stops being the first of three and becomes the map key,
16162            // and the labels stop being pairs and become a map of their own.
16163            (
16164                &[b"TS.MGET", b"FILTER", b"room=kitchen"],
16165                "%2\r\n$1\r\na\r\n*2\r\n%0\r\n*2\r\n:100\r\n,1.5\r\n\
16166                 $1\r\nc\r\n*2\r\n%0\r\n*0\r\n",
16167            ),
16168            (
16169                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=kitchen"],
16170                "%2\r\n$1\r\na\r\n*2\r\n%2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16171                 $1\r\nx\r\n$1\r\n1\r\n*2\r\n:100\r\n,1.5\r\n\
16172                 $1\r\nc\r\n*2\r\n%1\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n*0\r\n",
16173            ),
16174            (
16175                &[
16176                    b"TS.MGET",
16177                    b"SELECTED_LABELS",
16178                    b"x",
16179                    b"FILTER",
16180                    b"room=kitchen",
16181                ],
16182                "%2\r\n$1\r\na\r\n*2\r\n%1\r\n$1\r\nx\r\n$1\r\n1\r\n\
16183                 *2\r\n:100\r\n,1.5\r\n\
16184                 $1\r\nc\r\n*2\r\n%1\r\n$1\r\nx\r\n_\r\n*0\r\n",
16185            ),
16186            // A map with a name in it twice, which is what a series wearing one
16187            // label name twice turns into.
16188            (
16189                &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"r=b"],
16190                "%1\r\n$1\r\ne\r\n*2\r\n%2\r\n$1\r\nr\r\n$2\r\nbb\r\n\
16191                 $1\r\nr\r\n$1\r\nb\r\n*0\r\n",
16192            ),
16193        ];
16194        for (argv, want) in cases {
16195            let got = f.run(argv);
16196            assert_eq!(&got, want, "{:?}", argv.last());
16197        }
16198    }
16199
16200    /// The same five series with enough samples in them for a group to have
16201    /// something to fold.
16202    fn spanned() -> Fixture {
16203        let mut f = labelled();
16204        f.run(&[b"TS.ADD", b"a", b"200", b"2.5"]);
16205        f.run(&[b"TS.ADD", b"c", b"100", b"10"]);
16206        f.run(&[b"TS.ADD", b"c", b"300", b"30"]);
16207        f
16208    }
16209
16210    /// A span read out of every series a filter takes, with and without a group
16211    /// over the top of it.
16212    #[test]
16213    fn mrange_reads_every_series_and_folds_the_groups_it_is_asked_for() {
16214        let mut f = spanned();
16215        let cases: &[(&[&[u8]], &str)] = &[
16216            (
16217                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=kitchen"],
16218                "*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\
16219                 *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",
16220            ),
16221            // Newest first is applied to each series before anything else sees
16222            // the rows.
16223            (
16224                &[
16225                    b"TS.MREVRANGE",
16226                    b"-",
16227                    b"+",
16228                    b"WITHLABELS",
16229                    b"FILTER",
16230                    b"room=kitchen",
16231                ],
16232                "*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\
16233                 *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\
16234                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$4\r\nroom\r\n$7\r\nkitchen\r\n\
16235                 *2\r\n*2\r\n:300\r\n+30\r\n*2\r\n:100\r\n+10\r\n",
16236            ),
16237            // A label a series does not wear comes back against a nil rather
16238            // than being left out.
16239            (
16240                &[
16241                    b"TS.MRANGE",
16242                    b"-",
16243                    b"+",
16244                    b"SELECTED_LABELS",
16245                    b"x",
16246                    b"FILTER",
16247                    b"room=kitchen",
16248                ],
16249                "*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\
16250                 *2\r\n*2\r\n:100\r\n+1.5\r\n*2\r\n:200\r\n+2.5\r\n\
16251                 *3\r\n$1\r\nc\r\n*1\r\n*2\r\n$1\r\nx\r\n$-1\r\n\
16252                 *2\r\n*2\r\n:100\r\n+10\r\n*2\r\n:300\r\n+30\r\n",
16253            ),
16254            // The fold: 100 is in both series and adds up, the other two are in
16255            // one each and are still rows.
16256            (
16257                &[
16258                    b"TS.MRANGE",
16259                    b"-",
16260                    b"+",
16261                    b"FILTER",
16262                    b"room=kitchen",
16263                    b"GROUPBY",
16264                    b"room",
16265                    b"REDUCE",
16266                    b"sum",
16267                ],
16268                "*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\
16269                 *2\r\n:200\r\n+2.5\r\n*2\r\n:300\r\n+30\r\n",
16270            ),
16271            // RESP2 has nowhere to put the reducer and the member keys, so a
16272            // group wearing labels writes them as two more labels.
16273            (
16274                &[
16275                    b"TS.MRANGE",
16276                    b"-",
16277                    b"+",
16278                    b"WITHLABELS",
16279                    b"FILTER",
16280                    b"room=kitchen",
16281                    b"GROUPBY",
16282                    b"room",
16283                    b"REDUCE",
16284                    b"max",
16285                ],
16286                "*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\
16287                 *2\r\n$11\r\n__reducer__\r\n$3\r\nmax\r\n\
16288                 *2\r\n$10\r\n__source__\r\n$3\r\na,c\r\n\
16289                 *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",
16290            ),
16291            // A count is applied to each member and then again to the fold.
16292            (
16293                &[
16294                    b"TS.MREVRANGE",
16295                    b"-",
16296                    b"+",
16297                    b"COUNT",
16298                    b"1",
16299                    b"FILTER",
16300                    b"room=kitchen",
16301                    b"GROUPBY",
16302                    b"room",
16303                    b"REDUCE",
16304                    b"count",
16305                ],
16306                "*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",
16307            ),
16308            // Nothing wears the label, so nothing is in any group.
16309            (
16310                &[
16311                    b"TS.MRANGE",
16312                    b"-",
16313                    b"+",
16314                    b"FILTER",
16315                    b"room=kitchen",
16316                    b"GROUPBY",
16317                    b"nope",
16318                    b"REDUCE",
16319                    b"sum",
16320                ],
16321                "*0\r\n",
16322            ),
16323            (
16324                &[
16325                    b"TS.MRANGE",
16326                    b"-",
16327                    b"+",
16328                    b"AGGREGATION",
16329                    b"sum,avg",
16330                    b"100",
16331                    b"FILTER",
16332                    b"room=bedroom",
16333                ],
16334                "*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",
16335            ),
16336            // The errors, in the order they are looked for.
16337            (
16338                &[b"TS.MRANGE", b"-", b"+", b"room=kitchen"],
16339                "-ERR TSDB: missing FILTER argument\r\n",
16340            ),
16341            (
16342                &[b"TS.MRANGE", b"-", b"+", b"FILTER"],
16343                "-ERR TSDB: missing labels for filter argument\r\n",
16344            ),
16345            (
16346                &[
16347                    b"TS.MRANGE",
16348                    b"-",
16349                    b"+",
16350                    b"GROUPBY",
16351                    b"room",
16352                    b"REDUCE",
16353                    b"sum",
16354                    b"FILTER",
16355                    b"room=kitchen",
16356                ],
16357                "-ERR TSDB: GROUPBY should always come after filter\r\n",
16358            ),
16359            // The group is four words from the end here, so the length is what
16360            // is wrong with it.
16361            (
16362                &[
16363                    b"TS.MRANGE",
16364                    b"-",
16365                    b"+",
16366                    b"FILTER",
16367                    b"room=kitchen",
16368                    b"GROUPBY",
16369                    b"room",
16370                    b"REDUCE",
16371                    b"sum",
16372                    b"x",
16373                ],
16374                "-ERR wrong number of arguments for 'ts.mrange' command\r\n",
16375            ),
16376            // And here it is not, so its words are filters and answer first.
16377            (
16378                &[
16379                    b"TS.MRANGE",
16380                    b"-",
16381                    b"+",
16382                    b"FILTER",
16383                    b"nope",
16384                    b"GROUPBY",
16385                    b"room",
16386                    b"REDUCE",
16387                    b"sum",
16388                    b"x",
16389                ],
16390                "-ERR TSDB: failed parsing labels\r\n",
16391            ),
16392            (
16393                &[
16394                    b"TS.MRANGE",
16395                    b"-",
16396                    b"+",
16397                    b"FILTER",
16398                    b"room=kitchen",
16399                    b"GROUPBY",
16400                    b"room",
16401                    b"REDUCE",
16402                    b"twa",
16403                ],
16404                "-ERR TSDB: Invalid reducer type\r\n",
16405            ),
16406            (
16407                &[
16408                    b"TS.MRANGE",
16409                    b"-",
16410                    b"+",
16411                    b"AGGREGATION",
16412                    b"sum,avg",
16413                    b"100",
16414                    b"FILTER",
16415                    b"room=kitchen",
16416                    b"GROUPBY",
16417                    b"room",
16418                    b"REDUCE",
16419                    b"sum",
16420                ],
16421                "-ERR TSDB: GROUPBY is not allowed when multiple aggregators are specified\r\n",
16422            ),
16423            // The label list ends at a keyword, so this is a `COUNT` with a
16424            // `FILTER` where its number should be.
16425            (
16426                &[
16427                    b"TS.MRANGE",
16428                    b"-",
16429                    b"+",
16430                    b"SELECTED_LABELS",
16431                    b"COUNT",
16432                    b"FILTER",
16433                    b"room=kitchen",
16434                ],
16435                "-ERR TSDB: Couldn't parse COUNT\r\n",
16436            ),
16437        ];
16438        for (argv, want) in cases {
16439            let got = f.run(argv);
16440            assert_eq!(&got, want, "{argv:?}");
16441        }
16442    }
16443
16444    /// The multi key reads on RESP3, where the key becomes a map key and the
16445    /// reducer and the member keys become fields of their own.
16446    #[test]
16447    fn resp3_writes_a_multi_key_read_as_a_map_of_four() {
16448        let mut f = spanned();
16449        f.out = Out::new(Proto::Resp3);
16450        let cases: &[(&[&[u8]], &str)] = &[
16451            (
16452                &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=bedroom"],
16453                "%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\
16454                 *1\r\n*2\r\n:200\r\n,2\r\n",
16455            ),
16456            // The reductions a read asked for, which RESP2 has no room for at
16457            // all and which is empty on a read that asked for none.
16458            (
16459                &[
16460                    b"TS.MRANGE",
16461                    b"-",
16462                    b"+",
16463                    b"AGGREGATION",
16464                    b"sum,avg",
16465                    b"100",
16466                    b"FILTER",
16467                    b"room=bedroom",
16468                ],
16469                "%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\
16470                 $3\r\navg\r\n*1\r\n*3\r\n:200\r\n,2\r\n,2\r\n",
16471            ),
16472            (
16473                &[
16474                    b"TS.MRANGE",
16475                    b"-",
16476                    b"+",
16477                    b"FILTER",
16478                    b"room=kitchen",
16479                    b"GROUPBY",
16480                    b"room",
16481                    b"REDUCE",
16482                    b"sum",
16483                ],
16484                "%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\
16485                 $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\
16486                 *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",
16487            ),
16488            // The labels hold only the pair the group was made on, because the
16489            // reducer and the sources have somewhere else to go.
16490            (
16491                &[
16492                    b"TS.MRANGE",
16493                    b"-",
16494                    b"+",
16495                    b"WITHLABELS",
16496                    b"FILTER",
16497                    b"room=kitchen",
16498                    b"GROUPBY",
16499                    b"room",
16500                    b"REDUCE",
16501                    b"max",
16502                ],
16503                "%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\
16504                 %1\r\n$8\r\nreducers\r\n*1\r\n$3\r\nmax\r\n\
16505                 %1\r\n$7\r\nsources\r\n*2\r\n$1\r\na\r\n$1\r\nc\r\n\
16506                 *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",
16507            ),
16508            (
16509                &[
16510                    b"TS.MRANGE",
16511                    b"-",
16512                    b"+",
16513                    b"FILTER",
16514                    b"room=kitchen",
16515                    b"GROUPBY",
16516                    b"nope",
16517                    b"REDUCE",
16518                    b"sum",
16519                ],
16520                "%0\r\n",
16521            ),
16522        ];
16523        for (argv, want) in cases {
16524            let got = f.run(argv);
16525            assert_eq!(&got, want, "{argv:?}");
16526        }
16527    }
16528
16529    /// `TS.CREATERULE`, whose refusals come in an order of their own.
16530    #[test]
16531    fn createrule_checks_the_two_keys_last_and_the_two_links_after_that() {
16532        let mut f = Fixture::new();
16533        f.run(&[b"TS.CREATE", b"src"]);
16534        f.run(&[b"TS.CREATE", b"dst"]);
16535        f.run(&[b"SET", b"plain", b"v"]);
16536        let cases: &[(&[&[u8]], &str)] = &[
16537            // The width is read before the reduction, the reduction before the
16538            // width being above zero, and all three before either key is looked
16539            // at, so a command that is wrong twice complains about the first.
16540            (
16541                &[
16542                    b"TS.CREATERULE",
16543                    b"src",
16544                    b"dst",
16545                    b"AGGREGATION",
16546                    b"nope",
16547                    b"x",
16548                ],
16549                "-ERR TSDB: Couldn't parse AGGREGATION\r\n",
16550            ),
16551            (
16552                &[
16553                    b"TS.CREATERULE",
16554                    b"src",
16555                    b"dst",
16556                    b"AGGREGATION",
16557                    b"nope",
16558                    b"10",
16559                ],
16560                "-ERR TSDB: Unknown aggregation type\r\n",
16561            ),
16562            (
16563                &[
16564                    b"TS.CREATERULE",
16565                    b"src",
16566                    b"dst",
16567                    b"AGGREGATION",
16568                    b"avg",
16569                    b"0",
16570                ],
16571                "-ERR TSDB: bucketDuration must be greater than zero\r\n",
16572            ),
16573            (
16574                &[
16575                    b"TS.CREATERULE",
16576                    b"src",
16577                    b"dst",
16578                    b"AGGREGATION",
16579                    b"avg",
16580                    b"10",
16581                    b"x",
16582                ],
16583                "-ERR TSDB: Couldn't parse alignTimestamp\r\n",
16584            ),
16585            (
16586                &[
16587                    b"TS.CREATERULE",
16588                    b"src",
16589                    b"src",
16590                    b"AGGREGATION",
16591                    b"avg",
16592                    b"10",
16593                ],
16594                "-ERR TSDB: the source key and destination key should be different\r\n",
16595            ),
16596            // A key holding something else answers the same as a key that is not
16597            // there at all, because the source is looked up first and neither of
16598            // them is a series.
16599            (
16600                &[
16601                    b"TS.CREATERULE",
16602                    b"nope",
16603                    b"plain",
16604                    b"AGGREGATION",
16605                    b"avg",
16606                    b"10",
16607                ],
16608                "-ERR TSDB: the key does not exist\r\n",
16609            ),
16610            (
16611                &[
16612                    b"TS.CREATERULE",
16613                    b"src",
16614                    b"nope",
16615                    b"AGGREGATION",
16616                    b"avg",
16617                    b"10",
16618                ],
16619                "-ERR TSDB: the key does not exist\r\n",
16620            ),
16621            // A keyword other than AGGREGATION is an arity error rather than a
16622            // syntax one, because the arity is all that is checked.
16623            (
16624                &[b"TS.CREATERULE", b"src", b"dst", b"NOPE", b"avg", b"10"],
16625                "-ERR wrong number of arguments for 'ts.createrule' command\r\n",
16626            ),
16627            (
16628                &[
16629                    b"TS.CREATERULE",
16630                    b"src",
16631                    b"dst",
16632                    b"AGGREGATION",
16633                    b"avg",
16634                    b"10",
16635                ],
16636                "+OK\r\n",
16637            ),
16638            // The link is now in place, so the same rule again is refused from
16639            // the destination's end.
16640            (
16641                &[
16642                    b"TS.CREATERULE",
16643                    b"src",
16644                    b"dst",
16645                    b"AGGREGATION",
16646                    b"avg",
16647                    b"10",
16648                ],
16649                "-ERR TSDB: the destination key already has a src rule\r\n",
16650            ),
16651            // A source that is already someone's destination, and a destination
16652            // that is already someone's source, are two different sentences.
16653            (
16654                &[
16655                    b"TS.CREATERULE",
16656                    b"dst",
16657                    b"src",
16658                    b"AGGREGATION",
16659                    b"avg",
16660                    b"10",
16661                ],
16662                "-ERR TSDB: the source key already has a source rule\r\n",
16663            ),
16664            (&[b"TS.DELETERULE", b"src", b"dst"], "+OK\r\n"),
16665            (
16666                &[b"TS.DELETERULE", b"src", b"dst"],
16667                "-ERR TSDB: compaction rule does not exist\r\n",
16668            ),
16669            // The source is looked up and the destination is not, so a missing
16670            // destination is a missing rule and a missing source is a missing
16671            // key, which is the other way round from `TS.CREATERULE`.
16672            (
16673                &[b"TS.DELETERULE", b"src", b"nope"],
16674                "-ERR TSDB: compaction rule does not exist\r\n",
16675            ),
16676            (
16677                &[b"TS.DELETERULE", b"nope", b"dst"],
16678                "-ERR TSDB: the key does not exist\r\n",
16679            ),
16680        ];
16681        for (argv, want) in cases {
16682            let got = f.run(argv);
16683            assert_eq!(&got, want, "{argv:?}");
16684        }
16685    }
16686
16687    /// What a rule writes, which is every bucket but the one it is filling.
16688    #[test]
16689    fn a_rule_writes_a_bucket_when_a_later_reading_closes_it() {
16690        let mut f = Fixture::new();
16691        f.run(&[b"TS.CREATE", b"src"]);
16692        f.run(&[b"TS.CREATE", b"dst"]);
16693        // The readings written before the rule was made are not folded, so the
16694        // destination is still empty after the first two.
16695        f.run(&[b"TS.ADD", b"src", b"10", b"1"]);
16696        f.run(&[
16697            b"TS.CREATERULE",
16698            b"src",
16699            b"dst",
16700            b"AGGREGATION",
16701            b"sum",
16702            b"100",
16703        ]);
16704        f.run(&[b"TS.ADD", b"src", b"20", b"2"]);
16705        assert_eq!(f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]), "*0\r\n");
16706        // The bucket the rule is filling holds only what it was given, so it is
16707        // 2 rather than 3, and it is written when a reading lands past it.
16708        assert_eq!(f.run(&[b"TS.GET", b"dst", b"LATEST"]), "*2\r\n:0\r\n+2\r\n");
16709        f.run(&[b"TS.ADD", b"src", b"110", b"4"]);
16710        assert_eq!(
16711            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16712            "*1\r\n*2\r\n:0\r\n+2\r\n"
16713        );
16714        // A reading into a bucket that has already been written works that
16715        // bucket out again over everything the source now holds.
16716        f.run(&[b"TS.ADD", b"src", b"30", b"8"]);
16717        assert_eq!(
16718            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16719            "*1\r\n*2\r\n:0\r\n+11\r\n"
16720        );
16721        // Deleting from the source works the buckets it touched out again and
16722        // reopens the newest one, so `LATEST` starts from the whole bucket.
16723        assert_eq!(f.run(&[b"TS.DEL", b"src", b"0", b"25"]), ":2\r\n");
16724        assert_eq!(
16725            f.run(&[b"TS.RANGE", b"dst", b"-", b"+"]),
16726            "*1\r\n*2\r\n:0\r\n+8\r\n"
16727        );
16728        assert_eq!(
16729            f.run(&[b"TS.GET", b"dst", b"LATEST"]),
16730            "*2\r\n:100\r\n+4\r\n"
16731        );
16732        // The link shows on both ends, and dropping either key takes it down.
16733        assert!(f.run(&[b"TS.INFO", b"dst"]).contains("sourceKey"));
16734        f.run(&[b"DEL", b"dst"]);
16735        assert_eq!(
16736            f.run(&[b"TS.DELETERULE", b"src", b"dst"]),
16737            "-ERR TSDB: compaction rule does not exist\r\n"
16738        );
16739    }
16740
16741    /// The three shapes an `XADD` id can take, and the one rule behind all of
16742    /// them.
16743    #[test]
16744    fn xadd_ids_only_ever_go_up() {
16745        let mut f = Fixture::new();
16746        // A bare millisecond is that millisecond and sequence zero.
16747        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
16748        // And `5-*` is the next free sequence inside it.
16749        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
16750        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
16751        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
16752        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16753
16754        assert!(
16755            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
16756                .contains("equal or smaller")
16757        );
16758        assert!(
16759            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
16760                .contains("must be greater than 0-0")
16761        );
16762        assert!(
16763            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
16764                .contains("Invalid stream ID")
16765        );
16766        // The pairs have to be pairs, and Redis calls an odd one an arity error
16767        // rather than a syntax error even though the table has already passed.
16768        assert!(
16769            f.run(&[b"XADD", b"s", b"*", b"a"])
16770                .contains("wrong number of arguments")
16771        );
16772
16773        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
16774        // producer can tell nobody is consuming this yet from the write landed.
16775        assert_eq!(
16776            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
16777            "$-1\r\n"
16778        );
16779        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
16780        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
16781        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
16782    }
16783
16784    /// The trim options, which are three keywords that disagree about how many
16785    /// arguments they take.
16786    #[test]
16787    fn trimming_reads_its_options_the_way_redis_does() {
16788        let mut f = Fixture::new();
16789        for i in 1..=10u32 {
16790            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
16791        }
16792        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
16793        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
16794        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
16795        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16796
16797        // One argument after the keyword and the `~` is read as the threshold,
16798        // which is what a real server does and is the reason this is a number
16799        // complaint and not a syntax one.
16800        assert!(
16801            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
16802                .contains("not an integer")
16803        );
16804        assert!(
16805            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
16806                .contains("MAXLEN argument must be >= 0")
16807        );
16808        // The strategy check runs before the approximation check, so a LIMIT
16809        // with neither is told about the missing strategy.
16810        assert!(
16811            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
16812                .contains("without specifying a trimming strategy")
16813        );
16814        assert!(
16815            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
16816                .contains("without the special ~ option")
16817        );
16818        assert!(
16819            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
16820                .contains("at the same time are not compatible")
16821        );
16822        // NOMKSTREAM is XADD's and XTRIM does not take it.
16823        assert!(
16824            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
16825                .contains("syntax error")
16826        );
16827        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
16828    }
16829
16830    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
16831    #[test]
16832    fn xrange_looks_the_key_up_before_it_reads_the_count() {
16833        let mut f = Fixture::new();
16834        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
16835        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
16836
16837        assert_eq!(
16838            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
16839            "*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\
16840             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16841        );
16842        assert_eq!(
16843            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
16844            "*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"
16845        );
16846        // The exclusive bound is stepped after the missing sequence is filled
16847        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
16848        // `6-1` is still in the range.
16849        assert_eq!(
16850            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
16851            "*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\
16852             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
16853        );
16854        assert_eq!(
16855            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
16856            "*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"
16857        );
16858        assert!(
16859            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
16860                .contains("Invalid stream ID")
16861        );
16862
16863        // The two kinds of nothing. A key that is not there is an empty array
16864        // and a key that is there with a count of zero is a null array, because
16865        // the lookup happens first.
16866        assert_eq!(
16867            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
16868            "*0\r\n"
16869        );
16870        assert_eq!(
16871            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
16872            "*-1\r\n"
16873        );
16874        f.run(&[b"SET", b"str", b"v"]);
16875        assert!(
16876            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
16877                .starts_with("-WRONGTYPE")
16878        );
16879        // The count is read in a loop, so the last one wins.
16880        assert_eq!(
16881            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
16882            "*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"
16883        );
16884    }
16885
16886    /// `XDEL` and `XACK` check every id before they touch any of them.
16887    #[test]
16888    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
16889        let mut f = Fixture::new();
16890        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16891        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16892        assert!(
16893            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
16894                .contains("Invalid stream ID")
16895        );
16896        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
16897        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
16898        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
16899        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
16900        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
16901    }
16902
16903    /// `XGROUP`, and the two different complaints it makes about arguments.
16904    #[test]
16905    fn xgroup_has_an_arity_per_subcommand() {
16906        let mut f = Fixture::new();
16907        assert!(
16908            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16909                .contains("requires the key")
16910        );
16911        assert_eq!(
16912            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
16913            "+OK\r\n"
16914        );
16915        // A second CREATE is BUSYGROUP and not an ordinary error, because a
16916        // client racing another one to make a group branches on the prefix.
16917        assert!(
16918            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
16919                .starts_with("-BUSYGROUP")
16920        );
16921        assert_eq!(
16922            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16923            ":1\r\n"
16924        );
16925        assert_eq!(
16926            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
16927            ":0\r\n"
16928        );
16929        assert_eq!(
16930            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
16931            ":0\r\n"
16932        );
16933
16934        // Below the subcommand's own arity is an arity error naming the pair.
16935        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
16936        assert!(
16937            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
16938            "{short}"
16939        );
16940        // At or above it in a shape the handler will not take is the other one.
16941        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
16942        assert!(
16943            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
16944            "{odd}"
16945        );
16946        assert!(
16947            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
16948                .contains("Try XGROUP HELP")
16949        );
16950
16951        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
16952        assert!(
16953            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
16954                .starts_with("-NOGROUP")
16955        );
16956        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
16957        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
16958        assert!(
16959            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
16960                .contains("requires the key")
16961        );
16962    }
16963
16964    /// A group read, an acknowledgement, and what is left in between.
16965    #[test]
16966    fn xreadgroup_hands_out_and_xack_takes_back() {
16967        let mut f = Fixture::new();
16968        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
16969        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
16970        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
16971
16972        let first = f.run(&[
16973            b"XREADGROUP",
16974            b"GROUP",
16975            b"g",
16976            b"c1",
16977            b"COUNT",
16978            b"1",
16979            b"STREAMS",
16980            b"s",
16981            b">",
16982        ]);
16983        assert_eq!(
16984            first,
16985            "*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"
16986        );
16987        // A history read names its stream even with nothing to show, which is
16988        // the difference between it and a `>` read that found nothing.
16989        assert_eq!(
16990            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
16991            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
16992        );
16993        assert_eq!(
16994            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
16995            "*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"
16996        );
16997
16998        assert_eq!(
16999            f.run(&[b"XPENDING", b"s", b"g"]),
17000            "*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"
17001        );
17002        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
17003        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
17004        // Empty is four nulls and not a zero with three empty things.
17005        assert_eq!(
17006            f.run(&[b"XPENDING", b"s", b"g"]),
17007            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
17008        );
17009
17010        // A history read of an entry that has since been deleted is the id with
17011        // a null beside it, so the consumer can still acknowledge it.
17012        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17013        f.run(&[b"XDEL", b"s", b"2-1"]);
17014        assert_eq!(
17015            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
17016            "*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"
17017        );
17018
17019        // The group lookup runs before the id parse, so a `+` at a stream with
17020        // no such group is told about the group and not about the id.
17021        assert!(
17022            f.run(&[
17023                b"XREADGROUP",
17024                b"GROUP",
17025                b"nope",
17026                b"c",
17027                b"STREAMS",
17028                b"s",
17029                b"+"
17030            ])
17031            .starts_with("-NOGROUP")
17032        );
17033        assert!(
17034            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
17035                .contains("meaningless in the context of XREADGROUP")
17036        );
17037        assert!(
17038            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
17039                .contains("only supported by XREADGROUP")
17040        );
17041        assert!(
17042            f.run(&[
17043                b"XREADGROUP",
17044                b"GROUP",
17045                b"g",
17046                b"c",
17047                b"STREAMS",
17048                b"s",
17049                b"a",
17050                b"b"
17051            ])
17052            .contains("Unbalanced 'xreadgroup' list of streams")
17053        );
17054    }
17055
17056    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
17057    /// answer.
17058    #[test]
17059    fn xread_with_no_block_writes_the_null_itself() {
17060        let mut f = Fixture::new();
17061        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17062        assert_eq!(
17063            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17064            "*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"
17065        );
17066        // Nothing new is a null array and not an empty one, and a stream with
17067        // nothing new is left out rather than sent with an empty list.
17068        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
17069        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
17070        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
17071        assert_eq!(
17072            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
17073            "*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"
17074        );
17075        // `$` is the last id, so nothing that is already there comes back.
17076        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
17077        // And `+` is the last entry, whatever COUNT says.
17078        assert_eq!(
17079            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
17080            "*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"
17081        );
17082        // A count of zero means unlimited here, which is the opposite of what it
17083        // means to XRANGE.
17084        assert_eq!(
17085            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
17086            "*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"
17087        );
17088        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
17089        assert!(
17090            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
17091                .contains("not an integer")
17092        );
17093        assert!(
17094            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
17095                .contains("timeout is negative")
17096        );
17097        assert!(
17098            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
17099                .contains("Unbalanced 'xread' list of streams")
17100        );
17101    }
17102
17103    /// A blocked reader, and the two ways it stops being blocked.
17104    #[test]
17105    fn a_blocked_xread_wakes_on_the_next_entry() {
17106        let mut f = Fixture::new();
17107        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17108        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17109        assert_eq!(flow, Flow::Block);
17110        assert!(reply.is_empty());
17111
17112        // Everybody parked on the stream gets the entry, because a read takes
17113        // nothing away. That is the difference between this and BLPOP.
17114        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
17115        assert_eq!(flow, Flow::Block);
17116        assert_eq!(f.server.waiters().len(), 2);
17117
17118        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17119        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";
17120        for at in 0..2 {
17121            let mut out = Out::new(Proto::Resp2);
17122            assert!(f.server.serve_waiter(at, 0, &mut out));
17123            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
17124        }
17125
17126        // And a deadline that runs out is a null array, the same as a plain
17127        // XREAD that found nothing.
17128        f.server.waiters_mut().forget(7);
17129        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
17130        assert_eq!(flow, Flow::Block);
17131        let mut out = Out::new(Proto::Resp2);
17132        assert!(!f.server.serve_waiter(0, 0, &mut out));
17133        assert!(out.as_slice().is_empty());
17134        assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
17135        assert_eq!(
17136            core::str::from_utf8(out.as_slice()).expect("ascii"),
17137            "*-1\r\n"
17138        );
17139    }
17140
17141    /// A blocked group reader whose group is destroyed under it.
17142    #[test]
17143    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
17144        let mut f = Fixture::new();
17145        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17146        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
17147        let (flow, _) = f.flow(&[
17148            b"XREADGROUP",
17149            b"GROUP",
17150            b"g",
17151            b"c",
17152            b"BLOCK",
17153            b"0",
17154            b"STREAMS",
17155            b"s",
17156            b">",
17157        ]);
17158        assert_eq!(flow, Flow::Block);
17159
17160        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
17161        let mut out = Out::new(Proto::Resp2);
17162        assert!(f.server.serve_waiter(0, 0, &mut out));
17163        // The ordinary sentence and not a special one about having been parked,
17164        // which is what a running 8.10 sends.
17165        assert_eq!(
17166            core::str::from_utf8(out.as_slice()).expect("ascii"),
17167            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
17168        );
17169    }
17170
17171    /// `XCLAIM`, whose argument shape is the odd one in the group.
17172    #[test]
17173    fn xclaim_reads_ids_until_one_will_not_parse() {
17174        let mut f = Fixture::new();
17175        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17176        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17177        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17178        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17179
17180        // Everything after the first argument that is not an id is an option, so
17181        // a `-` is an unrecognised option and not a bad id.
17182        assert!(
17183            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
17184                .contains("Unrecognized XCLAIM option '-'")
17185        );
17186        assert_eq!(
17187            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
17188            "*1\r\n$3\r\n1-1\r\n"
17189        );
17190        // An id that is pending but whose entry has gone is an empty answer, and
17191        // it leaves the pending list on the way past.
17192        f.run(&[b"XDEL", b"s", b"2-1"]);
17193        assert_eq!(
17194            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
17195            "*0\r\n"
17196        );
17197        assert!(
17198            f.run(&[b"XPENDING", b"s", b"g"])
17199                .starts_with("*4\r\n:1\r\n")
17200        );
17201        assert!(
17202            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
17203                .starts_with("-NOGROUP")
17204        );
17205        assert!(
17206            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
17207                .contains("Invalid min-idle-time argument for XCLAIM")
17208        );
17209    }
17210
17211    /// `XAUTOCLAIM`, and the third value nobody expects.
17212    #[test]
17213    fn xautoclaim_reports_what_it_dropped() {
17214        let mut f = Fixture::new();
17215        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17216        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17217        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17218        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17219        f.run(&[b"XDEL", b"s", b"1-1"]);
17220
17221        // The cursor, what was claimed, and what was dropped for no longer being
17222        // in the stream. The third one is what makes a sweep converge.
17223        assert_eq!(
17224            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
17225            "*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"
17226        );
17227        assert!(
17228            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
17229                .contains("COUNT must be > 0")
17230        );
17231        assert!(
17232            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
17233                .starts_with("-NOGROUP")
17234        );
17235    }
17236
17237    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
17238    #[test]
17239    fn xdelex_answers_one_integer_an_id() {
17240        let mut f = Fixture::new();
17241        for i in 1..=4 {
17242            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17243        }
17244        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17245        f.run(&[
17246            b"XREADGROUP",
17247            b"GROUP",
17248            b"g",
17249            b"c",
17250            b"COUNT",
17251            b"2",
17252            b"STREAMS",
17253            b"s",
17254            b">",
17255        ]);
17256
17257        // One means gone and minus one means it was not there to start with.
17258        assert_eq!(
17259            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
17260            "*2\r\n:1\r\n:-1\r\n"
17261        );
17262        // `KEEPREF` leaves the pending entry behind, so the group still counts
17263        // the one it was handed even though the entry has gone.
17264        assert!(
17265            f.run(&[b"XPENDING", b"s", b"g"])
17266                .starts_with("*4\r\n:2\r\n")
17267        );
17268        // `DELREF` takes it out of every pending list on the way past.
17269        assert_eq!(
17270            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
17271            "*1\r\n:1\r\n"
17272        );
17273        // `1-1` is still in the list, because the delete before it said KEEPREF.
17274        assert_eq!(
17275            f.run(&[b"XPENDING", b"s", b"g"]),
17276            "*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"
17277        );
17278
17279        // Two means somebody still wants it, and the question is wider than the
17280        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
17281        // refused even though no consumer has ever been handed it.
17282        assert_eq!(
17283            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
17284            "*2\r\n:2\r\n:2\r\n"
17285        );
17286
17287        // A key that is not there answers minus ones without reading the IDs.
17288        assert_eq!(
17289            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
17290            "*2\r\n:-1\r\n:-1\r\n"
17291        );
17292        // A key that is there validates every ID before deleting any of them.
17293        assert!(
17294            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
17295                .starts_with("-ERR Invalid stream ID")
17296        );
17297        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17298
17299        assert!(
17300            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
17301                .contains("Number of IDs must be a positive integer")
17302        );
17303        assert!(
17304            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
17305                .contains("The `numids` parameter must match the number of arguments")
17306        );
17307        // The condition is one word, so a second one is a syntax error, and so
17308        // is one ID more than the count promised.
17309        assert!(
17310            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
17311                .starts_with("-ERR syntax error")
17312        );
17313        assert!(
17314            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
17315                .starts_with("-ERR syntax error")
17316        );
17317        // The key is looked up first, so the wrong type beats the syntax.
17318        f.run(&[b"SET", b"str", b"v"]);
17319        assert!(
17320            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
17321                .starts_with("-WRONGTYPE")
17322        );
17323    }
17324
17325    /// `XACKDEL`, whose reply is about the pending list and not about the log.
17326    #[test]
17327    fn xackdel_reports_what_the_group_was_holding() {
17328        let mut f = Fixture::new();
17329        for i in 1..=3 {
17330            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
17331        }
17332        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17333        f.run(&[
17334            b"XREADGROUP",
17335            b"GROUP",
17336            b"g",
17337            b"c",
17338            b"COUNT",
17339            b"1",
17340            b"STREAMS",
17341            b"s",
17342            b">",
17343        ]);
17344
17345        // Minus one is not about the stream: `2-1` is sitting there unread and
17346        // still answers minus one, because the group was not holding it. It also
17347        // stays, since only an ID that was acknowledged is deleted.
17348        assert_eq!(
17349            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
17350            "*2\r\n:1\r\n:-1\r\n"
17351        );
17352        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17353
17354        // A missing group is minus one an ID and not a NOGROUP.
17355        assert_eq!(
17356            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
17357            "*1\r\n:-1\r\n"
17358        );
17359        assert_eq!(
17360            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
17361            "*1\r\n:-1\r\n"
17362        );
17363
17364        // The acknowledgement happens whatever the condition says, so an ACKED
17365        // that answers two has still emptied the pending list.
17366        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
17367        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
17368        assert_eq!(
17369            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
17370            "*1\r\n:2\r\n"
17371        );
17372        assert_eq!(
17373            f.run(&[b"XPENDING", b"s", b"g"]),
17374            "*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"
17375        );
17376        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
17377    }
17378
17379    /// `XNACK`, which hands an entry back to nobody.
17380    #[test]
17381    fn xnack_releases_an_entry_for_the_next_claim() {
17382        let mut f = Fixture::new();
17383        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17384        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17385        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17386        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17387        // Twice, so the delivery count is two and the words have something to
17388        // do with it.
17389        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
17390
17391        assert_eq!(
17392            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
17393            ":1\r\n"
17394        );
17395        // No owner, no idle time, and the count left where it was. A released
17396        // entry reads as idle for longer than any min-idle-time, which is what
17397        // puts it at the front of the next claim.
17398        assert_eq!(
17399            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
17400            "*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"
17401        );
17402        // The consumer no longer holds it, so a filtered XPENDING skips it.
17403        assert_eq!(
17404            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17405            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
17406        );
17407        // The bookmark did not move, so a `>` read will not hand it out again.
17408        assert_eq!(
17409            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
17410            "*-1\r\n"
17411        );
17412        // A claim at any min-idle-time takes it.
17413        assert_eq!(
17414            f.run(&[
17415                b"XAUTOCLAIM",
17416                b"s",
17417                b"g",
17418                b"c2",
17419                b"99999999",
17420                b"-",
17421                b"JUSTID"
17422            ]),
17423            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
17424        );
17425
17426        // `SILENT` takes one off the count rather than putting it back to zero,
17427        // which only shows on an entry that has been handed out more than once.
17428        // It was delivered and then claimed, so it is on two and goes to one.
17429        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17430        assert!(
17431            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17432                .contains(":-1\r\n:1\r\n")
17433        );
17434        // And it stops at zero rather than wrapping.
17435        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17436        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
17437        assert!(
17438            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17439                .contains(":-1\r\n:0\r\n")
17440        );
17441        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
17442        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
17443        assert!(
17444            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17445                .contains(":9223372036854775807\r\n")
17446        );
17447        f.run(&[
17448            b"XNACK",
17449            b"s",
17450            b"g",
17451            b"FATAL",
17452            b"IDS",
17453            b"1",
17454            b"1-1",
17455            b"RETRYCOUNT",
17456            b"3",
17457        ]);
17458        assert!(
17459            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17460                .contains(":-1\r\n:3\r\n")
17461        );
17462
17463        // Releasing something the group is not holding is zero, and `FORCE`
17464        // makes the pending entry rather than answering zero. A forced entry
17465        // starts at zero, since there was no earlier count to keep.
17466        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
17467        assert_eq!(
17468            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
17469            ":0\r\n"
17470        );
17471        assert_eq!(
17472            f.run(&[
17473                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
17474            ]),
17475            ":1\r\n"
17476        );
17477        assert!(
17478            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
17479                .contains(":-1\r\n:0\r\n")
17480        );
17481        // `FORCE` on an ID the stream does not have is still zero.
17482        assert_eq!(
17483            f.run(&[
17484                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
17485            ]),
17486            ":0\r\n"
17487        );
17488
17489        // The group is looked up before the mode word, and it raises rather
17490        // than answering per ID the way the two delete commands do.
17491        assert_eq!(
17492            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
17493            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
17494        );
17495        assert!(
17496            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
17497                .starts_with("-ERR")
17498        );
17499        // Its own sentences, which are not the ones XDELEX uses.
17500        assert!(
17501            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
17502                .contains("numids must be a positive integer")
17503        );
17504        assert!(
17505            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
17506                .contains("number of IDs doesn't match numids")
17507        );
17508        // Everything past the counted IDs is an option, so one too many is an
17509        // option nobody recognises and not a count that does not add up.
17510        assert!(
17511            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
17512                .contains("Unrecognized XNACK option '2-1'")
17513        );
17514    }
17515
17516    /// `XINFO`, which is where the shape of the storage shows through.
17517    #[test]
17518    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
17519        let mut f = Fixture::new();
17520        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17521        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
17522        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17523        f.run(&[
17524            b"XREADGROUP",
17525            b"GROUP",
17526            b"g",
17527            b"c1",
17528            b"COUNT",
17529            b"1",
17530            b"STREAMS",
17531            b"s",
17532            b">",
17533        ]);
17534
17535        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17536        // Ten pairs, since the six idempotency fields have nothing behind them
17537        // here and a zero would claim they had. That is D-27.
17538        assert!(info.starts_with("*20\r\n"), "{info}");
17539        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
17540        assert!(
17541            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
17542            "{info}"
17543        );
17544        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
17545        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
17546
17547        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
17548        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
17549        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
17550        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
17551        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
17552
17553        // A consumer that has never been given anything reports minus one for
17554        // inactive rather than the moment it turned up, which is what tells a
17555        // worker that is stuck from one that has nothing to do.
17556        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
17557        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
17558        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
17559        assert!(
17560            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
17561            "{consumers}"
17562        );
17563        // And in name order, which the storage does not hold them in.
17564        let c1 = consumers.find("c1").unwrap();
17565        let c2 = consumers.find("c2").unwrap();
17566        assert!(c1 < c2, "{consumers}");
17567
17568        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
17569        assert!(full.starts_with("*18\r\n"), "{full}");
17570        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
17571        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
17572
17573        assert!(
17574            f.run(&[b"XINFO", b"STREAM", b"missing"])
17575                .contains("no such key")
17576        );
17577        assert!(
17578            f.run(&[b"XINFO", b"GROUPS", b"missing"])
17579                .contains("no such key")
17580        );
17581        assert!(
17582            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
17583                .starts_with("-NOGROUP")
17584        );
17585        assert!(
17586            f.run(&[b"XINFO", b"NOSUCH", b"s"])
17587                .contains("Try XINFO HELP")
17588        );
17589        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
17590        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
17591    }
17592
17593    /// `XPENDING`'s long form, which reads its arguments by counting them.
17594    #[test]
17595    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
17596        let mut f = Fixture::new();
17597        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17598        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
17599        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
17600
17601        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
17602        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");
17603        assert_eq!(
17604            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
17605            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
17606        );
17607        // A consumer nobody has heard of holds nothing rather than erroring.
17608        assert_eq!(
17609            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
17610            "*0\r\n"
17611        );
17612        assert_eq!(
17613            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
17614            list
17615        );
17616        // IDLE is only read at position three.
17617        assert!(
17618            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
17619                .contains("syntax error")
17620        );
17621        assert!(
17622            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
17623                .contains("syntax error")
17624        );
17625        assert_eq!(
17626            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
17627            "*0\r\n"
17628        );
17629        assert!(
17630            f.run(&[b"XPENDING", b"missing", b"g"])
17631                .starts_with("-NOGROUP")
17632        );
17633    }
17634
17635    /// `XSETID`, which is three counters and two refusals.
17636    #[test]
17637    fn xsetid_will_not_go_below_what_is_there() {
17638        let mut f = Fixture::new();
17639        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
17640        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
17641        assert_eq!(
17642            f.run(&[
17643                b"XSETID",
17644                b"s",
17645                b"10-1",
17646                b"ENTRIESADDED",
17647                b"7",
17648                b"MAXDELETEDID",
17649                b"9-1"
17650            ]),
17651            "+OK\r\n"
17652        );
17653        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
17654        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
17655        assert!(
17656            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
17657            "{info}"
17658        );
17659
17660        assert!(
17661            f.run(&[b"XSETID", b"s", b"1-1"])
17662                .contains("smaller than the target stream top item")
17663        );
17664        assert!(
17665            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
17666                .contains("entries_added must be positive")
17667        );
17668        assert!(
17669            f.run(&[b"XSETID", b"missing", b"1-1"])
17670                .contains("no such key")
17671        );
17672    }
17673
17674    /// RESP3, where the two reads answer a map and the entries stay an array.
17675    #[test]
17676    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
17677        let mut f = Fixture::new();
17678        f.run(&[b"HELLO", b"3"]);
17679        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
17680        // A map header and then the key and the entries side by side, with no
17681        // two element array wrapping the pair.
17682        assert_eq!(
17683            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
17684            "%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"
17685        );
17686        // The fields are still one flat array and not a map, which is Redis's
17687        // shape and is what every consumer written before RESP3 expects.
17688        assert_eq!(
17689            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
17690            "*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"
17691        );
17692        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
17693    }
17694
17695    /// A store to migrate values into, so a test can watch the inversion.
17696    ///
17697    /// A vector rather than a file for the same reason the tier's own tests use
17698    /// one: the file work has not attached a real store yet, and what this is
17699    /// checking is the policy above the store rather than the store.
17700    struct Mem {
17701        blobs: Vec<Vec<u8>>,
17702    }
17703
17704    impl yo_kv::cold::Blocks for Mem {
17705        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
17706            self.blobs.push(bytes.to_vec());
17707            Ok(yo_common::Addr::new(
17708                yo_common::Space::Log,
17709                (self.blobs.len() - 1) as u64,
17710            ))
17711        }
17712
17713        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
17714            self.blobs
17715                .get(at.offset() as usize)
17716                .map(Vec::as_slice)
17717                .ok_or_else(|| {
17718                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
17719                })
17720        }
17721
17722        fn bytes(&self) -> u64 {
17723            self.blobs.iter().map(|b| b.len() as u64).sum()
17724        }
17725    }
17726
17727    /// A server holding several segments of strings, with somewhere to put them.
17728    ///
17729    /// Answers the fixture and what it was holding when it stopped filling.
17730    fn filled(attach: bool) -> (Fixture, usize) {
17731        let mut f = Fixture::new();
17732        if attach {
17733            f.server
17734                .striped(0)
17735                .stripe_mut(0)
17736                .attach(Box::new(Mem { blobs: Vec::new() }));
17737        }
17738        let val = vec![b'v'; 256];
17739        for i in 0..24000u32 {
17740            let k = format!("key:{i:08}");
17741            f.run(&[b"SET", k.as_bytes(), &val]);
17742        }
17743        let full = f.server.memory_bytes();
17744        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
17745        (f, full)
17746    }
17747
17748    /// Write until the server is under `limit` or the writes run out.
17749    ///
17750    /// The same shape the eviction test uses. A memory limit is enforced in
17751    /// front of a command, so nothing happens until something is written, and
17752    /// the budget means one command does not do the whole job.
17753    fn press(f: &mut Fixture, limit: usize) {
17754        let val = vec![b'v'; 256];
17755        for i in 0..3000u32 {
17756            let k = format!("new:{i:08}");
17757            assert_eq!(
17758                f.run(&[b"SET", k.as_bytes(), &val]),
17759                "+OK\r\n",
17760                "write {i} was refused"
17761            );
17762            f.server.refresh_memory();
17763            if f.server.memory_bytes() <= limit {
17764                return;
17765            }
17766        }
17767        panic!(
17768            "it never got under: {} against {limit}",
17769            f.server.memory_bytes()
17770        );
17771    }
17772
17773    #[test]
17774    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
17775        let mut f = Fixture::new();
17776        assert_eq!(
17777            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17778            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
17779            "no limit is the default"
17780        );
17781        // The same memory value parser `maxmemory` uses, and the same trap in
17782        // it, plus the one spelling that means no limit at all.
17783        for (typed, bytes) in [
17784            (&b"0"[..], "0"),
17785            (b"1024", "1024"),
17786            (b"1k", "1000"),
17787            (b"1gb", "1073741824"),
17788            (b"-1", "-1"),
17789        ] {
17790            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
17791            assert_eq!(
17792                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
17793                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
17794                "set {}",
17795                String::from_utf8_lossy(typed)
17796            );
17797        }
17798        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
17799            assert_eq!(
17800                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
17801                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
17802                "refused {}",
17803                String::from_utf8_lossy(bad)
17804            );
17805        }
17806        // Nothing is attached, so the answer to a memory limit is still Redis's.
17807        let info = f.run(&[b"INFO", b"memory"]);
17808        assert!(info.contains("maxstore:-1"), "{info}");
17809        assert!(info.contains("yo_memory_regime:evict"), "{info}");
17810        assert!(info.contains("yo_store_bytes:0"), "{info}");
17811    }
17812
17813    #[test]
17814    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
17815        // The inversion. The same pressure that makes a Redis server throw keys
17816        // away makes this one move values to the file, and afterwards every key
17817        // is still there and still answers with what was stored in it.
17818        let (mut f, full) = filled(true);
17819        let keys = f.run(&[b"DBSIZE"]);
17820        assert!(
17821            f.run(&[b"INFO", b"memory"])
17822                .contains("yo_memory_regime:migrate"),
17823            "a database with somewhere to put values migrates"
17824        );
17825
17826        let limit = full - 2 * 1024 * 1024;
17827        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17828        f.run(&[
17829            b"CONFIG",
17830            b"SET",
17831            b"maxmemory",
17832            limit.to_string().as_bytes(),
17833        ]);
17834        press(&mut f, limit);
17835
17836        assert!(
17837            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17838            "nothing was thrown away"
17839        );
17840        let after: usize = f.run(&[b"DBSIZE"])[1..]
17841            .trim_end()
17842            .parse()
17843            .expect("a count");
17844        let before: usize = keys[1..].trim_end().parse().expect("a count");
17845        assert!(after > before, "the keys that came in are all still here");
17846        assert!(
17847            f.server.store_bytes() > 0,
17848            "and what came out of memory went to the file"
17849        );
17850        // And the values read back, which is the part that makes it a migration
17851        // rather than a loss.
17852        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
17853        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
17854        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
17855    }
17856
17857    #[test]
17858    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
17859        // The documented setting for a drop in cache. A file that may hold
17860        // nothing cannot be migrated to, so eviction is all that is left, and
17861        // the server behaves exactly as it did before any of this existed.
17862        let (mut f, full) = filled(true);
17863        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
17864        assert!(
17865            f.run(&[b"INFO", b"memory"])
17866                .contains("yo_memory_regime:evict"),
17867            "nothing may go to the file"
17868        );
17869
17870        let limit = full - 2 * 1024 * 1024;
17871        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17872        f.run(&[
17873            b"CONFIG",
17874            b"SET",
17875            b"maxmemory",
17876            limit.to_string().as_bytes(),
17877        ]);
17878        press(&mut f, limit);
17879
17880        assert!(
17881            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17882            "keys were thrown away, which is what was asked for"
17883        );
17884        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
17885    }
17886
17887    #[test]
17888    fn a_full_file_goes_back_to_evicting() {
17889        // A storage limit reached is a storage limit, and eviction is the right
17890        // answer to one. The budget here is a few kilobytes, so the first round
17891        // of migration fills it and everything after that is evicted.
17892        let (mut f, full) = filled(true);
17893        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
17894        let limit = full - 2 * 1024 * 1024;
17895        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
17896        f.run(&[
17897            b"CONFIG",
17898            b"SET",
17899            b"maxmemory",
17900            limit.to_string().as_bytes(),
17901        ]);
17902        press(&mut f, limit);
17903
17904        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
17905        assert!(
17906            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
17907            "and then it started evicting"
17908        );
17909        assert!(
17910            f.run(&[b"INFO", b"memory"])
17911                .contains("yo_memory_regime:evict"),
17912            "and it says so"
17913        );
17914    }
17915    // ------------------------------------------------------------- stripes
17916
17917    /// Every string command, run twice: once on a database that is one keyspace
17918    /// and once on a database that is eight, with the same commands in the same
17919    /// order and the replies compared byte for byte.
17920    ///
17921    /// This is the whole claim the striping rests on. A key belongs to one
17922    /// stripe and to no other, so the answer to a command cannot depend on how
17923    /// many stripes there are, and the way to check that is to ask the same
17924    /// question of two servers that differ in nothing else.
17925    ///
17926    /// The keys are chosen to land on different stripes rather than to look
17927    /// tidy. `MSET a 1 b 2 c 3` over eight stripes is only a test of anything if
17928    /// those three keys are not all on the same one, and at eight stripes three
17929    /// keys land together about one time in fifty.
17930    #[test]
17931    fn the_string_group_answers_the_same_however_many_stripes_there_are() {
17932        let script: &[&[&[u8]]] = &[
17933            // The single key commands, which are the ones that get handed one
17934            // stripe at the dispatch site.
17935            &[b"SET", b"k1", b"v1"],
17936            &[b"SET", b"k2", b"v2"],
17937            &[b"GET", b"k1"],
17938            &[b"GET", b"nothing"],
17939            &[b"GETSET", b"k1", b"v1b"],
17940            &[b"SETNX", b"k1", b"no"],
17941            &[b"SETNX", b"k3", b"yes"],
17942            &[b"APPEND", b"k3", b"!"],
17943            &[b"STRLEN", b"k3"],
17944            &[b"SETRANGE", b"k3", b"1", b"XY"],
17945            &[b"GETRANGE", b"k3", b"0", b"-1"],
17946            &[b"INCR", b"n1"],
17947            &[b"INCRBY", b"n1", b"41"],
17948            &[b"DECRBY", b"n1", b"2"],
17949            &[b"INCRBYFLOAT", b"f1", b"1.5"],
17950            &[b"SETEX", b"e1", b"100", b"v"],
17951            &[b"PSETEX", b"e2", b"100000", b"v"],
17952            &[b"GETEX", b"e1", b"PERSIST"],
17953            &[b"GETDEL", b"k2"],
17954            &[b"GET", b"k2"],
17955            &[b"DIGEST", b"k1"],
17956            &[b"DELEX", b"k3"],
17957            // The five that name more than one key, which are the ones that
17958            // cannot be handed one stripe at all.
17959            &[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"],
17960            &[b"MGET", b"a", b"b", b"c", b"missing"],
17961            &[b"MSETNX", b"d", b"4", b"e", b"5"],
17962            &[b"MSETNX", b"e", b"6", b"f", b"7"],
17963            &[b"MGET", b"d", b"e", b"f"],
17964            &[b"MSETEX", b"2", b"g", b"7", b"h", b"8", b"NX"],
17965            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"NX"],
17966            &[b"MSETEX", b"2", b"g", b"9", b"h", b"9", b"XX"],
17967            &[b"MGET", b"g", b"h"],
17968            &[b"SET", b"s1", b"ohmytext"],
17969            &[b"SET", b"s2", b"mynewtext"],
17970            &[b"LCS", b"s1", b"s2"],
17971            &[b"LCS", b"s1", b"s2", b"LEN"],
17972            &[b"LCS", b"s1", b"s2", b"IDX", b"MINMATCHLEN", b"4"],
17973            &[b"LCS", b"s1", b"s2", b"IDX", b"WITHMATCHLEN"],
17974            &[b"LCS", b"s1", b"gone"],
17975            // And the errors, which have to be the same errors.
17976            &[b"MSET", b"odd"],
17977            &[b"LCS", b"s1", b"s2", b"LEN", b"IDX"],
17978            &[b"MGET"],
17979        ];
17980
17981        let mut one = Fixture::new();
17982        let mut many = Fixture::striped(8);
17983        for parts in script {
17984            let a = one.run(parts);
17985            let b = many.run(parts);
17986            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
17987        }
17988    }
17989
17990    /// The keys of an `MSET` really do end up on different stripes.
17991    ///
17992    /// Without this the test above could pass on a server whose stripe number
17993    /// happened to be a constant, which is a striped database in name only.
17994    #[test]
17995    fn a_striped_database_spreads_the_keys_it_is_given() {
17996        let mut f = Fixture::striped(8);
17997        for i in 0..256 {
17998            let key = format!("key:{i}");
17999            f.run(&[b"SET", key.as_bytes(), b"v"]);
18000        }
18001        assert_eq!(f.run(&[b"DBSIZE"]), ":256\r\n");
18002    }
18003
18004    /// A wrong type stops an `MGET` no more than it does on one stripe: the key
18005    /// that is not a string comes back nil and the rest of the reply is intact.
18006    #[test]
18007    fn a_wrong_type_in_the_middle_of_an_mget_is_still_one_nil() {
18008        let mut one = Fixture::new();
18009        let mut many = Fixture::striped(8);
18010        for f in [&mut one, &mut many] {
18011            f.run(&[b"SET", b"str", b"v"]);
18012            // Planted rather than pushed. `RPUSH` belongs to the list group,
18013            // which has not been taught about stripes yet and would refuse the
18014            // wide server. What is under test is what `MGET` does when it walks
18015            // onto a key that is not a string, and that does not care how the
18016            // key got there.
18017            f.server
18018                .striped(0)
18019                .at(b"list")
18020                .push(b"list", yo_kv::End::Right, core::iter::once(&b"v"[..]))
18021                .expect("a new list");
18022        }
18023        assert_eq!(
18024            one.run(&[b"MGET", b"str", b"list", b"gone"]),
18025            many.run(&[b"MGET", b"str", b"list", b"gone"])
18026        );
18027    }
18028
18029    /// The same claim for the keyspace group, and the same way of checking it.
18030    ///
18031    /// `SORT` is not in the script because it is the one command in that file
18032    /// that has not been taught about stripes, and `SCAN`, `KEYS` and
18033    /// `RANDOMKEY` are not in it either, because those three do not promise an
18034    /// order and comparing two replies byte for byte would be asserting one.
18035    /// They get tests of their own below.
18036    #[test]
18037    fn the_keyspace_group_answers_the_same_however_many_stripes_there_are() {
18038        let script: &[&[&[u8]]] = &[
18039            &[b"SET", b"k1", b"v1"],
18040            &[b"SET", b"k2", b"v2"],
18041            &[b"EXISTS", b"k1", b"k2", b"k1", b"gone"],
18042            &[b"TYPE", b"k1"],
18043            &[b"TYPE", b"gone"],
18044            &[b"TOUCH", b"k1", b"k2", b"k1", b"gone"],
18045            &[b"EXPIRE", b"k1", b"100"],
18046            &[b"TTL", b"k1"],
18047            &[b"EXPIRE", b"k1", b"200", b"NX"],
18048            &[b"PERSIST", b"k1"],
18049            &[b"TTL", b"k1"],
18050            &[b"PEXPIREAT", b"k2", b"1900000000000"],
18051            &[b"EXPIRETIME", b"k2"],
18052            &[b"PEXPIRETIME", b"k2"],
18053            &[b"PERSIST", b"k2"],
18054            &[b"OBJECT", b"ENCODING", b"k1"],
18055            &[b"OBJECT", b"REFCOUNT", b"k1"],
18056            &[b"OBJECT", b"IDLETIME", b"k1"],
18057            &[b"OBJECT", b"FREQ", b"k1"],
18058            &[b"OBJECT", b"ENCODING", b"gone"],
18059            &[b"OBJECT", b"HELP"],
18060            &[b"RENAME", b"k1", b"k9"],
18061            &[b"GET", b"k9"],
18062            &[b"RENAME", b"gone", b"x"],
18063            &[b"RENAMENX", b"k9", b"k2"],
18064            &[b"RENAMENX", b"k9", b"k8"],
18065            &[b"GET", b"k8"],
18066            &[b"COPY", b"k8", b"c1"],
18067            &[b"COPY", b"k8", b"c1"],
18068            &[b"COPY", b"k8", b"c1", b"REPLACE"],
18069            &[b"COPY", b"k8", b"k8"],
18070            &[b"COPY", b"gone", b"c2"],
18071            &[b"COPY", b"k8", b"k8", b"DB", b"1"],
18072            &[b"COPY", b"k8", b"c9", b"DB", b"9"],
18073            &[b"MOVE", b"c1", b"1"],
18074            &[b"MOVE", b"c1", b"1"],
18075            &[b"MOVE", b"k8", b"0"],
18076            &[b"DEL", b"k2", b"gone"],
18077            &[b"UNLINK", b"k8", b"k8"],
18078            &[b"DBSIZE"],
18079        ];
18080
18081        let mut one = Fixture::new();
18082        let mut many = Fixture::striped(8);
18083        for parts in script {
18084            let a = one.run(parts);
18085            let b = many.run(parts);
18086            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18087        }
18088
18089        // `RESTORE` needs bytes a client would have got from a `DUMP`, so the
18090        // payload is taken from the store rather than parsed back out of a
18091        // reply that is not text. Both servers dump the same key and the bytes
18092        // are the same bytes, which is the first half of what is being checked
18093        // here.
18094        for f in [&mut one, &mut many] {
18095            f.run(&[b"SET", b"d1", b"payload"]);
18096            let payload = f
18097                .server
18098                .striped(0)
18099                .at(b"d1")
18100                .dump(b"d1")
18101                .expect("a key that is there");
18102            assert!(
18103                f.run(&[b"DUMP", b"d1"])
18104                    .starts_with(&format!("${}", payload.len())),
18105                "a payload of the length the store gave"
18106            );
18107            assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
18108            assert_eq!(f.run(&[b"RESTORE", b"d2", b"0", &payload]), "+OK\r\n");
18109            assert_eq!(f.run(&[b"GET", b"d2"]), "$7\r\npayload\r\n");
18110            assert_eq!(
18111                f.run(&[b"RESTORE", b"d2", b"0", &payload]),
18112                "-BUSYKEY Target key name already exists.\r\n"
18113            );
18114            assert_eq!(
18115                f.run(&[b"RESTORE", b"d3", b"0", b"rubbish"]),
18116                "-ERR DUMP payload version or checksum are wrong\r\n"
18117            );
18118        }
18119    }
18120
18121    /// A `SCAN` of a database of eight stripes comes back with all of it.
18122    ///
18123    /// The cursor is the thing under test. It has to carry the stripe as well
18124    /// as the place in it, so a client that stops at one stripe and comes back
18125    /// carries on in that stripe and not at the top of the database, and the
18126    /// walk has to end once rather than eight times.
18127    #[test]
18128    fn a_scan_of_a_striped_database_walks_all_of_it() {
18129        let mut f = Fixture::striped(8);
18130        for i in 0..500 {
18131            let key = format!("key:{i}");
18132            f.run(&[b"SET", key.as_bytes(), b"v"]);
18133        }
18134
18135        let mut seen = Vec::new();
18136        let mut cursor = "0".to_owned();
18137        let mut calls = 0;
18138        loop {
18139            let reply = f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"10"]);
18140            let (next, keys) = scan_reply(&reply);
18141            seen.extend(keys);
18142            cursor = next;
18143            calls += 1;
18144            assert!(calls < 5_000, "a scan that will not finish");
18145            if cursor == "0" {
18146                break;
18147            }
18148        }
18149        seen.sort();
18150        assert_eq!(seen.len(), 500, "a quiet scan answered a key twice");
18151        assert_eq!(seen, sorted(&f.run(&[b"KEYS", b"*"])));
18152
18153        // And the options still work when the walk is over several stripes,
18154        // since a `MATCH` is applied to keys a stripe handed up and a `TYPE` is
18155        // applied by each stripe on the way.
18156        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"MATCH", b"key:4?"]);
18157        let (_, keys) = scan_reply(&reply);
18158        assert_eq!(keys.len(), 10, "key:40 through key:49");
18159        let reply = f.run(&[b"SCAN", b"0", b"COUNT", b"1000", b"TYPE", b"list"]);
18160        let (_, keys) = scan_reply(&reply);
18161        assert!(keys.is_empty(), "nothing here is a list");
18162    }
18163
18164    /// `RANDOMKEY` on a striped database answers a key from any of the stripes.
18165    ///
18166    /// The draw picks the stripe first, so the thing that can go wrong is that
18167    /// it always picks the same one, and two hundred draws over eight stripes
18168    /// would make that obvious.
18169    #[test]
18170    fn a_random_key_can_come_from_any_stripe() {
18171        let mut f = Fixture::striped(8);
18172        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
18173        for i in 0..200 {
18174            let key = format!("key:{i}");
18175            f.run(&[b"SET", key.as_bytes(), b"v"]);
18176        }
18177        let mut homes = std::collections::HashSet::new();
18178        for _ in 0..200 {
18179            let got = f.run(&[b"RANDOMKEY"]);
18180            let key = got.split("\r\n").nth(1).expect("a key").to_owned();
18181            assert_eq!(f.run(&[b"EXISTS", key.as_bytes()]), ":1\r\n");
18182            homes.insert(f.server.striped(0).stripe_of(key.as_bytes()));
18183        }
18184        assert_eq!(homes.len(), 8, "some stripe was never drawn from");
18185    }
18186
18187    /// Two keys that are not on the same stripe, which is what `RENAME` and
18188    /// `COPY` have to cope with and what a test has to arrange rather than
18189    /// hope for.
18190    fn apart(f: &mut Fixture, src: &str) -> String {
18191        let home = f.server.striped(0).stripe_of(src.as_bytes());
18192        for i in 0..1_000 {
18193            let dst = format!("dst:{i}");
18194            if f.server.striped(0).stripe_of(dst.as_bytes()) != home {
18195                return dst;
18196            }
18197        }
18198        panic!("eight stripes and a thousand keys all landed in one place");
18199    }
18200
18201    /// A rename whose two keys are on two stripes moves the value, the deadline
18202    /// and, for a collection, the body itself.
18203    #[test]
18204    fn a_rename_across_stripes_takes_everything_with_it() {
18205        let mut f = Fixture::striped(8);
18206        let dst = apart(&mut f, "src");
18207        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18208
18209        f.run(&[b"SET", src, b"v"]);
18210        f.run(&[b"EXPIRE", src, b"100"]);
18211        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18212        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":1\r\n");
18213        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nv\r\n");
18214        assert_eq!(f.run(&[b"TTL", dst]), ":100\r\n", "the deadline came too");
18215
18216        // A list, because a string lives in its record and a collection lives
18217        // in a slab, and the second of those is the one that can be left
18218        // behind. Planted through the store, since the list group has not been
18219        // taught about stripes yet.
18220        f.server
18221            .striped(0)
18222            .at(src)
18223            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18224            .expect("a new list");
18225        assert_eq!(f.run(&[b"RENAME", src, dst]), "+OK\r\n");
18226        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n");
18227        assert_eq!(
18228            f.server.striped(0).at(dst).llen(dst).expect("a list"),
18229            2,
18230            "the members are on the stripe the key moved to"
18231        );
18232
18233        // And `RENAMENX` still refuses a destination that is taken, which is
18234        // the one answer the cross stripe path has to work out for itself.
18235        f.run(&[b"SET", src, b"v"]);
18236        assert_eq!(f.run(&[b"RENAMENX", src, dst]), ":0\r\n");
18237        assert_eq!(f.run(&[b"TYPE", dst]), "+list\r\n", "and left it alone");
18238        assert_eq!(f.run(&[b"GET", src]), "$1\r\nv\r\n", "and left the source");
18239    }
18240
18241    /// And a copy across two stripes leaves both keys behind it.
18242    #[test]
18243    fn a_copy_across_stripes_leaves_the_source_where_it_was() {
18244        let mut f = Fixture::striped(8);
18245        let dst = apart(&mut f, "src");
18246        let (src, dst) = (b"src".as_slice(), dst.as_bytes());
18247
18248        f.run(&[b"SET", src, b"v"]);
18249        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18250        assert_eq!(f.run(&[b"EXISTS", src, dst]), ":2\r\n");
18251        assert_eq!(
18252            f.run(&[b"COPY", src, dst]),
18253            ":0\r\n",
18254            "the destination is taken"
18255        );
18256        f.run(&[b"SET", src, b"w"]);
18257        assert_eq!(f.run(&[b"COPY", src, dst, b"REPLACE"]), ":1\r\n");
18258        assert_eq!(f.run(&[b"GET", dst]), "$1\r\nw\r\n");
18259
18260        // A collection is cloned rather than moved, so both keys have a body of
18261        // their own afterwards and writing to one does not show up in the
18262        // other.
18263        f.run(&[b"DEL", src, dst]);
18264        f.server
18265            .striped(0)
18266            .at(src)
18267            .push(src, yo_kv::End::Right, [&b"a"[..], &b"b"[..]].into_iter())
18268            .expect("a new list");
18269        assert_eq!(f.run(&[b"COPY", src, dst]), ":1\r\n");
18270        f.server
18271            .striped(0)
18272            .at(src)
18273            .push(src, yo_kv::End::Right, core::iter::once(&b"c"[..]))
18274            .expect("a list that is there");
18275        assert_eq!(f.server.striped(0).at(src).llen(src).expect("a list"), 3);
18276        assert_eq!(f.server.striped(0).at(dst).llen(dst).expect("a list"), 2);
18277    }
18278
18279    /// Every bitmap command, on one stripe and on eight, replies compared byte
18280    /// for byte.
18281    ///
18282    /// `BITOP` is the one that names more than one key and it is where the work
18283    /// went. The rest are single key commands that now find their own stripe,
18284    /// and they are here because the cheapest way to be sure the routing is
18285    /// right is to ask.
18286    #[test]
18287    fn the_bitmap_group_answers_the_same_however_many_stripes_there_are() {
18288        let script: &[&[&[u8]]] = &[
18289            &[b"SET", b"k1", b"foobar"],
18290            &[b"SETBIT", b"b1", b"7", b"1"],
18291            &[b"SETBIT", b"b1", b"7", b"0"],
18292            &[b"GETBIT", b"k1", b"6"],
18293            &[b"GETBIT", b"k1", b"100"],
18294            &[b"BITCOUNT", b"k1"],
18295            &[b"BITCOUNT", b"k1", b"0", b"0"],
18296            &[b"BITCOUNT", b"k1", b"5", b"30", b"BIT"],
18297            &[b"BITPOS", b"k1", b"1"],
18298            &[b"BITPOS", b"k1", b"0", b"2"],
18299            &[b"BITPOS", b"k1", b"1", b"2", b"-1", b"BIT"],
18300            &[
18301                b"BITFIELD",
18302                b"bf",
18303                b"SET",
18304                b"u8",
18305                b"0",
18306                b"255",
18307                b"GET",
18308                b"u8",
18309                b"0",
18310            ],
18311            &[
18312                b"BITFIELD",
18313                b"bf",
18314                b"OVERFLOW",
18315                b"SAT",
18316                b"INCRBY",
18317                b"u8",
18318                b"0",
18319                b"10",
18320            ],
18321            &[b"BITFIELD_RO", b"bf", b"GET", b"u8", b"0"],
18322            // The multi key one, over sources that are not on one stripe unless
18323            // eight stripes have folded into one.
18324            &[b"SET", b"s1", b"abc"],
18325            &[b"SET", b"s2", b"abd"],
18326            &[b"SET", b"s3", b"a"],
18327            &[b"BITOP", b"AND", b"d1", b"s1", b"s2"],
18328            &[b"GET", b"d1"],
18329            &[b"BITOP", b"OR", b"d2", b"s1", b"s2", b"s3"],
18330            &[b"GET", b"d2"],
18331            &[b"BITOP", b"XOR", b"d3", b"s1", b"s2"],
18332            &[b"STRLEN", b"d3"],
18333            &[b"BITOP", b"NOT", b"d4", b"s1"],
18334            &[b"STRLEN", b"d4"],
18335            &[b"BITOP", b"DIFF", b"d5", b"s1", b"s2"],
18336            &[b"BITOP", b"DIFF1", b"d6", b"s1", b"s2"],
18337            &[b"BITOP", b"ANDOR", b"d7", b"s1", b"s2"],
18338            &[b"BITOP", b"ONE", b"d8", b"s1", b"s2"],
18339            // A source that is not there reads as empty, and a result with
18340            // nothing in it deletes the destination rather than writing one.
18341            &[b"BITOP", b"AND", b"d1", b"gone", b"also-gone"],
18342            &[b"EXISTS", b"d1"],
18343            &[b"BITOP", b"OR", b"d9", b"s1", b"gone"],
18344            &[b"GET", b"d9"],
18345            // And the errors, which have to be the same errors. The key that
18346            // is not a string is planted below rather than pushed here, since
18347            // the list group has not been taught about stripes yet.
18348            &[b"BITOP", b"AND", b"d1", b"s1", b"list"],
18349            &[b"BITOP", b"AND", b"list", b"s1", b"s2"],
18350            &[b"BITOP", b"NOT", b"d1", b"s1", b"s2"],
18351            &[b"BITOP", b"DIFF", b"d1", b"s1"],
18352            &[b"BITOP", b"NOPE", b"d1", b"s1"],
18353            &[b"BITCOUNT", b"list"],
18354            &[b"BITFIELD_RO", b"bf", b"SET", b"u8", b"0", b"1"],
18355        ];
18356
18357        let mut one = Fixture::new();
18358        let mut many = Fixture::striped(8);
18359        for f in [&mut one, &mut many] {
18360            plant_list(f, b"list");
18361        }
18362        for parts in script {
18363            let a = one.run(parts);
18364            let b = many.run(parts);
18365            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18366        }
18367    }
18368
18369    /// A list under `key`, put there through the store.
18370    ///
18371    /// What a test does when it wants a key of the wrong type on a striped
18372    /// server, because the command that would make one is in a group that has
18373    /// not been taught about stripes yet.
18374    fn plant_list(f: &mut Fixture, key: &[u8]) {
18375        f.server
18376            .striped(0)
18377            .at(key)
18378            .push(key, yo_kv::End::Right, core::iter::once(&b"x"[..]))
18379            .expect("a new list");
18380    }
18381
18382    /// A `BITOP` whose keys are on two stripes reads both of them.
18383    ///
18384    /// The test above spreads its keys by hashing and would still pass if one
18385    /// stripe were doing all the work, since the answers would be the same. This
18386    /// one puts the destination and the two sources where they are known not to
18387    /// share a stripe.
18388    #[test]
18389    fn a_bitop_across_stripes_reads_every_source() {
18390        let mut f = Fixture::striped(8);
18391        let other = apart(&mut f, "src");
18392        let (src, far) = (b"src".as_slice(), other.as_bytes());
18393        assert_ne!(
18394            f.server.striped(0).stripe_of(src),
18395            f.server.striped(0).stripe_of(far),
18396            "the two keys are the point of the test"
18397        );
18398
18399        f.run(&[b"SET", src, b"abc"]);
18400        f.run(&[b"SET", far, b"abd"]);
18401        assert_eq!(f.run(&[b"BITOP", b"AND", far, src, far]), ":3\r\n");
18402        assert_eq!(
18403            f.run(&[b"GET", far]),
18404            "$3\r\nab`\r\n",
18405            "a destination that is also a source"
18406        );
18407        f.run(&[b"SET", far, b"abd"]);
18408        assert_eq!(f.run(&[b"BITOP", b"XOR", src, src, far]), ":3\r\n");
18409        assert_eq!(
18410            f.run(&[b"GET", src]),
18411            "$3\r\n\0\0\x07\r\n",
18412            "and the other way round"
18413        );
18414
18415        // A result of nothing deletes a destination on whatever stripe it is
18416        // on, and a source of the wrong type is refused before anything is
18417        // written.
18418        f.run(&[b"SET", src, b"abc"]);
18419        f.run(&[b"DEL", far]);
18420        assert_eq!(f.run(&[b"BITOP", b"AND", src, far, b"gone"]), ":0\r\n");
18421        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n");
18422        f.run(&[b"SET", src, b"abc"]);
18423        f.run(&[b"DEL", far]);
18424        plant_list(&mut f, far);
18425        assert_eq!(
18426            f.run(&[b"BITOP", b"OR", b"out", src, far]),
18427            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18428        );
18429        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
18430    }
18431
18432    /// Every HyperLogLog command, on one stripe and on eight.
18433    #[test]
18434    fn the_hyperloglog_group_answers_the_same_however_many_stripes_there_are() {
18435        let script: &[&[&[u8]]] = &[
18436            &[b"PFADD", b"h1", b"a", b"b", b"c"],
18437            &[b"PFADD", b"h1", b"a"],
18438            &[b"PFADD", b"h2"],
18439            &[b"PFADD", b"h2", b"c", b"d", b"e"],
18440            &[b"PFCOUNT", b"h1"],
18441            &[b"PFCOUNT", b"h2"],
18442            &[b"PFCOUNT", b"missing"],
18443            // The two that name more than one key.
18444            &[b"PFCOUNT", b"h1", b"h2"],
18445            &[b"PFCOUNT", b"h1", b"missing"],
18446            &[b"PFMERGE", b"m", b"h1", b"h2"],
18447            &[b"PFCOUNT", b"m"],
18448            &[b"STRLEN", b"m"],
18449            &[b"PFMERGE", b"m"],
18450            &[b"PFCOUNT", b"m"],
18451            &[b"PFMERGE", b"m2", b"missing"],
18452            &[b"PFCOUNT", b"m2"],
18453            // The debugging ones, which are single key and change what they
18454            // look at.
18455            &[b"PFDEBUG", b"ENCODING", b"h1"],
18456            &[b"PFDEBUG", b"DECODE", b"h1"],
18457            &[b"PFDEBUG", b"TODENSE", b"h1"],
18458            &[b"PFDEBUG", b"ENCODING", b"h1"],
18459            &[b"PFDEBUG", b"TODENSE", b"h1"],
18460            &[b"PFCOUNT", b"h1", b"h2"],
18461            &[b"PFSELFTEST"],
18462            // And the errors.
18463            &[b"SET", b"plain", b"not a sketch at all"],
18464            &[b"PFADD", b"plain", b"a"],
18465            &[b"PFCOUNT", b"plain"],
18466            &[b"PFCOUNT", b"h1", b"plain"],
18467            &[b"PFMERGE", b"plain", b"h1"],
18468            &[b"PFMERGE", b"m", b"plain"],
18469            &[b"PFDEBUG", b"ENCODING", b"gone"],
18470            &[b"PFDEBUG", b"NOPE", b"h1"],
18471        ];
18472
18473        let mut one = Fixture::new();
18474        let mut many = Fixture::striped(8);
18475        for parts in script {
18476            let a = one.run(parts);
18477            let b = many.run(parts);
18478            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18479        }
18480    }
18481
18482    /// Every set command, on one stripe and on eight.
18483    ///
18484    /// The commands that answer members answer them in whatever order the set
18485    /// or the table they were built in holds them, so those replies are
18486    /// compared as sets. Everything else is compared byte for byte. Two servers
18487    /// agreeing on the order would be a fact about the tables and not about the
18488    /// answer, and asserting it would make this test fail for a reason nobody
18489    /// cares about.
18490    #[test]
18491    fn the_set_group_answers_the_same_however_many_stripes_there_are() {
18492        const UNORDERED: [&str; 4] = ["SMEMBERS", "SINTER", "SUNION", "SDIFF"];
18493        let script: &[&[&[u8]]] = &[
18494            &[b"SADD", b"s1", b"a", b"b", b"c"],
18495            &[b"SADD", b"s1", b"a"],
18496            &[b"SADD", b"s2", b"b", b"c", b"d"],
18497            &[b"SADD", b"ints", b"1", b"2", b"3"],
18498            &[b"SCARD", b"s1"],
18499            &[b"SISMEMBER", b"s1", b"a"],
18500            &[b"SISMEMBER", b"s1", b"z"],
18501            &[b"SMISMEMBER", b"s1", b"a", b"z", b"c"],
18502            &[b"SMEMBERS", b"s1"],
18503            &[b"SREM", b"s1", b"c"],
18504            &[b"SADD", b"s1", b"c"],
18505            &[b"SSCAN", b"s1", b"0"],
18506            &[b"SSCAN", b"s1", b"0", b"COUNT", b"100", b"MATCH", b"a*"],
18507            // The two draws, on a set of one member, which is the only shape
18508            // whose answer two servers have to agree on.
18509            &[b"SADD", b"one", b"m"],
18510            &[b"SRANDMEMBER", b"one"],
18511            &[b"SRANDMEMBER", b"one", b"-3"],
18512            &[b"SRANDMEMBER", b"gone"],
18513            &[b"SPOP", b"one"],
18514            &[b"SPOP", b"one"],
18515            &[b"SPOP", b"gone", b"2"],
18516            // The one that names two keys.
18517            &[b"SMOVE", b"s1", b"s2", b"a"],
18518            &[b"SMOVE", b"s1", b"s2", b"zzz"],
18519            &[b"SMOVE", b"gone", b"s2", b"a"],
18520            &[b"SMEMBERS", b"s1"],
18521            &[b"SMEMBERS", b"s2"],
18522            // The algebra.
18523            &[b"SINTER", b"s1", b"s2"],
18524            &[b"SUNION", b"s1", b"s2"],
18525            &[b"SDIFF", b"s2", b"s1"],
18526            &[b"SINTER", b"s1", b"gone"],
18527            &[b"SUNION", b"s1", b"gone"],
18528            &[b"SDIFF", b"gone", b"s1"],
18529            &[b"SINTER", b"ints", b"s1"],
18530            &[b"SINTERCARD", b"2", b"s1", b"s2"],
18531            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"1"],
18532            &[b"SUNIONCARD", b"2", b"s1", b"s2"],
18533            &[b"SDIFFCARD", b"2", b"s2", b"s1"],
18534            &[b"SINTERSTORE", b"d1", b"s1", b"s2"],
18535            &[b"SMEMBERS", b"d1"],
18536            &[b"SUNIONSTORE", b"d2", b"s1", b"s2"],
18537            &[b"SCARD", b"d2"],
18538            &[b"SDIFFSTORE", b"d3", b"s2", b"s1"],
18539            &[b"SCARD", b"d3"],
18540            // An empty result deletes the destination rather than storing a
18541            // set with nothing in it.
18542            &[b"SINTERSTORE", b"d4", b"s1", b"gone"],
18543            &[b"EXISTS", b"d4"],
18544            // And a destination that is also a source.
18545            &[b"SUNIONSTORE", b"s2", b"s1", b"s2"],
18546            &[b"SCARD", b"s2"],
18547            // The errors, which have to be the same errors.
18548            &[b"SET", b"str", b"v"],
18549            &[b"SADD", b"str", b"a"],
18550            &[b"SINTER", b"s1", b"str"],
18551            &[b"SINTERSTORE", b"d5", b"s1", b"str"],
18552            &[b"EXISTS", b"d5"],
18553            &[b"SMOVE", b"str", b"s2", b"a"],
18554            &[b"SMOVE", b"s1", b"str", b"b"],
18555            &[b"SMOVE", b"gone", b"str", b"b"],
18556            &[b"SINTERCARD", b"0", b"s1"],
18557            &[b"SINTERCARD", b"3", b"s1", b"s2"],
18558            &[b"SINTERCARD", b"2", b"s1", b"s2", b"LIMIT", b"-1"],
18559            &[b"SPOP", b"s1", b"-1"],
18560        ];
18561
18562        let mut one = Fixture::new();
18563        let mut many = Fixture::striped(8);
18564        for parts in script {
18565            let a = one.run(parts);
18566            let b = many.run(parts);
18567            let name = String::from_utf8_lossy(parts[0]).to_uppercase();
18568            if UNORDERED.contains(&name.as_str()) && a.starts_with(['*', '~']) {
18569                assert_eq!(sorted(&a), sorted(&b), "{name}");
18570            } else {
18571                assert_eq!(a, b, "{name}");
18572            }
18573        }
18574    }
18575
18576    /// The algebra over sets that are known to be on different stripes.
18577    #[test]
18578    fn a_set_operation_across_stripes_reads_every_set() {
18579        let mut f = Fixture::striped(8);
18580        let second = apart(&mut f, "s1");
18581        let third = apart(&mut f, &second);
18582        let (s1, s2, s3) = (b"s1".as_slice(), second.as_bytes(), third.as_bytes());
18583
18584        f.run(&[b"SADD", s1, b"a", b"b", b"c"]);
18585        f.run(&[b"SADD", s2, b"b", b"c", b"d"]);
18586        assert_eq!(sorted(&f.run(&[b"SINTER", s1, s2])), ["b", "c"]);
18587        assert_eq!(
18588            sorted(&f.run(&[b"SUNION", s1, s2])),
18589            ["a", "b", "c", "d"],
18590            "a union of two stripes is both of them"
18591        );
18592        assert_eq!(sorted(&f.run(&[b"SDIFF", s1, s2])), ["a"]);
18593        assert_eq!(f.run(&[b"SINTERCARD", b"2", s1, s2]), ":2\r\n");
18594        assert_eq!(f.run(&[b"SUNIONCARD", b"2", s1, s2]), ":4\r\n");
18595        assert_eq!(f.run(&[b"SDIFFCARD", b"2", s1, s2]), ":1\r\n");
18596
18597        // A destination on a third stripe, and then one that is also a source.
18598        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, s2]), ":2\r\n");
18599        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["b", "c"]);
18600        assert_eq!(f.run(&[b"SUNIONSTORE", s2, s1, s2]), ":4\r\n");
18601        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s2])), ["a", "b", "c", "d"]);
18602        assert_eq!(f.run(&[b"SDIFFSTORE", s3, s2, s1]), ":1\r\n");
18603        assert_eq!(sorted(&f.run(&[b"SMEMBERS", s3])), ["d"]);
18604
18605        // An empty result deletes a destination wherever it is, and a key of
18606        // the wrong type stops the command before the destination is touched.
18607        assert_eq!(f.run(&[b"SINTERSTORE", s3, s1, b"gone"]), ":0\r\n");
18608        assert_eq!(f.run(&[b"EXISTS", s3]), ":0\r\n");
18609        f.run(&[b"SET", s3, b"v"]);
18610        assert_eq!(
18611            f.run(&[b"SINTER", s1, s3]),
18612            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18613        );
18614        assert_eq!(f.run(&[b"GET", s3]), "$1\r\nv\r\n", "and left it alone");
18615    }
18616
18617    /// An `SMOVE` whose two keys are on two stripes.
18618    #[test]
18619    fn a_move_across_stripes_takes_the_member_with_it() {
18620        let mut f = Fixture::striped(8);
18621        let other = apart(&mut f, "src");
18622        let (src, dst) = (b"src".as_slice(), other.as_bytes());
18623
18624        f.run(&[b"SADD", src, b"a", b"b"]);
18625        f.run(&[b"SADD", dst, b"c"]);
18626        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":1\r\n");
18627        assert_eq!(sorted(&f.run(&[b"SMEMBERS", src])), ["b"]);
18628        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["a", "c"]);
18629        assert_eq!(f.run(&[b"SMOVE", src, dst, b"a"]), ":0\r\n", "it has gone");
18630
18631        // A destination that is not there is created on its own stripe, and a
18632        // source that loses its last member is deleted from its own.
18633        f.run(&[b"DEL", dst]);
18634        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":1\r\n");
18635        assert_eq!(f.run(&[b"EXISTS", src]), ":0\r\n", "the source is empty");
18636        assert_eq!(sorted(&f.run(&[b"SMEMBERS", dst])), ["b"]);
18637
18638        // And a source that is not there answers zero without ever asking what
18639        // the destination holds, which is Redis's order and not the obvious
18640        // one.
18641        f.run(&[b"SET", dst, b"v"]);
18642        assert_eq!(f.run(&[b"SMOVE", src, dst, b"b"]), ":0\r\n");
18643        f.run(&[b"SADD", src, b"b"]);
18644        assert_eq!(
18645            f.run(&[b"SMOVE", src, dst, b"b"]),
18646            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18647        );
18648    }
18649
18650    /// A count and a merge over sketches that are known to be on two stripes.
18651    #[test]
18652    fn a_pfcount_and_a_pfmerge_reach_across_stripes() {
18653        let mut f = Fixture::striped(8);
18654        let other = apart(&mut f, "src");
18655        let (src, far) = (b"src".as_slice(), other.as_bytes());
18656
18657        for i in 0..150 {
18658            let ele = format!("e:{i}");
18659            f.run(&[b"PFADD", src, ele.as_bytes()]);
18660        }
18661        for i in 150..200 {
18662            let ele = format!("e:{i}");
18663            f.run(&[b"PFADD", far, ele.as_bytes()]);
18664        }
18665        // The three numbers a real server gives for these elements, which are
18666        // the numbers the single stripe tests in the keyspace crate check too.
18667        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n");
18668        assert_eq!(f.run(&[b"PFCOUNT", far]), ":49\r\n");
18669        assert_eq!(f.run(&[b"PFCOUNT", src, far]), ":199\r\n");
18670
18671        // A merge whose destination is on a third stripe, and then one that
18672        // writes into a source.
18673        let dest = apart(&mut f, &other);
18674        assert_eq!(f.run(&[b"PFMERGE", dest.as_bytes(), src, far]), "+OK\r\n");
18675        assert_eq!(f.run(&[b"PFCOUNT", dest.as_bytes()]), ":199\r\n");
18676        assert_eq!(f.run(&[b"PFMERGE", far, src]), "+OK\r\n");
18677        assert_eq!(f.run(&[b"PFCOUNT", far]), ":199\r\n", "and kept its own");
18678        assert_eq!(f.run(&[b"PFCOUNT", src]), ":151\r\n", "and left the source");
18679    }
18680
18681    /// Every sorted set command, on one stripe and on eight.
18682    ///
18683    /// Every reply here is compared byte for byte, unlike the set group, because
18684    /// a sorted set answers in rank order and members sharing a score come out
18685    /// in the order of their bytes. There is nothing left for the table the
18686    /// answer was built in to decide.
18687    #[test]
18688    fn the_sorted_set_group_answers_the_same_however_many_stripes_there_are() {
18689        let script: &[&[&[u8]]] = &[
18690            &[b"ZADD", b"z1", b"1", b"a", b"2", b"b", b"3", b"c"],
18691            &[b"ZADD", b"z1", b"NX", b"9", b"a"],
18692            &[b"ZADD", b"z1", b"XX", b"CH", b"5", b"a"],
18693            &[b"ZADD", b"z1", b"GT", b"CH", b"1", b"a"],
18694            &[b"ZADD", b"z1", b"INCR", b"2", b"a"],
18695            &[b"ZINCRBY", b"z1", b"1.5", b"b"],
18696            &[b"ZADD", b"z2", b"1", b"b", b"2", b"c", b"3", b"d"],
18697            &[b"ZADD", b"lex", b"0", b"a", b"0", b"b", b"0", b"c"],
18698            &[b"ZADD", b"one", b"1", b"m"],
18699            &[b"ZCARD", b"z1"],
18700            &[b"ZCARD", b"gone"],
18701            &[b"ZSCORE", b"z1", b"a"],
18702            &[b"ZSCORE", b"z1", b"zz"],
18703            &[b"ZMSCORE", b"z1", b"a", b"zz", b"c"],
18704            &[b"ZRANK", b"z1", b"c"],
18705            &[b"ZRANK", b"z1", b"c", b"WITHSCORE"],
18706            &[b"ZREVRANK", b"z1", b"c"],
18707            &[b"ZRANK", b"z1", b"gone"],
18708            &[b"ZCOUNT", b"z1", b"-inf", b"+inf"],
18709            &[b"ZCOUNT", b"z1", b"(1", b"3"],
18710            &[b"ZLEXCOUNT", b"lex", b"-", b"+"],
18711            // The range commands, which are one parse and one walk.
18712            &[b"ZRANGE", b"z1", b"0", b"-1"],
18713            &[b"ZRANGE", b"z1", b"0", b"-1", b"WITHSCORES"],
18714            &[b"ZRANGE", b"z1", b"1", b"9", b"BYSCORE"],
18715            &[b"ZRANGE", b"z1", b"9", b"1", b"BYSCORE", b"REV"],
18716            &[b"ZRANGE", b"lex", b"[a", b"(c", b"BYLEX"],
18717            &[b"ZREVRANGE", b"z1", b"0", b"-1"],
18718            &[
18719                b"ZRANGEBYSCORE",
18720                b"z1",
18721                b"-inf",
18722                b"+inf",
18723                b"LIMIT",
18724                b"1",
18725                b"1",
18726            ],
18727            &[b"ZREVRANGEBYLEX", b"lex", b"+", b"-"],
18728            &[b"ZSCAN", b"z1", b"0"],
18729            &[b"ZSCAN", b"z1", b"0", b"MATCH", b"a*", b"COUNT", b"100"],
18730            // The draw, on a sorted set of one member, which is the only shape
18731            // whose answer two servers have to agree on.
18732            &[b"ZRANDMEMBER", b"one"],
18733            &[b"ZRANDMEMBER", b"one", b"-3", b"WITHSCORES"],
18734            &[b"ZRANDMEMBER", b"gone"],
18735            // The one that copies a window into another key.
18736            &[b"ZRANGESTORE", b"d0", b"z1", b"0", b"1"],
18737            &[b"ZRANGE", b"d0", b"0", b"-1", b"WITHSCORES"],
18738            &[b"ZRANGESTORE", b"d0", b"z1", b"5", b"1"],
18739            &[b"EXISTS", b"d0"],
18740            // The algebra, in both its shapes.
18741            &[b"ZUNION", b"2", b"z1", b"z2"],
18742            &[b"ZUNION", b"2", b"z1", b"z2", b"WITHSCORES"],
18743            &[
18744                b"ZUNION",
18745                b"2",
18746                b"z1",
18747                b"z2",
18748                b"WEIGHTS",
18749                b"2",
18750                b"3",
18751                b"AGGREGATE",
18752                b"MAX",
18753                b"WITHSCORES",
18754            ],
18755            &[b"ZINTER", b"2", b"z1", b"z2", b"WITHSCORES"],
18756            &[b"ZDIFF", b"2", b"z1", b"z2", b"WITHSCORES"],
18757            &[b"ZDIFF", b"2", b"gone", b"z1"],
18758            &[b"ZINTERCARD", b"2", b"z1", b"z2"],
18759            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"1"],
18760            &[b"ZUNIONSTORE", b"d1", b"2", b"z1", b"z2"],
18761            &[b"ZRANGE", b"d1", b"0", b"-1", b"WITHSCORES"],
18762            &[
18763                b"ZINTERSTORE",
18764                b"d2",
18765                b"2",
18766                b"z1",
18767                b"z2",
18768                b"AGGREGATE",
18769                b"MIN",
18770            ],
18771            &[b"ZRANGE", b"d2", b"0", b"-1", b"WITHSCORES"],
18772            &[b"ZDIFFSTORE", b"d3", b"2", b"z1", b"z2"],
18773            &[b"ZCARD", b"d3"],
18774            // An empty result deletes the destination rather than storing a
18775            // sorted set with nothing in it.
18776            &[b"ZINTERSTORE", b"d4", b"2", b"z1", b"gone"],
18777            &[b"EXISTS", b"d4"],
18778            // A plain set is a sorted set where every score is one, so it is a
18779            // legal input to all of these.
18780            &[b"SADD", b"plain", b"a", b"x"],
18781            &[b"ZUNIONSTORE", b"d5", b"2", b"z1", b"plain"],
18782            &[b"ZRANGE", b"d5", b"0", b"-1", b"WITHSCORES"],
18783            // And a destination that is also a source.
18784            &[b"ZUNIONSTORE", b"z2", b"2", b"z1", b"z2"],
18785            &[b"ZRANGE", b"z2", b"0", b"-1", b"WITHSCORES"],
18786            // The three removals and the two pops.
18787            &[b"ZREM", b"d5", b"x", b"nothere"],
18788            &[b"ZREMRANGEBYRANK", b"d5", b"0", b"0"],
18789            &[b"ZREMRANGEBYSCORE", b"d1", b"-inf", b"1"],
18790            &[b"ZREMRANGEBYLEX", b"lex", b"[a", b"[a"],
18791            &[b"ZPOPMIN", b"z1"],
18792            &[b"ZPOPMAX", b"z1", b"2"],
18793            &[b"ZPOPMIN", b"gone"],
18794            &[b"ZMPOP", b"2", b"gone", b"z2", b"MIN"],
18795            &[b"ZMPOP", b"2", b"gone", b"nothere", b"MAX", b"COUNT", b"2"],
18796            // The errors, which have to be the same errors.
18797            &[b"SET", b"str", b"v"],
18798            &[b"ZADD", b"str", b"1", b"a"],
18799            &[b"ZSCORE", b"str", b"a"],
18800            &[b"ZADD", b"z1", b"nan", b"a"],
18801            &[b"ZUNION", b"2", b"z1", b"str"],
18802            &[b"ZUNIONSTORE", b"d6", b"2", b"z1", b"str"],
18803            &[b"EXISTS", b"d6"],
18804            &[b"ZINTERCARD", b"0", b"z1"],
18805            &[b"ZINTERCARD", b"2", b"z1", b"z2", b"LIMIT", b"-1"],
18806            &[b"ZRANGESTORE", b"d7", b"str", b"0", b"-1"],
18807            &[b"ZMPOP", b"1", b"str", b"MIN"],
18808            &[b"ZPOPMIN", b"z1", b"-1"],
18809        ];
18810
18811        let mut one = Fixture::new();
18812        let mut many = Fixture::striped(8);
18813        for parts in script {
18814            let a = one.run(parts);
18815            let b = many.run(parts);
18816            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
18817        }
18818    }
18819
18820    /// The algebra over sorted sets that are known to be on different stripes.
18821    #[test]
18822    fn a_sorted_set_operation_across_stripes_reads_every_input() {
18823        let mut f = Fixture::striped(8);
18824        let second = apart(&mut f, "z1");
18825        let third = apart(&mut f, &second);
18826        let (z1, z2, z3) = (b"z1".as_slice(), second.as_bytes(), third.as_bytes());
18827
18828        f.run(&[b"ZADD", z1, b"1", b"a", b"2", b"b"]);
18829        f.run(&[b"ZADD", z2, b"3", b"b", b"4", b"c"]);
18830        // a is 1, c is 4, b is 2 and 3 added together, which is the order they
18831        // come out in and the answer that says both stripes were read.
18832        assert_eq!(
18833            f.run(&[b"ZUNION", b"2", z1, z2]),
18834            "*3\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n"
18835        );
18836        assert_eq!(f.run(&[b"ZINTER", b"2", z1, z2]), "*1\r\n$1\r\nb\r\n");
18837        assert_eq!(f.run(&[b"ZDIFF", b"2", z1, z2]), "*1\r\n$1\r\na\r\n");
18838        assert_eq!(f.run(&[b"ZINTERCARD", b"2", z1, z2]), ":1\r\n");
18839        assert_eq!(
18840            f.run(&[b"ZINTERCARD", b"2", z1, z2, b"LIMIT", b"1"]),
18841            ":1\r\n"
18842        );
18843
18844        // A destination on a third stripe, and the weights and the aggregate
18845        // reaching every input.
18846        assert_eq!(f.run(&[b"ZUNIONSTORE", z3, b"2", z1, z2]), ":3\r\n");
18847        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n5\r\n");
18848        assert_eq!(
18849            f.run(&[
18850                b"ZUNIONSTORE",
18851                z3,
18852                b"2",
18853                z1,
18854                z2,
18855                b"WEIGHTS",
18856                b"2",
18857                b"3",
18858                b"AGGREGATE",
18859                b"MAX"
18860            ]),
18861            ":3\r\n"
18862        );
18863        assert_eq!(f.run(&[b"ZSCORE", z3, b"b"]), "$1\r\n9\r\n");
18864        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, z2]), ":1\r\n");
18865        assert_eq!(f.run(&[b"ZCARD", z3]), ":1\r\n");
18866        assert_eq!(f.run(&[b"ZDIFFSTORE", z3, b"2", z2, z1]), ":1\r\n");
18867        assert_eq!(f.run(&[b"ZSCORE", z3, b"c"]), "$1\r\n4\r\n");
18868
18869        // A pop over keys on several stripes takes from the first one that has
18870        // anything, which is what makes the order of the keys matter.
18871        let popped = format!(
18872            "*2\r\n${}\r\n{second}\r\n*1\r\n*2\r\n$1\r\nb\r\n$1\r\n3\r\n",
18873            second.len()
18874        );
18875        assert_eq!(f.run(&[b"ZMPOP", b"3", b"gone", z2, z1, b"MIN"]), popped);
18876        f.run(&[b"ZADD", z2, b"3", b"b"]);
18877
18878        // An empty result deletes a destination wherever it is, and an input of
18879        // the wrong type stops the command before the destination is touched.
18880        assert_eq!(f.run(&[b"ZINTERSTORE", z3, b"2", z1, b"gone"]), ":0\r\n");
18881        assert_eq!(f.run(&[b"EXISTS", z3]), ":0\r\n");
18882        f.run(&[b"SET", z3, b"v"]);
18883        assert_eq!(
18884            f.run(&[b"ZUNION", b"2", z1, z3]),
18885            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18886        );
18887        assert_eq!(f.run(&[b"GET", z3]), "$1\r\nv\r\n", "and left it alone");
18888
18889        // And a destination that is also a source works across stripes for the
18890        // reason it works on one: the whole result is built before anything is
18891        // written.
18892        assert_eq!(f.run(&[b"ZUNIONSTORE", z2, b"2", z1, z2]), ":3\r\n");
18893        assert_eq!(f.run(&[b"ZSCORE", z2, b"b"]), "$1\r\n5\r\n");
18894        assert_eq!(f.run(&[b"ZCARD", z2]), ":3\r\n");
18895    }
18896
18897    /// A `ZRANGESTORE` whose two keys are on two stripes.
18898    #[test]
18899    fn a_range_store_across_stripes_copies_the_window() {
18900        let mut f = Fixture::striped(8);
18901        let other = apart(&mut f, "src");
18902        let third = apart(&mut f, &other);
18903        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
18904
18905        f.run(&[b"ZADD", src, b"1", b"a", b"2", b"b", b"3", b"c"]);
18906        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"0", b"1"]), ":2\r\n");
18907        assert_eq!(
18908            f.run(&[b"ZRANGE", dst, b"0", b"-1", b"WITHSCORES"]),
18909            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
18910        );
18911        assert_eq!(f.run(&[b"ZCARD", src]), ":3\r\n", "the source kept its own");
18912
18913        // A window walked backwards takes the other end of the sorted set and
18914        // still stores what it took in score order.
18915        assert_eq!(
18916            f.run(&[
18917                b"ZRANGESTORE",
18918                dst,
18919                src,
18920                b"+inf",
18921                b"-inf",
18922                b"BYSCORE",
18923                b"REV",
18924                b"LIMIT",
18925                b"0",
18926                b"2"
18927            ]),
18928            ":2\r\n"
18929        );
18930        assert_eq!(
18931            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
18932            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
18933        );
18934
18935        // An empty window deletes the destination on its own stripe, and a
18936        // source of the wrong type is refused before the destination is touched.
18937        assert_eq!(f.run(&[b"ZRANGESTORE", dst, src, b"5", b"1"]), ":0\r\n");
18938        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
18939        f.run(&[b"ZRANGESTORE", dst, src, b"0", b"-1"]);
18940        f.run(&[b"SET", plain, b"v"]);
18941        assert_eq!(
18942            f.run(&[b"ZRANGESTORE", dst, plain, b"0", b"-1"]),
18943            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
18944        );
18945        assert_eq!(
18946            f.run(&[b"ZCARD", dst]),
18947            ":3\r\n",
18948            "and left the destination"
18949        );
18950    }
18951
18952    /// Every list command, on one stripe and on eight.
18953    ///
18954    /// The blocking six are in here too, both when they can be answered on the
18955    /// spot and when they cannot, since a command that parks its client writes
18956    /// nothing at all and two servers have to agree about that as much as they
18957    /// agree about a reply.
18958    #[test]
18959    fn the_list_group_answers_the_same_however_many_stripes_there_are() {
18960        let script: &[&[&[u8]]] = &[
18961            &[b"RPUSH", b"l1", b"a", b"b", b"c"],
18962            &[b"LPUSH", b"l1", b"z"],
18963            &[b"RPUSHX", b"l1", b"d"],
18964            &[b"LPUSHX", b"gone", b"x"],
18965            &[b"RPUSHX", b"gone", b"x"],
18966            &[b"LLEN", b"l1"],
18967            &[b"LLEN", b"gone"],
18968            &[b"LRANGE", b"l1", b"0", b"-1"],
18969            &[b"LRANGE", b"l1", b"1", b"2"],
18970            &[b"LRANGE", b"l1", b"5", b"9"],
18971            &[b"LINDEX", b"l1", b"0"],
18972            &[b"LINDEX", b"l1", b"-1"],
18973            &[b"LINDEX", b"l1", b"99"],
18974            &[b"LSET", b"l1", b"0", b"y"],
18975            &[b"LINSERT", b"l1", b"BEFORE", b"b", b"aa"],
18976            &[b"LINSERT", b"l1", b"AFTER", b"nothere", b"x"],
18977            &[b"LPOS", b"l1", b"b"],
18978            &[b"LPOS", b"l1", b"b", b"COUNT", b"0"],
18979            &[b"LPOS", b"l1", b"nothere"],
18980            &[b"LPOS", b"l1", b"b", b"RANK", b"-1", b"MAXLEN", b"2"],
18981            &[b"LREM", b"l1", b"1", b"aa"],
18982            &[b"LTRIM", b"l1", b"0", b"3"],
18983            &[b"LRANGE", b"l1", b"0", b"-1"],
18984            &[b"LPOP", b"l1"],
18985            &[b"RPOP", b"l1"],
18986            &[b"LPOP", b"l1", b"2"],
18987            &[b"LPOP", b"gone"],
18988            &[b"LPOP", b"gone", b"2"],
18989            &[b"EXISTS", b"l1"],
18990            // The ones that name two keys, and the one that takes a block of
18991            // elements rather than the one on the end.
18992            &[b"RPUSH", b"src", b"a", b"b", b"c", b"d"],
18993            &[b"LMOVE", b"src", b"dst", b"LEFT", b"RIGHT"],
18994            &[b"RPOPLPUSH", b"src", b"dst"],
18995            &[b"LRANGE", b"dst", b"0", b"-1"],
18996            &[b"LMOVE", b"gone", b"dst", b"LEFT", b"RIGHT"],
18997            &[b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT"],
18998            &[
18999                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK",
19000            ],
19001            &[
19002                b"LMOVEM", b"dst", b"dst", b"LEFT", b"RIGHT", b"COUNT", b"2", b"OBO",
19003            ],
19004            &[b"LRANGE", b"dst", b"0", b"-1"],
19005            &[
19006                b"LMOVEM", b"src", b"dst", b"LEFT", b"RIGHT", b"EXACTLY", b"9", b"BULK",
19007            ],
19008            &[b"LMPOP", b"2", b"gone", b"dst", b"LEFT"],
19009            &[b"LMPOP", b"2", b"gone", b"dst", b"RIGHT", b"COUNT", b"2"],
19010            &[b"LMPOP", b"1", b"gone", b"LEFT"],
19011            // The blocking ones, first with something there to answer them and
19012            // then with nothing, which parks the client and writes nothing.
19013            &[b"RPUSH", b"q", b"a", b"b", b"c"],
19014            &[b"BLPOP", b"gone", b"q", b"0"],
19015            &[b"BRPOP", b"q", b"0"],
19016            &[b"BLMPOP", b"0", b"2", b"gone", b"q", b"LEFT"],
19017            &[b"RPUSH", b"q", b"x", b"y", b"z"],
19018            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19019            &[b"BRPOPLPUSH", b"q", b"dst", b"0"],
19020            &[b"BLMOVEM", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19021            &[b"BLPOP", b"q", b"0"],
19022            &[b"BLMOVE", b"q", b"dst", b"LEFT", b"RIGHT", b"0"],
19023            // The errors, which have to be the same errors.
19024            &[b"SET", b"plain", b"v"],
19025            &[b"LPUSH", b"plain", b"a"],
19026            &[b"LLEN", b"plain"],
19027            &[b"LMOVE", b"dst", b"plain", b"LEFT", b"RIGHT"],
19028            &[b"LRANGE", b"dst", b"0", b"-1"],
19029            &[b"LMOVEM", b"dst", b"plain", b"LEFT", b"RIGHT"],
19030            &[b"LSET", b"gone", b"0", b"v"],
19031            &[b"LSET", b"dst", b"99", b"v"],
19032            &[b"LPOP", b"dst", b"-1"],
19033            &[b"LMPOP", b"0", b"dst", b"LEFT"],
19034            &[b"LPOS", b"dst", b"a", b"RANK", b"0"],
19035        ];
19036
19037        let mut one = Fixture::new();
19038        let mut many = Fixture::striped(8);
19039        for parts in script {
19040            let a = one.run(parts);
19041            let b = many.run(parts);
19042            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19043        }
19044    }
19045
19046    /// An `LMOVE` and an `LMOVEM` whose two keys are on two stripes.
19047    #[test]
19048    fn a_list_move_across_stripes_takes_the_elements_with_it() {
19049        let mut f = Fixture::striped(8);
19050        let other = apart(&mut f, "src");
19051        let third = apart(&mut f, &other);
19052        let (src, dst, plain) = (b"src".as_slice(), other.as_bytes(), third.as_bytes());
19053
19054        f.run(&[b"RPUSH", src, b"a", b"b", b"c", b"d"]);
19055        assert_eq!(
19056            f.run(&[b"LMOVE", src, dst, b"LEFT", b"RIGHT"]),
19057            "$1\r\na\r\n"
19058        );
19059        assert_eq!(f.run(&[b"RPOPLPUSH", src, dst]), "$1\r\nd\r\n");
19060        assert_eq!(
19061            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19062            "*2\r\n$1\r\nd\r\n$1\r\na\r\n",
19063            "one went on each end of the destination"
19064        );
19065        assert_eq!(
19066            f.run(&[b"LRANGE", src, b"0", b"-1"]),
19067            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19068        );
19069
19070        // A block of them, which under BULK arrives in the order it left.
19071        assert_eq!(
19072            f.run(&[
19073                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
19074            ]),
19075            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
19076        );
19077        assert_eq!(
19078            f.run(&[b"LRANGE", dst, b"0", b"-1"]),
19079            "*4\r\n$1\r\nd\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
19080        );
19081        assert_eq!(
19082            f.run(&[b"EXISTS", src]),
19083            ":0\r\n",
19084            "and the source is gone with its last element"
19085        );
19086
19087        // An `EXACTLY` the source cannot fill moves nothing, and a source that
19088        // is not there at all is the two kinds of nothing the two commands have.
19089        f.run(&[b"RPUSH", src, b"e", b"f"]);
19090        assert_eq!(
19091            f.run(&[
19092                b"LMOVEM", src, dst, b"LEFT", b"RIGHT", b"EXACTLY", b"3", b"BULK"
19093            ]),
19094            "*-1\r\n"
19095        );
19096        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n", "and took none of them");
19097        assert_eq!(
19098            f.run(&[b"LMOVE", b"gone", dst, b"LEFT", b"RIGHT"]),
19099            "$-1\r\n"
19100        );
19101        assert_eq!(
19102            f.run(&[b"LMOVEM", b"gone", dst, b"LEFT", b"RIGHT"]),
19103            "*-1\r\n"
19104        );
19105
19106        // A destination of the wrong type is refused before anything is taken,
19107        // which is the order that matters most here, since an element already
19108        // out of the source would have nowhere to go back to.
19109        f.run(&[b"SET", plain, b"v"]);
19110        assert_eq!(
19111            f.run(&[b"LMOVE", src, plain, b"LEFT", b"RIGHT"]),
19112            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19113        );
19114        assert_eq!(
19115            f.run(&[b"LLEN", src]),
19116            ":2\r\n",
19117            "and left the source alone"
19118        );
19119        assert_eq!(
19120            f.run(&[b"LMOVEM", src, plain, b"LEFT", b"RIGHT"]),
19121            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19122        );
19123        assert_eq!(f.run(&[b"LLEN", src]), ":2\r\n");
19124    }
19125
19126    /// A parked client served by a push that landed on another stripe.
19127    ///
19128    /// A waiter remembers the database and not the stripe, which is the point:
19129    /// serving it runs the same attempt the command ran, and the attempt finds
19130    /// the stripe each of its keys is on for itself.
19131    #[test]
19132    fn a_parked_client_is_served_from_the_stripe_its_key_is_on() {
19133        let mut f = Fixture::striped(8);
19134        let other = apart(&mut f, "q");
19135        let (q, far) = (b"q".as_slice(), other.as_bytes());
19136
19137        assert_eq!(f.flow(&[b"BLPOP", q, far, b"0"]).0, Flow::Block);
19138        assert_eq!(f.server.waiters().len(), 1);
19139        f.run(&[b"RPUSH", far, b"v"]);
19140        let mut out = Out::new(Proto::Resp2);
19141        assert!(f.server.serve_waiter(0, 0, &mut out));
19142        let want = format!("*2\r\n${}\r\n{other}\r\n$1\r\nv\r\n", other.len());
19143        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19144        assert_eq!(
19145            f.run(&[b"EXISTS", far]),
19146            ":0\r\n",
19147            "and it took the element with it"
19148        );
19149
19150        // And a move across two stripes is served the same way, by the push
19151        // that fills its source.
19152        f.server.waiters_mut().forget(7);
19153        assert_eq!(
19154            f.flow(&[b"BLMOVE", q, far, b"LEFT", b"RIGHT", b"0"]).0,
19155            Flow::Block
19156        );
19157        f.run(&[b"RPUSH", q, b"w"]);
19158        let mut out = Out::new(Proto::Resp2);
19159        assert!(f.server.serve_waiter(0, 0, &mut out));
19160        assert_eq!(
19161            core::str::from_utf8(out.as_slice()).expect("ascii"),
19162            "$1\r\nw\r\n"
19163        );
19164        assert_eq!(f.run(&[b"LRANGE", far, b"0", b"-1"]), "*1\r\n$1\r\nw\r\n");
19165    }
19166
19167    /// Every stream command, on one stripe and on eight.
19168    ///
19169    /// Every ID is written out rather than left to the clock, so the two servers
19170    /// are being compared on what they store and not on how long the test took
19171    /// to get from one of them to the other.
19172    #[test]
19173    fn the_stream_group_answers_the_same_however_many_stripes_there_are() {
19174        let script: &[&[&[u8]]] = &[
19175            &[b"XADD", b"s", b"1-1", b"a", b"1"],
19176            &[b"XADD", b"s", b"2-1", b"b", b"2", b"c", b"3"],
19177            &[b"XADD", b"s", b"3-1", b"d", b"4"],
19178            &[b"XADD", b"s", b"1-1", b"e", b"5"],
19179            &[b"XADD", b"nomk", b"NOMKSTREAM", b"1-1", b"a", b"1"],
19180            &[b"XLEN", b"s"],
19181            &[b"XLEN", b"gone"],
19182            &[b"XRANGE", b"s", b"-", b"+"],
19183            &[b"XRANGE", b"s", b"2", b"+", b"COUNT", b"1"],
19184            &[b"XRANGE", b"gone", b"-", b"+", b"COUNT", b"0"],
19185            &[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"],
19186            &[b"XREVRANGE", b"s", b"+", b"-"],
19187            &[b"XREAD", b"COUNT", b"2", b"STREAMS", b"s", b"0"],
19188            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0", b"0"],
19189            &[b"XREAD", b"STREAMS", b"s", b"$"],
19190            // The groups, which is where most of the state is.
19191            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19192            &[b"XGROUP", b"CREATE", b"s", b"g", b"0"],
19193            &[b"XGROUP", b"CREATE", b"gone", b"g", b"0"],
19194            &[b"XGROUP", b"CREATE", b"made", b"g", b"$", b"MKSTREAM"],
19195            &[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"idle"],
19196            &[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"],
19197            &[
19198                b"XREADGROUP",
19199                b"GROUP",
19200                b"g",
19201                b"c1",
19202                b"COUNT",
19203                b"1",
19204                b"STREAMS",
19205                b"s",
19206                b"0",
19207            ],
19208            &[
19209                b"XREADGROUP",
19210                b"GROUP",
19211                b"nope",
19212                b"c1",
19213                b"STREAMS",
19214                b"s",
19215                b">",
19216            ],
19217            &[b"XPENDING", b"s", b"g"],
19218            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10"],
19219            &[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"],
19220            &[b"XPENDING", b"s", b"nope"],
19221            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1"],
19222            &[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1", b"JUSTID"],
19223            &[b"XAUTOCLAIM", b"s", b"g", b"c3", b"0", b"0"],
19224            &[b"XACK", b"s", b"g", b"1-1"],
19225            &[b"XACK", b"s", b"g", b"1-1"],
19226            &[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"],
19227            &[b"XPENDING", b"s", b"g"],
19228            &[b"XINFO", b"STREAM", b"s"],
19229            &[b"XINFO", b"GROUPS", b"s"],
19230            &[b"XINFO", b"CONSUMERS", b"s", b"g"],
19231            &[b"XINFO", b"STREAM", b"gone"],
19232            // Deleting, trimming and moving the ID on.
19233            &[b"XDEL", b"s", b"3-1"],
19234            &[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"],
19235            &[b"XACKDEL", b"s", b"g", b"KEEPREF", b"IDS", b"1", b"1-1"],
19236            &[b"XADD", b"s", b"9-1", b"z", b"9"],
19237            &[b"XTRIM", b"s", b"MAXLEN", b"1"],
19238            &[b"XTRIM", b"s", b"MINID", b"9"],
19239            &[b"XSETID", b"s", b"99-1"],
19240            &[b"XSETID", b"s", b"1-1"],
19241            &[b"XLEN", b"s"],
19242            &[b"XGROUP", b"SETID", b"s", b"g", b"0"],
19243            &[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c1"],
19244            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19245            &[b"XGROUP", b"DESTROY", b"s", b"g"],
19246            // And the errors.
19247            &[b"SET", b"plain", b"v"],
19248            &[b"XADD", b"plain", b"1-1", b"a", b"1"],
19249            &[b"XLEN", b"plain"],
19250            &[b"XREAD", b"STREAMS", b"plain", b"0"],
19251            &[b"XRANGE", b"s", b"bogus", b"+"],
19252            &[b"XADD", b"s", b"1-1", b"a"],
19253            &[b"XREAD", b"STREAMS", b"s", b"gone", b"0"],
19254            &[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"],
19255        ];
19256
19257        let mut one = Fixture::new();
19258        let mut many = Fixture::striped(8);
19259        for parts in script {
19260            let a = one.run(parts);
19261            let b = many.run(parts);
19262            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19263        }
19264    }
19265
19266    /// An `XREAD` and an `XREADGROUP` naming two keys on two stripes.
19267    ///
19268    /// Nothing is shared between the two streams, so the only thing this can go
19269    /// wrong at is looking both of them up, which is exactly what a read that
19270    /// held one database and walked it would get wrong.
19271    #[test]
19272    fn a_stream_read_across_stripes_reads_every_key() {
19273        let mut f = Fixture::striped(8);
19274        let other = apart(&mut f, "s1");
19275        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
19276
19277        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19278        f.run(&[b"XADD", s2, b"2-1", b"b", b"2"]);
19279        let got = f.run(&[b"XREAD", b"STREAMS", s1, s2, b"0", b"0"]);
19280        assert!(got.starts_with("*2\r\n"), "both streams answered: {got}");
19281        assert!(got.contains("1-1"), "the first one is in there: {got}");
19282        assert!(got.contains("2-1"), "and so is the second: {got}");
19283
19284        // A group read looks its group up on every key before it reads any of
19285        // them, so a group that is missing on the far key stops the near one.
19286        f.run(&[b"XGROUP", b"CREATE", s1, b"g", b"0"]);
19287        let got = f.run(&[
19288            b"XREADGROUP",
19289            b"GROUP",
19290            b"g",
19291            b"c",
19292            b"STREAMS",
19293            s1,
19294            s2,
19295            b">",
19296            b">",
19297        ]);
19298        assert!(got.starts_with("-NOGROUP"), "{got}");
19299        assert_eq!(
19300            f.run(&[b"XPENDING", s1, b"g"]),
19301            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n",
19302            "and read nothing from the key that did have the group"
19303        );
19304
19305        f.run(&[b"XGROUP", b"CREATE", s2, b"g", b"0"]);
19306        let got = f.run(&[
19307            b"XREADGROUP",
19308            b"GROUP",
19309            b"g",
19310            b"c",
19311            b"STREAMS",
19312            s1,
19313            s2,
19314            b">",
19315            b">",
19316        ]);
19317        assert!(got.starts_with("*2\r\n"), "now both are read: {got}");
19318    }
19319
19320    /// A client parked on an `XREAD` woken by an entry on another stripe.
19321    #[test]
19322    fn a_parked_stream_reader_is_served_from_the_stripe_its_key_is_on() {
19323        let mut f = Fixture::striped(8);
19324        let other = apart(&mut f, "s1");
19325        let (s1, far) = (b"s1".as_slice(), other.as_bytes());
19326        f.run(&[b"XADD", s1, b"1-1", b"a", b"1"]);
19327        f.run(&[b"XADD", far, b"1-1", b"a", b"1"]);
19328
19329        assert_eq!(
19330            f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", s1, far, b"$", b"$"])
19331                .0,
19332            Flow::Block
19333        );
19334        f.run(&[b"XADD", far, b"2-1", b"b", b"2"]);
19335        let mut out = Out::new(Proto::Resp2);
19336        assert!(f.server.serve_waiter(0, 0, &mut out));
19337        let want = format!(
19338            "*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",
19339            other.len()
19340        );
19341        assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
19342    }
19343
19344    /// Every JSON command, on one stripe and on eight.
19345    #[test]
19346    fn the_json_group_answers_the_same_however_many_stripes_there_are() {
19347        let script: &[&[&[u8]]] = &[
19348            &[
19349                b"JSON.SET",
19350                b"d",
19351                b"$",
19352                br#"{"a":1,"b":[1,2,3],"s":"hi","t":true}"#,
19353            ],
19354            &[b"JSON.SET", b"d", b"$.a", b"2"],
19355            &[b"JSON.SET", b"d", b"$.new", b"9", b"NX"],
19356            &[b"JSON.SET", b"d", b"$.new", b"8", b"NX"],
19357            &[b"JSON.SET", b"d", b"$.nope", b"7", b"XX"],
19358            &[b"JSON.GET", b"d"],
19359            &[b"JSON.GET", b"d", b"$.b"],
19360            &[b"JSON.GET", b"gone", b"$"],
19361            &[b"JSON.TYPE", b"d", b"$.b"],
19362            &[b"JSON.TYPE", b"d", b"$.s"],
19363            &[b"JSON.TOGGLE", b"d", b"$.t"],
19364            &[b"JSON.ARRLEN", b"d", b"$.b"],
19365            &[b"JSON.OBJLEN", b"d", b"$"],
19366            &[b"JSON.OBJKEYS", b"d", b"$"],
19367            &[b"JSON.STRLEN", b"d", b"$.s"],
19368            &[b"JSON.STRAPPEND", b"d", b"$.s", br#""there""#],
19369            &[b"JSON.ARRAPPEND", b"d", b"$.b", b"4"],
19370            &[b"JSON.ARRINSERT", b"d", b"$.b", b"0", b"0"],
19371            &[b"JSON.ARRINDEX", b"d", b"$.b", b"3"],
19372            &[b"JSON.ARRTRIM", b"d", b"$.b", b"1", b"3"],
19373            &[b"JSON.ARRPOP", b"d", b"$.b"],
19374            &[b"JSON.NUMINCRBY", b"d", b"$.a", b"5"],
19375            &[b"JSON.NUMMULTBY", b"d", b"$.a", b"2"],
19376            &[b"JSON.NUMPOWBY", b"d", b"$.a", b"2"],
19377            &[b"JSON.MERGE", b"d", b"$", br#"{"a":null,"m":1}"#],
19378            &[b"JSON.RESP", b"d", b"$.b"],
19379            &[b"JSON.DEBUG", b"MEMORY", b"d"],
19380            &[b"JSON.CLEAR", b"d", b"$.b"],
19381            &[b"JSON.DEL", b"d", b"$.m"],
19382            &[b"JSON.FORGET", b"d", b"$.nothere"],
19383            // The two that name more than one key.
19384            &[
19385                b"JSON.MSET",
19386                b"m1",
19387                b"$",
19388                b"1",
19389                b"m2",
19390                b"$",
19391                b"2",
19392                b"m3",
19393                b"$",
19394                b"3",
19395            ],
19396            &[b"JSON.MGET", b"m1", b"m2", b"m3", b"gone", b"$"],
19397            &[b"JSON.MSET", b"m1", b"$", b"9", b"m2", b"$.deep", b"9"],
19398            &[b"JSON.GET", b"m1", b"$"],
19399            &[b"JSON.MSET", b"m1", b"$", b"nonsense", b"m2", b"$", b"5"],
19400            &[b"JSON.GET", b"m2", b"$"],
19401            // And the errors.
19402            &[b"SET", b"plain", b"v"],
19403            &[b"JSON.GET", b"plain", b"$"],
19404            &[b"JSON.SET", b"plain", b"$", b"1"],
19405            &[b"JSON.MGET", b"m1", b"plain", b"$"],
19406            &[b"JSON.SET", b"d", b"$.b", b"["],
19407            &[b"JSON.DEL", b"plain"],
19408        ];
19409
19410        let mut one = Fixture::new();
19411        let mut many = Fixture::striped(8);
19412        for parts in script {
19413            let a = one.run(parts);
19414            let b = many.run(parts);
19415            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19416        }
19417    }
19418
19419    /// A `JSON.MSET` and a `JSON.MGET` whose keys are on several stripes.
19420    ///
19421    /// `JSON.MSET` works every triple out against the keyspace as it was before
19422    /// the command and writes nothing until all of them are known to work, so
19423    /// the thing to check is that a triple that cannot be written stops the
19424    /// ones on other stripes as well as the ones on its own.
19425    #[test]
19426    fn a_json_multi_write_across_stripes_reaches_every_key() {
19427        let mut f = Fixture::striped(8);
19428        let second = apart(&mut f, "m1");
19429        let third = apart(&mut f, &second);
19430        let (m1, m2, m3) = (b"m1".as_slice(), second.as_bytes(), third.as_bytes());
19431
19432        assert_eq!(
19433            f.run(&[b"JSON.MSET", m1, b"$", b"1", m2, b"$", b"2", m3, b"$", b"3"]),
19434            "+OK\r\n"
19435        );
19436        assert_eq!(
19437            f.run(&[b"JSON.MGET", m1, m2, m3, b"gone", b"$"]),
19438            "*4\r\n$3\r\n[1]\r\n$3\r\n[2]\r\n$3\r\n[3]\r\n$-1\r\n"
19439        );
19440
19441        // A value that is not JSON is refused before anything is written, and
19442        // the key on the far stripe keeps what it had.
19443        assert_eq!(
19444            f.run(&[b"JSON.MSET", m1, b"$", b"9", m2, b"$", b"nonsense"]),
19445            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
19446        );
19447        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[1]\r\n");
19448
19449        // A path that names nowhere is not an error. That triple is skipped,
19450        // the ones on the other stripes are still written, and the reply is a
19451        // nil rather than OK.
19452        assert_eq!(
19453            f.run(&[
19454                b"JSON.MSET",
19455                m1,
19456                b"$",
19457                b"9",
19458                m2,
19459                b"$.deep",
19460                b"9",
19461                m3,
19462                b"$",
19463                b"7"
19464            ]),
19465            "$-1\r\n"
19466        );
19467        assert_eq!(f.run(&[b"JSON.GET", m1, b"$"]), "$3\r\n[9]\r\n");
19468        assert_eq!(f.run(&[b"JSON.GET", m2, b"$"]), "$3\r\n[2]\r\n");
19469        assert_eq!(f.run(&[b"JSON.GET", m3, b"$"]), "$3\r\n[7]\r\n");
19470    }
19471
19472    /// Every geospatial command, on one stripe and on eight.
19473    #[test]
19474    fn the_geo_group_answers_the_same_however_many_stripes_there_are() {
19475        let script: &[&[&[u8]]] = &[
19476            &[
19477                b"GEOADD",
19478                b"g",
19479                b"13.361389",
19480                b"38.115556",
19481                b"palermo",
19482                b"15.087269",
19483                b"37.502669",
19484                b"catania",
19485            ],
19486            &[
19487                b"GEOADD",
19488                b"g",
19489                b"NX",
19490                b"13.361389",
19491                b"38.115556",
19492                b"palermo",
19493            ],
19494            &[b"GEOADD", b"g", b"XX", b"CH", b"13.4", b"38.1", b"palermo"],
19495            &[b"GEOPOS", b"g", b"palermo", b"nothere"],
19496            &[b"GEOHASH", b"g", b"palermo", b"catania"],
19497            &[b"GEODIST", b"g", b"palermo", b"catania"],
19498            &[b"GEODIST", b"g", b"palermo", b"catania", b"KM"],
19499            &[b"GEODIST", b"g", b"palermo", b"nothere"],
19500            &[
19501                b"GEOSEARCH",
19502                b"g",
19503                b"FROMLONLAT",
19504                b"15",
19505                b"37",
19506                b"BYRADIUS",
19507                b"200",
19508                b"KM",
19509                b"ASC",
19510                b"WITHCOORD",
19511                b"WITHDIST",
19512                b"WITHHASH",
19513            ],
19514            &[
19515                b"GEOSEARCH",
19516                b"g",
19517                b"FROMMEMBER",
19518                b"palermo",
19519                b"BYBOX",
19520                b"400",
19521                b"400",
19522                b"KM",
19523                b"DESC",
19524            ],
19525            &[
19526                b"GEORADIUS",
19527                b"g",
19528                b"15",
19529                b"37",
19530                b"200",
19531                b"KM",
19532                b"COUNT",
19533                b"1",
19534            ],
19535            &[b"GEORADIUSBYMEMBER", b"g", b"palermo", b"200", b"KM"],
19536            &[b"GEORADIUSBYMEMBER_RO", b"g", b"nothere", b"200", b"KM"],
19537            &[
19538                b"GEOSEARCHSTORE",
19539                b"dst",
19540                b"g",
19541                b"FROMLONLAT",
19542                b"15",
19543                b"37",
19544                b"BYRADIUS",
19545                b"200",
19546                b"KM",
19547            ],
19548            &[b"ZRANGE", b"dst", b"0", b"-1"],
19549            &[
19550                b"GEOSEARCHSTORE",
19551                b"dst",
19552                b"g",
19553                b"FROMLONLAT",
19554                b"15",
19555                b"37",
19556                b"BYRADIUS",
19557                b"1",
19558                b"M",
19559                b"STOREDIST",
19560            ],
19561            &[b"EXISTS", b"dst"],
19562            &[
19563                b"GEORADIUS",
19564                b"g",
19565                b"15",
19566                b"37",
19567                b"200",
19568                b"KM",
19569                b"STORE",
19570                b"dst",
19571            ],
19572            &[b"ZCARD", b"dst"],
19573            // And the errors.
19574            &[b"GEOADD", b"g", b"181", b"38", b"nowhere"],
19575            &[b"SET", b"plain", b"v"],
19576            &[b"GEOPOS", b"plain", b"a"],
19577            &[b"GEOSEARCH", b"g", b"FROMLONLAT", b"15", b"37"],
19578            &[
19579                b"GEOSEARCHSTORE",
19580                b"dst",
19581                b"g",
19582                b"FROMLONLAT",
19583                b"15",
19584                b"37",
19585                b"BYRADIUS",
19586                b"200",
19587                b"KM",
19588                b"WITHCOORD",
19589            ],
19590        ];
19591
19592        let mut one = Fixture::new();
19593        let mut many = Fixture::striped(8);
19594        for parts in script {
19595            let a = one.run(parts);
19596            let b = many.run(parts);
19597            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19598        }
19599    }
19600
19601    /// A `GEOSEARCHSTORE` whose two keys are on two stripes.
19602    #[test]
19603    fn a_geo_search_store_across_stripes_writes_what_it_found() {
19604        let mut f = Fixture::striped(8);
19605        let other = apart(&mut f, "g");
19606        let third = apart(&mut f, &other);
19607        let (g, dst, plain) = (b"g".as_slice(), other.as_bytes(), third.as_bytes());
19608
19609        f.run(&[
19610            b"GEOADD",
19611            g,
19612            b"13.361389",
19613            b"38.115556",
19614            b"palermo",
19615            b"15.087269",
19616            b"37.502669",
19617            b"catania",
19618        ]);
19619        assert_eq!(
19620            f.run(&[
19621                b"GEOSEARCHSTORE",
19622                dst,
19623                g,
19624                b"FROMLONLAT",
19625                b"15",
19626                b"37",
19627                b"BYRADIUS",
19628                b"200",
19629                b"KM",
19630                b"ASC",
19631            ]),
19632            ":2\r\n"
19633        );
19634        assert_eq!(
19635            f.run(&[b"ZRANGE", dst, b"0", b"-1"]),
19636            "*2\r\n$7\r\npalermo\r\n$7\r\ncatania\r\n",
19637            "the geohash is the score, so the order is not the search order"
19638        );
19639        assert_eq!(f.run(&[b"ZCARD", g]), ":2\r\n", "the source is untouched");
19640
19641        // `STOREDIST` stores the distance in the unit the search was asked in,
19642        // which is the destination stripe's sorted set and not the source's.
19643        assert_eq!(
19644            f.run(&[
19645                b"GEOSEARCHSTORE",
19646                dst,
19647                g,
19648                b"FROMMEMBER",
19649                b"palermo",
19650                b"BYRADIUS",
19651                b"200",
19652                b"KM",
19653                b"STOREDIST",
19654            ]),
19655            ":2\r\n"
19656        );
19657        assert_eq!(
19658            f.run(&[b"ZSCORE", dst, b"palermo"]),
19659            "$1\r\n0\r\n",
19660            "the centre is nought away from itself"
19661        );
19662
19663        // A search that found nothing deletes the destination on its own
19664        // stripe, and a source of the wrong type is refused with the
19665        // destination left alone.
19666        assert_eq!(
19667            f.run(&[
19668                b"GEOSEARCHSTORE",
19669                dst,
19670                g,
19671                b"FROMLONLAT",
19672                b"0",
19673                b"0",
19674                b"BYRADIUS",
19675                b"1",
19676                b"M",
19677            ]),
19678            ":0\r\n"
19679        );
19680        assert_eq!(f.run(&[b"EXISTS", dst]), ":0\r\n");
19681        f.run(&[
19682            b"GEOSEARCHSTORE",
19683            dst,
19684            g,
19685            b"FROMLONLAT",
19686            b"15",
19687            b"37",
19688            b"BYRADIUS",
19689            b"200",
19690            b"KM",
19691        ]);
19692        f.run(&[b"SET", plain, b"v"]);
19693        assert_eq!(
19694            f.run(&[
19695                b"GEOSEARCHSTORE",
19696                dst,
19697                plain,
19698                b"FROMLONLAT",
19699                b"15",
19700                b"37",
19701                b"BYRADIUS",
19702                b"200",
19703                b"KM",
19704            ]),
19705            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
19706        );
19707        assert_eq!(
19708            f.run(&[b"ZCARD", dst]),
19709            ":2\r\n",
19710            "and left the destination"
19711        );
19712    }
19713
19714    /// Every time series command, on one stripe and on eight.
19715    ///
19716    /// Every timestamp is written out rather than left to the clock, so the two
19717    /// servers are compared on the samples they hold and not on how long the
19718    /// test took to get from one of them to the other.
19719    #[test]
19720    fn the_time_series_group_answers_the_same_however_many_stripes_there_are() {
19721        let script: &[&[&[u8]]] = &[
19722            &[
19723                b"TS.CREATE",
19724                b"ts:a",
19725                b"LABELS",
19726                b"sensor",
19727                b"a",
19728                b"room",
19729                b"1",
19730            ],
19731            &[b"TS.CREATE", b"ts:a"],
19732            &[b"TS.ALTER", b"ts:a", b"RETENTION", b"0"],
19733            &[b"TS.ADD", b"ts:a", b"1000", b"1.5"],
19734            &[
19735                b"TS.ADD", b"ts:b", b"1000", b"2", b"LABELS", b"sensor", b"b", b"room", b"1",
19736            ],
19737            &[
19738                b"TS.MADD", b"ts:a", b"2000", b"2.5", b"ts:b", b"2000", b"3", b"gone", b"1", b"1",
19739            ],
19740            &[b"TS.INCRBY", b"ts:a", b"1", b"TIMESTAMP", b"3000"],
19741            &[b"TS.DECRBY", b"ts:a", b"0.5", b"TIMESTAMP", b"4000"],
19742            &[b"TS.GET", b"ts:a"],
19743            &[b"TS.GET", b"gone"],
19744            &[b"TS.RANGE", b"ts:a", b"-", b"+"],
19745            &[b"TS.RANGE", b"ts:a", b"1000", b"3000", b"COUNT", b"2"],
19746            &[
19747                b"TS.RANGE",
19748                b"ts:a",
19749                b"-",
19750                b"+",
19751                b"AGGREGATION",
19752                b"avg",
19753                b"2000",
19754            ],
19755            &[b"TS.REVRANGE", b"ts:a", b"-", b"+"],
19756            &[b"TS.NRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19757            &[b"TS.NREVRANGE", b"2", b"ts:a", b"ts:b", b"-", b"+"],
19758            &[b"TS.NRANGE", b"2", b"ts:a", b"gone", b"-", b"+"],
19759            &[b"TS.READ", b"ts:a", b"0"],
19760            &[b"TS.READ", b"ts:a", b"+"],
19761            // The filters, which are the ones that have to walk every stripe.
19762            &[b"TS.QUERYINDEX", b"sensor=a"],
19763            &[b"TS.QUERYINDEX", b"room=1"],
19764            &[b"TS.QUERYINDEX", b"room=9"],
19765            &[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"],
19766            &[
19767                b"TS.QUERYLABELS",
19768                b"VALUES",
19769                b"sensor",
19770                b"FILTER",
19771                b"room=1",
19772            ],
19773            &[b"TS.MGET", b"WITHLABELS", b"FILTER", b"room=1"],
19774            &[
19775                b"TS.MGET",
19776                b"SELECTED_LABELS",
19777                b"sensor",
19778                b"FILTER",
19779                b"sensor=a",
19780            ],
19781            &[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"],
19782            &[
19783                b"TS.MREVRANGE",
19784                b"-",
19785                b"+",
19786                b"WITHLABELS",
19787                b"FILTER",
19788                b"sensor=a",
19789            ],
19790            &[
19791                b"TS.MRANGE",
19792                b"-",
19793                b"+",
19794                b"FILTER",
19795                b"room=1",
19796                b"GROUPBY",
19797                b"room",
19798                b"REDUCE",
19799                b"max",
19800            ],
19801            &[b"TS.INFO", b"ts:a"],
19802            // And a rule, which is the one thing here that names two keys.
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.CREATE", b"ts:down"],
19812            &[
19813                b"TS.CREATERULE",
19814                b"ts:a",
19815                b"ts:down",
19816                b"AGGREGATION",
19817                b"avg",
19818                b"1000",
19819            ],
19820            &[b"TS.ADD", b"ts:a", b"5000", b"4"],
19821            &[b"TS.ADD", b"ts:a", b"6000", b"5"],
19822            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19823            &[b"TS.GET", b"ts:down", b"LATEST"],
19824            &[b"TS.INFO", b"ts:down"],
19825            &[b"TS.DEL", b"ts:a", b"5000", b"6000"],
19826            &[b"TS.RANGE", b"ts:down", b"-", b"+"],
19827            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19828            &[b"TS.DELETERULE", b"ts:a", b"ts:down"],
19829            &[b"TS.DEL", b"ts:a", b"0", b"1000"],
19830            // And the errors.
19831            &[b"SET", b"plain", b"v"],
19832            &[b"TS.ADD", b"plain", b"1", b"1"],
19833            &[b"TS.GET", b"plain"],
19834            &[b"TS.READ", b"plain", b"0"],
19835            &[b"TS.ALTER", b"gone", b"RETENTION", b"0"],
19836            &[b"TS.RANGE", b"gone", b"-", b"+"],
19837            &[b"TS.INFO", b"gone"],
19838        ];
19839
19840        let mut one = Fixture::new();
19841        let mut many = Fixture::striped(8);
19842        for parts in script {
19843            let a = one.run(parts);
19844            let b = many.run(parts);
19845            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19846        }
19847    }
19848
19849    /// A compaction rule whose two ends are on two stripes.
19850    ///
19851    /// This is the one thing in the family that walks from a key to another key,
19852    /// and it walks it in both directions: a sample on the source closes a
19853    /// bucket on the destination, a `LATEST` read on the destination folds the
19854    /// bucket the source is still filling, and a delete on the source rewrites
19855    /// what the destination already held. The same script is run against a
19856    /// server one stripe wide, where the two keys share a store, and against one
19857    /// eight stripes wide, where they do not.
19858    #[test]
19859    fn a_compaction_rule_across_stripes_reaches_both_ends() {
19860        let mut many = Fixture::striped(8);
19861        let other = apart(&mut many, "src");
19862        let (src, dst) = (b"src".as_slice(), other.as_bytes());
19863        let mut one = Fixture::new();
19864        let mut both = |parts: &[&[u8]]| {
19865            let a = one.run(parts);
19866            let b = many.run(parts);
19867            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19868            a
19869        };
19870
19871        both(&[b"TS.CREATE", src]);
19872        both(&[b"TS.CREATE", dst]);
19873        assert_eq!(
19874            both(&[b"TS.CREATERULE", src, dst, b"AGGREGATION", b"avg", b"1000"]),
19875            "+OK\r\n"
19876        );
19877        both(&[b"TS.ADD", src, b"1000", b"1"]);
19878        both(&[b"TS.ADD", src, b"1500", b"3"]);
19879        // The bucket the source is filling is not written down yet, and asking
19880        // for it works it out off the source.
19881        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19882        let open = both(&[b"TS.GET", dst, b"LATEST"]);
19883        assert!(open.contains(":1000"), "the open bucket is folded: {open}");
19884
19885        // A sample past the bucket closes it, which is the write that has to
19886        // land on the other stripe.
19887        both(&[b"TS.ADD", src, b"2000", b"5"]);
19888        let got = both(&[b"TS.RANGE", dst, b"-", b"+"]);
19889        assert!(got.starts_with("*1\r\n"), "the bucket was written: {got}");
19890        assert!(got.contains(":1000"), "{got}");
19891
19892        // And a delete on the source takes it away again.
19893        both(&[b"TS.DEL", src, b"1000", b"1999"]);
19894        assert_eq!(both(&[b"TS.RANGE", dst, b"-", b"+"]), "*0\r\n");
19895
19896        // Both ends still know about each other, and the link comes apart from
19897        // the source.
19898        assert!(
19899            both(&[b"TS.INFO", dst]).contains("src"),
19900            "the source is named"
19901        );
19902        assert_eq!(both(&[b"TS.DELETERULE", src, dst]), "+OK\r\n");
19903        assert_eq!(
19904            both(&[b"TS.DELETERULE", src, dst]),
19905            "-ERR TSDB: compaction rule does not exist\r\n"
19906        );
19907    }
19908
19909    /// A label filter takes the series it names wherever they landed.
19910    #[test]
19911    fn a_label_query_across_stripes_finds_every_series() {
19912        let names: [&[u8]; 6] = [b"q:1", b"q:2", b"q:3", b"q:4", b"q:5", b"q:6"];
19913        let mut many = Fixture::striped(8);
19914        let mut homes: Vec<usize> = names
19915            .iter()
19916            .map(|name| many.server.striped(0).stripe_of(name))
19917            .collect();
19918        homes.sort_unstable();
19919        homes.dedup();
19920        assert!(homes.len() > 1, "the six keys are not all on one stripe");
19921
19922        let mut one = Fixture::new();
19923        let mut both = |parts: &[&[u8]]| {
19924            let a = one.run(parts);
19925            let b = many.run(parts);
19926            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
19927            a
19928        };
19929        for name in &names {
19930            both(&[b"TS.CREATE", name, b"LABELS", b"room", b"1"]);
19931            both(&[b"TS.ADD", name, b"1000", b"1"]);
19932        }
19933
19934        let got = both(&[b"TS.QUERYINDEX", b"room=1"]);
19935        assert!(got.starts_with("*6\r\n"), "every series answered: {got}");
19936        assert!(both(&[b"TS.MGET", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19937        assert!(both(&[b"TS.MRANGE", b"-", b"+", b"FILTER", b"room=1"]).starts_with("*6\r\n"));
19938        assert_eq!(
19939            both(&[b"TS.QUERYLABELS", b"LABELS", b"FILTER", b"room=1"]),
19940            "*1\r\n$4\r\nroom\r\n"
19941        );
19942    }
19943
19944    /// Every hash command, and the field import beside it, on one stripe and on
19945    /// eight.
19946    ///
19947    /// `HRANDFIELD` with a count draws from the stripe's own generator and two
19948    /// stripes do not draw the same numbers, so the only draw here is off a hash
19949    /// holding one field, where every generator gives the same answer.
19950    #[test]
19951    fn the_hash_group_answers_the_same_however_many_stripes_there_are() {
19952        let script: &[&[&[u8]]] = &[
19953            &[b"HSET", b"h", b"a", b"1", b"b", b"2"],
19954            &[b"HMSET", b"h", b"c", b"3"],
19955            &[b"HSETNX", b"h", b"a", b"9"],
19956            &[b"HSETNX", b"h", b"d", b"4"],
19957            &[b"HGET", b"h", b"a"],
19958            &[b"HGET", b"h", b"nope"],
19959            &[b"HMGET", b"h", b"a", b"nope"],
19960            &[b"HLEN", b"h"],
19961            &[b"HEXISTS", b"h", b"a"],
19962            &[b"HSTRLEN", b"h", b"a"],
19963            &[b"HGETALL", b"h"],
19964            &[b"HKEYS", b"h"],
19965            &[b"HVALS", b"h"],
19966            &[b"HINCRBY", b"h", b"a", b"5"],
19967            &[b"HINCRBYFLOAT", b"h", b"a", b"1.5"],
19968            &[b"HSCAN", b"h", b"0"],
19969            &[b"HSCAN", b"h", b"0", b"MATCH", b"a", b"COUNT", b"10"],
19970            &[b"HSCAN", b"h", b"0", b"NOVALUES"],
19971            &[b"HDEL", b"h", b"d"],
19972            &[b"HSET", b"one", b"f", b"v"],
19973            &[b"HRANDFIELD", b"one"],
19974            &[b"HRANDFIELD", b"one", b"1", b"WITHVALUES"],
19975            // The field deadlines.
19976            &[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"],
19977            &[b"HTTL", b"h", b"FIELDS", b"1", b"a"],
19978            &[b"HPTTL", b"h", b"FIELDS", b"1", b"a"],
19979            &[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19980            &[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"],
19981            &[b"HPERSIST", b"h", b"FIELDS", b"1", b"a"],
19982            &[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"],
19983            &[b"HGET", b"h", b"b"],
19984            // The three that came later and word everything their own way.
19985            &[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"e", b"5"],
19986            &[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"e"],
19987            &[b"HGETDEL", b"h", b"FIELDS", b"1", b"e"],
19988            &[b"HGET", b"h", b"e"],
19989            // And the import, whose key is the third word.
19990            &[b"HIMPORT", b"PREPARE", b"fs", b"x", b"y"],
19991            &[b"HIMPORT", b"SET", b"imp", b"fs", b"1", b"2"],
19992            &[b"HGETALL", b"imp"],
19993            &[b"HIMPORT", b"SET", b"imp", b"nofs", b"1", b"2"],
19994            &[b"HIMPORT", b"DISCARD", b"fs"],
19995            // And the errors.
19996            &[b"SET", b"plain", b"v"],
19997            &[b"HSET", b"plain", b"a", b"1"],
19998            &[b"HGETALL", b"plain"],
19999            &[b"HGET", b"gone", b"a"],
20000            &[b"HINCRBY", b"h", b"a", b"nan"],
20001        ];
20002
20003        let mut one = Fixture::new();
20004        let mut many = Fixture::striped(8);
20005        // The field deadlines are absolute milliseconds worked out from the
20006        // clock, so both servers are put on the same one rather than left to
20007        // read the wall a moment apart.
20008        one.server.set_clock_ms(1_700_000_000_000);
20009        many.server.set_clock_ms(1_700_000_000_000);
20010        for parts in script {
20011            let a = one.run(parts);
20012            let b = many.run(parts);
20013            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20014        }
20015    }
20016
20017    /// Every array command, on one stripe and on eight.
20018    #[test]
20019    fn the_array_group_answers_the_same_however_many_stripes_there_are() {
20020        let script: &[&[&[u8]]] = &[
20021            &[b"ARSET", b"a", b"0", b"x", b"y", b"z"],
20022            &[b"ARMSET", b"a", b"5", b"p", b"7", b"q"],
20023            &[b"ARGET", b"a", b"1"],
20024            &[b"ARGET", b"a", b"99"],
20025            &[b"ARMGET", b"a", b"0", b"5", b"99"],
20026            &[b"ARGETRANGE", b"a", b"0", b"7"],
20027            &[b"ARLEN", b"a"],
20028            &[b"ARCOUNT", b"a"],
20029            &[b"ARINSERT", b"a", b"m", b"n"],
20030            &[b"ARSCAN", b"a", b"0", b"20"],
20031            &[b"ARSCAN", b"a", b"0", b"20", b"LIMIT", b"2"],
20032            &[b"ARGREP", b"a", b"0", b"20", b"EXACT", b"x"],
20033            &[b"ARGREP", b"a", b"0", b"20", b"GLOB", b"*", b"WITHVALUES"],
20034            &[b"ARLASTITEMS", b"a", b"2"],
20035            &[b"ARLASTITEMS", b"a", b"2", b"REV"],
20036            &[b"ARNEXT", b"a"],
20037            &[b"ARSEEK", b"a", b"3"],
20038            &[b"AROP", b"a", b"0", b"20", b"USED"],
20039            &[b"AROP", b"a", b"0", b"20", b"MATCH", b"x"],
20040            &[b"ARINFO", b"a"],
20041            &[b"ARINFO", b"a", b"FULL"],
20042            &[b"ARDEL", b"a", b"0"],
20043            &[b"ARDELRANGE", b"a", b"1", b"2"],
20044            &[b"ARCOUNT", b"a"],
20045            &[b"ARRING", b"r", b"3", b"1", b"2", b"3", b"4"],
20046            &[b"ARGETRANGE", b"r", b"0", b"9"],
20047            // And the errors.
20048            &[b"SET", b"plain", b"v"],
20049            &[b"ARGET", b"plain", b"0"],
20050            &[b"ARSET", b"plain", b"0", b"v"],
20051            &[b"ARGET", b"gone", b"0"],
20052            &[b"ARSET", b"a", b"bad", b"v"],
20053        ];
20054
20055        let mut one = Fixture::new();
20056        let mut many = Fixture::striped(8);
20057        for parts in script {
20058            let a = one.run(parts);
20059            let b = many.run(parts);
20060            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20061        }
20062    }
20063
20064    /// Every graph and vector set command, on one stripe and on eight.
20065    ///
20066    /// `VRANDMEMBER` is not in here for the reason `HRANDFIELD` with a count is
20067    /// not: it draws from the stripe's generator, and the stripes do not share
20068    /// one.
20069    #[test]
20070    fn the_graph_and_vector_groups_answer_the_same_however_many_stripes_there_are() {
20071        let script: &[&[&[u8]]] = &[
20072            &[b"G.NADD", b"g", b"n1", b"name", b"one"],
20073            &[b"G.NADD", b"g", b"n2", b"name", b"two"],
20074            &[b"G.NADD", b"g", b"n3"],
20075            &[b"G.NGET", b"g", b"n1"],
20076            &[b"G.NGET", b"g", b"gone"],
20077            &[b"G.EADD", b"g", b"n1", b"n2", b"knows"],
20078            &[b"G.EADD", b"g", b"n2", b"n3", b"knows"],
20079            &[b"G.OUT", b"g", b"n1", b"knows"],
20080            &[b"G.IN", b"g", b"n2", b"knows"],
20081            &[b"G.DEG", b"g", b"n1", b"knows"],
20082            &[b"G.DEG", b"g", b"n2", b"knows", b"BOTH"],
20083            &[b"G.NEIGH", b"g", b"n1", b"knows", b"DEPTH", b"2"],
20084            &[b"G.PATH", b"g", b"n1", b"n3"],
20085            &[b"G.EDEL", b"g", b"n1", b"n2", b"knows"],
20086            &[b"G.NDEL", b"g", b"n3"],
20087            &[b"G.NGET", b"g", b"n3"],
20088            // The vector set, which is one index under one key.
20089            &[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"e1"],
20090            &[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"e2"],
20091            &[b"VCARD", b"v"],
20092            &[b"VDIM", b"v"],
20093            &[b"VEMB", b"v", b"e1"],
20094            &[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0"],
20095            &[b"VSIM", b"v", b"ELE", b"e1"],
20096            &[b"VISMEMBER", b"v", b"e1"],
20097            &[b"VISMEMBER", b"v", b"gone"],
20098            &[b"VSETATTR", b"v", b"e1", b"{\"k\":1}"],
20099            &[b"VGETATTR", b"v", b"e1"],
20100            &[b"VRANGE", b"v", b"-", b"+"],
20101            &[b"VLINKS", b"v", b"e1"],
20102            &[b"VINFO", b"v"],
20103            &[b"VREM", b"v", b"e2"],
20104            &[b"VCARD", b"v"],
20105            // And the errors.
20106            &[b"SET", b"plain", b"v"],
20107            &[b"G.NGET", b"plain", b"n1"],
20108            &[b"VCARD", b"plain"],
20109            &[b"G.NADD", b"gone2", b"n"],
20110            &[b"VEMB", b"gone3", b"e"],
20111        ];
20112
20113        let mut one = Fixture::new();
20114        let mut many = Fixture::striped(8);
20115        for parts in script {
20116            let a = one.run(parts);
20117            let b = many.run(parts);
20118            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20119        }
20120    }
20121
20122    /// Every bloom filter, cuckoo filter, count min sketch, top k and t digest
20123    /// command, on one stripe and on eight.
20124    #[test]
20125    fn the_probabilistic_groups_answer_the_same_however_many_stripes_there_are() {
20126        let script: &[&[&[u8]]] = &[
20127            // The bloom filter.
20128            &[b"BF.RESERVE", b"bf", b"0.01", b"100"],
20129            &[b"BF.ADD", b"bf", b"a"],
20130            &[b"BF.ADD", b"bf", b"a"],
20131            &[b"BF.MADD", b"bf", b"b", b"c"],
20132            &[b"BF.EXISTS", b"bf", b"a"],
20133            &[b"BF.MEXISTS", b"bf", b"a", b"zz"],
20134            &[b"BF.CARD", b"bf"],
20135            &[b"BF.INFO", b"bf"],
20136            &[b"BF.INFO", b"bf", b"CAPACITY"],
20137            &[b"BF.DEBUG", b"bf"],
20138            &[b"BF.INSERT", b"made", b"CAPACITY", b"50", b"ITEMS", b"x"],
20139            &[b"BF.EXISTS", b"made", b"x"],
20140            &[b"BF.SCANDUMP", b"bf", b"0"],
20141            // The cuckoo filter.
20142            &[b"CF.RESERVE", b"cf", b"100"],
20143            &[b"CF.ADD", b"cf", b"a"],
20144            &[b"CF.ADDNX", b"cf", b"a"],
20145            &[b"CF.COUNT", b"cf", b"a"],
20146            &[b"CF.EXISTS", b"cf", b"a"],
20147            &[b"CF.MEXISTS", b"cf", b"a", b"zz"],
20148            &[b"CF.INSERT", b"cf", b"ITEMS", b"b", b"c"],
20149            &[b"CF.DEL", b"cf", b"a"],
20150            &[b"CF.COMPACT", b"cf"],
20151            &[b"CF.INFO", b"cf"],
20152            &[b"CF.DEBUG", b"cf"],
20153            &[b"CF.SCANDUMP", b"cf", b"0"],
20154            // The count min sketch.
20155            &[b"CMS.INITBYDIM", b"cms", b"100", b"5"],
20156            &[b"CMS.INITBYPROB", b"cms2", b"0.01", b"0.01"],
20157            &[b"CMS.INCRBY", b"cms", b"a", b"5", b"b", b"3"],
20158            &[b"CMS.QUERY", b"cms", b"a", b"b", b"gone"],
20159            &[b"CMS.INFO", b"cms"],
20160            // The top k sketch.
20161            &[b"TOPK.RESERVE", b"tk", b"3"],
20162            &[b"TOPK.ADD", b"tk", b"a", b"b", b"a"],
20163            &[b"TOPK.INCRBY", b"tk", b"c", b"4"],
20164            &[b"TOPK.QUERY", b"tk", b"a", b"zz"],
20165            &[b"TOPK.COUNT", b"tk", b"a", b"c"],
20166            &[b"TOPK.LIST", b"tk"],
20167            &[b"TOPK.LIST", b"tk", b"WITHCOUNT"],
20168            &[b"TOPK.INFO", b"tk"],
20169            // The t digest.
20170            &[b"TDIGEST.CREATE", b"td"],
20171            &[b"TDIGEST.ADD", b"td", b"1", b"2", b"3", b"4", b"5"],
20172            &[b"TDIGEST.MIN", b"td"],
20173            &[b"TDIGEST.MAX", b"td"],
20174            &[b"TDIGEST.QUANTILE", b"td", b"0.5"],
20175            &[b"TDIGEST.CDF", b"td", b"3"],
20176            &[b"TDIGEST.RANK", b"td", b"3"],
20177            &[b"TDIGEST.REVRANK", b"td", b"3"],
20178            &[b"TDIGEST.BYRANK", b"td", b"0"],
20179            &[b"TDIGEST.BYREVRANK", b"td", b"0"],
20180            &[b"TDIGEST.TRIMMED_MEAN", b"td", b"0.1", b"0.9"],
20181            &[b"TDIGEST.INFO", b"td"],
20182            &[b"TDIGEST.RESET", b"td"],
20183            &[b"TDIGEST.MIN", b"td"],
20184            // And the errors.
20185            &[b"SET", b"plain", b"v"],
20186            &[b"BF.ADD", b"plain", b"a"],
20187            &[b"CF.ADD", b"plain", b"a"],
20188            &[b"CMS.QUERY", b"plain", b"a"],
20189            &[b"TOPK.ADD", b"plain", b"a"],
20190            &[b"TDIGEST.ADD", b"plain", b"1"],
20191            &[b"CMS.INFO", b"gone"],
20192            &[b"TOPK.INFO", b"gone"],
20193            &[b"TDIGEST.INFO", b"gone"],
20194        ];
20195
20196        let mut one = Fixture::new();
20197        let mut many = Fixture::striped(8);
20198        for parts in script {
20199            let a = one.run(parts);
20200            let b = many.run(parts);
20201            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20202        }
20203    }
20204
20205    /// The two sketch merges, with their sources on stripes of their own.
20206    ///
20207    /// These are the only two commands in the ten groups that name more than one
20208    /// key, and both read a run of sources and write a destination, so both go
20209    /// wrong in the same way if a merge holds one store and looks every source up
20210    /// in it.
20211    #[test]
20212    fn a_sketch_merge_across_stripes_reads_every_source() {
20213        let mut many = Fixture::striped(8);
20214        let other = apart(&mut many, "s1");
20215        let (s1, s2) = (b"s1".as_slice(), other.as_bytes());
20216        let mut one = Fixture::new();
20217        let mut both = |parts: &[&[u8]]| {
20218            let a = one.run(parts);
20219            let b = many.run(parts);
20220            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20221            a
20222        };
20223
20224        // The count min sketch. The destination has to be the sources' shape,
20225        // and it is named first, so all three keys are read before anything is
20226        // written.
20227        for key in [b"cd".as_slice(), s1, s2] {
20228            both(&[b"CMS.INITBYDIM", key, b"100", b"5"]);
20229        }
20230        both(&[b"CMS.INCRBY", s1, b"x", b"5"]);
20231        both(&[b"CMS.INCRBY", s2, b"x", b"3"]);
20232        assert_eq!(
20233            both(&[b"CMS.MERGE", b"cd", b"2", s1, s2]),
20234            "+OK\r\n",
20235            "the merge took both sources"
20236        );
20237        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:8\r\n");
20238        // And with weights, which are read against the sources in order.
20239        both(&[b"CMS.MERGE", b"cd", b"2", s1, s2, b"WEIGHTS", b"2", b"1"]);
20240        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20241        // A source that is not a sketch is answered before anything is written.
20242        both(&[b"SET", b"plain", b"v"]);
20243        assert!(both(&[b"CMS.MERGE", b"cd", b"2", s1, b"plain"]).starts_with('-'));
20244        assert_eq!(both(&[b"CMS.QUERY", b"cd", b"x"]), "*1\r\n:13\r\n");
20245
20246        // The t digest, which builds its destination and then puts it in place.
20247        // The two source keys are used again here, so what they held goes first.
20248        both(&[b"FLUSHALL"]);
20249        both(&[b"TDIGEST.CREATE", b"td"]);
20250        both(&[b"TDIGEST.CREATE", s1]);
20251        both(&[b"TDIGEST.CREATE", s2]);
20252        both(&[b"TDIGEST.ADD", s1, b"1", b"2"]);
20253        both(&[b"TDIGEST.ADD", s2, b"9", b"10"]);
20254        assert_eq!(both(&[b"TDIGEST.MERGE", b"td", b"2", s1, s2]), "+OK\r\n");
20255        assert_eq!(both(&[b"TDIGEST.MIN", b"td"]), "$1\r\n1\r\n");
20256        assert_eq!(both(&[b"TDIGEST.MAX", b"td"]), "$2\r\n10\r\n");
20257    }
20258
20259    /// Every shape of `SORT`, on one stripe and on eight.
20260    ///
20261    /// The key it sorts, the keys a `BY` names, the keys a `GET` names and the
20262    /// destination are four different names and nothing lines them up, so on
20263    /// eight stripes this script is reading and writing all over the database
20264    /// while on one it is doing what it always did.
20265    #[test]
20266    fn the_sort_command_answers_the_same_however_many_stripes_there_are() {
20267        let script: &[&[&[u8]]] = &[
20268            &[b"RPUSH", b"l", b"3", b"1", b"2", b"10"],
20269            &[b"SORT", b"l"],
20270            &[b"SORT", b"l", b"DESC"],
20271            &[b"SORT", b"l", b"ALPHA"],
20272            &[b"SORT", b"l", b"LIMIT", b"1", b"2"],
20273            &[b"SORT_RO", b"l"],
20274            // A weight per element, so the order comes off keys the command
20275            // never named.
20276            &[
20277                b"MSET", b"w_1", b"4", b"w_2", b"3", b"w_3", b"2", b"w_10", b"1",
20278            ],
20279            &[b"SORT", b"l", b"BY", b"w_*"],
20280            &[b"SORT", b"l", b"BY", b"w_*", b"DESC"],
20281            &[b"DEL", b"w_2"],
20282            &[b"SORT", b"l", b"BY", b"w_*"],
20283            // And the answer off another set of keys again, with `#` mixed in
20284            // so the rows are not all lookups.
20285            &[b"MSET", b"d_1", b"one", b"d_3", b"three"],
20286            &[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"],
20287            // A pattern that reaches into a hash, which is another key again.
20288            &[b"HSET", b"h_1", b"f", b"9"],
20289            &[b"HSET", b"h_2", b"f", b"8"],
20290            &[b"HSET", b"h_3", b"f", b"7"],
20291            &[b"HSET", b"h_10", b"f", b"6"],
20292            &[b"SORT", b"l", b"BY", b"h_*->f"],
20293            &[b"SORT", b"l", b"BY", b"nosort", b"GET", b"h_*->f"],
20294            // The destination, which is a fourth place to land.
20295            &[b"SORT", b"l", b"BY", b"w_*", b"STORE", b"out"],
20296            &[b"LRANGE", b"out", b"0", b"-1"],
20297            &[b"SORT", b"l", b"STORE", b"l"],
20298            &[b"LRANGE", b"l", b"0", b"-1"],
20299            // An empty result takes the destination away rather than leaving a
20300            // list of nothing behind.
20301            &[b"SORT", b"missing", b"STORE", b"out"],
20302            &[b"EXISTS", b"out"],
20303            // A set and a sorted set sort the same way a list does, and a set
20304            // written to a destination is sorted even when nothing asked.
20305            &[b"SADD", b"s", b"c", b"a", b"b"],
20306            &[b"SORT", b"s", b"ALPHA"],
20307            &[b"SORT", b"s", b"BY", b"nosort", b"STORE", b"out"],
20308            &[b"LRANGE", b"out", b"0", b"-1"],
20309            &[b"ZADD", b"z", b"3", b"c", b"1", b"a", b"2", b"b"],
20310            &[b"SORT", b"z", b"BY", b"nosort"],
20311            &[b"SORT", b"z", b"ALPHA", b"DESC"],
20312            // And the two ways it refuses: a key of the wrong type, and an
20313            // element that is not a number under a numeric sort.
20314            &[b"SET", b"str", b"v"],
20315            &[b"SORT", b"str"],
20316            &[b"RPUSH", b"words", b"one", b"two"],
20317            &[b"SORT", b"words"],
20318            &[b"SORT_RO", b"l", b"STORE", b"out"],
20319        ];
20320
20321        let mut one = Fixture::new();
20322        let mut many = Fixture::striped(8);
20323        for parts in script {
20324            let a = one.run(parts);
20325            let b = many.run(parts);
20326            assert_eq!(a, b, "{}", String::from_utf8_lossy(parts[0]));
20327        }
20328    }
20329
20330    /// One `SORT` whose four kinds of key are on stripes of their own.
20331    ///
20332    /// The script above spreads keys around by writing enough of them, and this
20333    /// one checks the spread rather than trusting it: the list, the weight key
20334    /// for one of its elements and the destination are asserted to be in three
20335    /// places before the command runs.
20336    #[test]
20337    fn a_sort_across_stripes_reads_every_pattern_key() {
20338        let mut f = Fixture::striped(8);
20339        let out = apart(&mut f, "l");
20340        let (list, dest) = (b"l".as_slice(), out.as_bytes());
20341
20342        f.run(&[b"RPUSH", list, b"a", b"b", b"c", b"d"]);
20343        f.run(&[
20344            b"MSET", b"w_a", b"4", b"w_b", b"3", b"w_c", b"2", b"w_d", b"1",
20345        ]);
20346        f.run(&[
20347            b"MSET", b"d_a", b"A", b"d_b", b"B", b"d_c", b"C", b"d_d", b"D",
20348        ]);
20349
20350        // The weights are four keys and they are not all in one place, which is
20351        // the thing that would go unnoticed if the command held a stripe.
20352        let db = f.server.striped(0);
20353        let weights: Vec<usize> = [b"w_a", b"w_b", b"w_c", b"w_d"]
20354            .iter()
20355            .map(|k| db.stripe_of(k.as_slice()))
20356            .collect();
20357        assert!(
20358            weights.iter().any(|s| *s != weights[0]),
20359            "the four weight keys all landed on one stripe, so this proves nothing"
20360        );
20361
20362        assert_eq!(
20363            f.run(&[b"SORT", list, b"BY", b"w_*", b"GET", b"d_*"]),
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 order came off the weights and the answer off the data keys"
20366        );
20367        assert_eq!(
20368            f.run(&[b"SORT", list, b"BY", b"w_*", b"STORE", dest]),
20369            ":4\r\n"
20370        );
20371        assert_eq!(
20372            f.run(&[b"LRANGE", dest, b"0", b"-1"]),
20373            "*4\r\n$1\r\nd\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n",
20374            "the destination is on a stripe of its own and got the whole answer"
20375        );
20376    }
20377
20378    /// A `CONFIG SET` reaches every stripe, so where a key landed does not
20379    /// decide what shape it is stored in.
20380    ///
20381    /// This is the setting that would go wrong quietly. A stripe that kept the
20382    /// old ladder would hold the same hash in a different encoding from the
20383    /// stripe next to it, and the only thing that would ever say so is
20384    /// `OBJECT ENCODING`, which is why the check is on that.
20385    #[test]
20386    fn a_setting_reaches_every_stripe_and_reads_back_from_any_of_them() {
20387        let mut f = Fixture::striped(8);
20388        let other = apart(&mut f, "h");
20389        let (first, second) = (b"h".as_slice(), other.as_bytes());
20390
20391        assert_eq!(
20392            f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"2"]),
20393            "+OK\r\n"
20394        );
20395        assert_eq!(
20396            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
20397            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n",
20398            "the read comes off one stripe and has to answer for all of them"
20399        );
20400        for key in [first, second] {
20401            f.run(&[b"HSET", key, b"a", b"1", b"b", b"2"]);
20402            assert_eq!(
20403                f.run(&[b"OBJECT", b"ENCODING", key]),
20404                "$8\r\nlistpack\r\n",
20405                "two fields is still under the ladder"
20406            );
20407            f.run(&[b"HSET", key, b"c", b"3"]);
20408            assert_eq!(
20409                f.run(&[b"OBJECT", b"ENCODING", key]),
20410                "$9\r\nhashtable\r\n",
20411                "three fields is over it, on whichever stripe the key is on"
20412            );
20413        }
20414
20415        // And the policy, which every stripe has to agree about for the same
20416        // reason: an eviction draws from one stripe at a time.
20417        assert_eq!(
20418            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]),
20419            "+OK\r\n"
20420        );
20421        let db = f.server.striped(0);
20422        assert!(
20423            db.stripes_mut().all(|s| s.policy().name() == "allkeys-lru"),
20424            "a stripe kept the old policy"
20425        );
20426    }
20427}