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 server;
75mod sets;
76mod streams;
77mod strings;
78pub mod table;
79mod tdigest;
80mod topk;
81mod vectors;
82mod vfilter;
83mod zsets;
84
85pub use args::Args;
86pub use blocking::{Parked, Waiters};
87pub use server::parse_memory;
88pub use table::{COMMANDS, Spec, arity_ok, lookup};
89
90use crate::reply::Out;
91use std::path::{Path, PathBuf};
92use yo_common::{Code, Error};
93use yo_kv::cold::Blocks;
94use yo_kv::{Clock, Keyspace};
95
96/// How many databases a server has.
97///
98/// Redis's default is sixteen and its `databases` setting can change it. Ours
99/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
100/// constant. Nothing in the design needs the number to be fixed; nothing yet
101/// needs it not to be.
102pub const DATABASES: usize = 16;
103
104/// Every database's bit in [`Server::dirty`], which is what a fresh server
105/// starts on so that the first maintenance turn asks all of them.
106///
107/// A `u64` holds sixteen bits with room to spare, and the assertion below is
108/// what turns raising [`DATABASES`] past sixty four into a build failure rather
109/// than a shift that silently drops the databases past the end.
110const ALL_DATABASES: u64 = if DATABASES == 64 {
111    u64::MAX
112} else {
113    (1u64 << DATABASES) - 1
114};
115const _: () = assert!(DATABASES <= 64);
116
117/// How many keys one command throws away before it leaves the rest to the next.
118///
119/// A bound and not a loop to the end, because this runs in front of a client
120/// that is waiting for its reply, and a server a long way over its limit would
121/// otherwise hold that client for as long as it took to walk all the way back
122/// under. Sixty four is a batch's worth of commands, so a server that went over
123/// by what one batch allocated comes back under in one command, and a server
124/// whose limit was just cut in half works through it over the next few thousand
125/// rather than in one long stall. Redis bounds the same loop by a time slice
126/// instead of a count and hands the rest to a timer; there is no timer here, so
127/// the rest goes to the next command that runs.
128const EVICT_BUDGET: usize = 64;
129
130/// What a server says to a command that would allocate when it has no room.
131///
132/// Redis's `shared.oomerr`, word for word including the full stop, because
133/// clients match on the `OOM` prefix and people match on the sentence.
134const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
135
136/// What the connection should do after a command.
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum Flow {
139    /// Read the next command.
140    Continue,
141    /// Write what is buffered and then close, which is what `QUIT` asks for.
142    Close,
143    /// Nothing was written and nothing is owed yet.
144    ///
145    /// The client is on the waiter list and its reply comes when a key it named
146    /// has something in it or when its deadline passes, whichever happens first.
147    /// Until then the connection stops reading commands, because a client that
148    /// is waiting for an answer is not a client that has sent another question.
149    Block,
150}
151
152/// The numbers `INFO` reports that this layer cannot see for itself.
153///
154/// The reactor owns the sockets, so the reactor is what knows how many clients
155/// there are. It writes these directly and nothing here does anything with them
156/// except report them.
157#[derive(Debug, Clone, Copy, Default)]
158pub struct Stats {
159    /// Connections open right now.
160    pub clients: u64,
161    /// Connections accepted since the server started.
162    pub connections: u64,
163    /// Commands run since the server started, which this layer counts itself.
164    pub commands: u64,
165}
166
167/// Where the process was started, which is what `dir` defaults to.
168///
169/// A dot if the working directory cannot be read, which happens when it has
170/// been deleted out from under a running process. That is not a reason to
171/// refuse to start a server, and it leaves `BACKUP` to fail with the real error
172/// from the filesystem if anybody asks for one.
173fn working_dir() -> PathBuf {
174    std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
175}
176
177/// One command's counters, for `INFO commandstats`.
178///
179/// Three of Redis's five. `usec` and `usec_per_call` are not here because
180/// nothing times a command, and timing one means two clock reads around a call
181/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
182/// has room for it; this does not, and a zero under a name that says microseconds
183/// is worse than an absent field, which is the same rule the rest of `INFO`
184/// follows.
185#[derive(Debug, Clone, Copy, Default)]
186pub struct CommandStat {
187    /// Times the command ran, whatever it answered.
188    pub calls: u64,
189    /// Times it was turned away before it ran, which is the wrong number of
190    /// arguments or no room under `maxmemory`.
191    pub rejected: u64,
192    /// Times it ran and answered with an error.
193    pub failed: u64,
194}
195
196impl CommandStat {
197    /// Whether this command has ever been seen.
198    ///
199    /// A row that has not is left out of the reply, which is what Redis does and
200    /// is why the section is a handful of lines on a working server rather than
201    /// one line per command in the table.
202    const fn seen(&self) -> bool {
203        self.calls != 0 || self.rejected != 0 || self.failed != 0
204    }
205}
206
207/// A counter per command, indexed the way [`table::index_of`] says.
208///
209/// A flat array and not a map, because the dispatcher is already holding the
210/// spec and the spec's position in the table is two addresses subtracted. That
211/// makes the counting a load, an add and a store on a row the previous command
212/// of the same name has already pulled into cache.
213struct CommandStats(Box<[CommandStat]>);
214
215impl Default for CommandStats {
216    fn default() -> CommandStats {
217        CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
218    }
219}
220
221impl CommandStats {
222    /// The row for one command.
223    fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
224        &mut self.0[table::index_of(spec)]
225    }
226}
227
228/// Where a database gets its store from, asked by database number.
229///
230/// `None` means that database cannot have one. The caller owns whatever the
231/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
232/// database, and this crate never learns what any of that is.
233pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
234
235/// Everything a server holds.
236///
237/// One of these per shard thread, not one per process: the databases inside are
238/// not `Sync` and are reached by sending their thread a command. What makes
239/// this a server rather than a shard is that it is the whole of what a
240/// connection can address.
241pub struct Server {
242    dbs: Vec<Keyspace>,
243    clock: Clock,
244    started_ms: u64,
245    /// Where the next maintenance turn starts looking, so that a database
246    /// under constant write load cannot hold the other fifteen's space.
247    next_db: usize,
248    /// One bit per database, set when a command ran against it.
249    ///
250    /// The maintenance turn after every batch used to ask all sixteen
251    /// databases whether they had anything to collect, and asking costs a load
252    /// and a store in each one. Fifteen of those are cold lines on a server
253    /// where every client is on database zero, which is every server, and the
254    /// answer is no every time. This is the cheap half of the question: a
255    /// database nobody has touched since it last said no cannot have started
256    /// saying yes.
257    dirty: u64,
258    /// What the connections are holding, kept by the engine.
259    conn_bytes: usize,
260    /// The `maxmemory` limit in bytes, zero when there is not one.
261    ///
262    /// Zero is the default and it is the whole reason the check in front of
263    /// every write is one comparison against a field that is already warm.
264    maxmemory: u64,
265    /// Where a database gets a store from the first time it needs one.
266    ///
267    /// A closure and not a store, because there are sixteen databases and a
268    /// server that fills memory on database zero should not have opened
269    /// anything for the other fifteen. Nothing is asked of this until a memory
270    /// limit is actually reached, so a server that never fills memory never
271    /// opens a file, and a server that has no file never has one of these.
272    ///
273    /// `None` from the closure means that database cannot have one, which is
274    /// how the caller says the file it opened has no more room for logs.
275    store: Option<Box<StoreSource>>,
276    /// The `maxstore` limit in bytes, `None` when there is not one.
277    ///
278    /// The storage limit, and the other half of the inversion `14` section 4.1
279    /// describes. `maxmemory` is a limit on memory and the right answer to a
280    /// memory limit on a system with a file under it is to move data to the
281    /// file, not to delete it. Deleting is the right answer to a limit on the
282    /// file, and this is that limit.
283    ///
284    /// Zero is not "no limit" here, which is the one place this reads
285    /// differently from `maxmemory` and is the difference that makes a drop in
286    /// cache possible. A storage budget of zero bytes means nothing may live on
287    /// the file, so migration cannot make room and eviction is the only thing
288    /// left, which is Redis exactly. `None` is no limit and is the default,
289    /// which with `noeviction` means the database grows until the disk is full
290    /// and then writes fail, which is what a database does.
291    maxstore: Option<u64>,
292    /// What [`Server::memory_bytes`] said at the last maintenance turn.
293    ///
294    /// The reading is a walk over every collection in every database and cannot
295    /// go on a command path, so the command path reads this instead and is at
296    /// most one batch behind. What that costs is overshoot: a server can end a
297    /// batch holding one batch's worth of allocation more than its limit before
298    /// anything notices. A batch is 64 commands, so that is bounded by what 64
299    /// commands can allocate and not by how long the server runs.
300    ///
301    /// Only kept up to date when there is a limit to judge it against. A server
302    /// with no `maxmemory` never reads it and never pays for it.
303    used: usize,
304    /// Which database the next eviction draws from.
305    ///
306    /// Its own cursor and not [`Server::next_db`], because eviction and
307    /// compaction move at different rates and sharing one would make the
308    /// database that gets compacted depend on how many keys were evicted.
309    evict_db: usize,
310    /// Which database the next active expiry sweep starts at.
311    ///
312    /// A third cursor for the same reason there is a second one. A sweep runs on
313    /// every turn of the loop and compaction runs when there is dead space, so
314    /// sharing a cursor would make which database gets swept depend on which one
315    /// was last collected.
316    expire_db: usize,
317    /// The millisecond the last active expiry sweep ran on, so the next one on
318    /// the same millisecond does not bother.
319    expire_ms: u64,
320    /// Clients parked on a blocking command.
321    waiters: Waiters,
322    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
323    ///
324    /// Empty on a server nobody has migrated a key out of, which is nearly all
325    /// of them, and it costs a vector's three words to be empty.
326    peers: migrate::Peers,
327    /// The numbers the reactor keeps for `INFO`.
328    pub stats: Stats,
329    /// A counter per command, for `INFO commandstats`.
330    cmdstats: CommandStats,
331    /// Where `BACKUP` puts its files, and where `CONFIG GET dir` points.
332    ///
333    /// Absolute, and resolved once when the server is built rather than every
334    /// time somebody asks. `BACKUP LIST` answers absolute paths and a client is
335    /// entitled to hand one of them to a copy tool, so a relative path that
336    /// meant something different after a `chdir` would be a path that stops
337    /// working for reasons nobody could see.
338    dir: PathBuf,
339    /// What backup is running, if one is.
340    ///
341    /// On the server and not on a session, because a backup outlives the
342    /// connection that asked for it and any other connection can seal it.
343    backup: backup::State,
344    /// Set by `SHUTDOWN`, and read by whatever is turning the loop.
345    ///
346    /// A flag rather than an exit, because the command layer is not what owns
347    /// the process. It runs inside a batch that has other commands behind it
348    /// and inside a driver that has a socket file to take away and a file to
349    /// close, and a server that calls `exit` from a command handler skips all
350    /// of that. So the command says stop and the driver stops, on the same turn
351    /// and through the same door a signal uses.
352    stopping: bool,
353}
354
355impl Server {
356    /// A server with [`DATABASES`] empty databases on the system clock.
357    #[must_use]
358    pub fn new() -> Server {
359        let clock = Clock::system();
360        Server {
361            dbs: (0..DATABASES)
362                .map(|_| Keyspace::with_clock(clock))
363                .collect(),
364            clock,
365            started_ms: clock.now_ms(),
366            next_db: 0,
367            dirty: ALL_DATABASES,
368            conn_bytes: 0,
369            maxmemory: 0,
370            store: None,
371            maxstore: None,
372            used: 0,
373            evict_db: 0,
374            expire_db: 0,
375            expire_ms: 0,
376            waiters: Waiters::default(),
377            peers: migrate::Peers::default(),
378            stats: Stats::default(),
379            cmdstats: CommandStats::default(),
380            dir: working_dir(),
381            backup: backup::State::default(),
382            stopping: false,
383        }
384    }
385
386    /// A server on a clock the caller moves by hand, for tests.
387    #[must_use]
388    pub fn with_clock(clock: Clock) -> Server {
389        Server {
390            dbs: (0..DATABASES)
391                .map(|_| Keyspace::with_clock(clock))
392                .collect(),
393            clock,
394            started_ms: clock.now_ms(),
395            next_db: 0,
396            dirty: ALL_DATABASES,
397            conn_bytes: 0,
398            maxmemory: 0,
399            store: None,
400            maxstore: None,
401            used: 0,
402            evict_db: 0,
403            expire_db: 0,
404            expire_ms: 0,
405            waiters: Waiters::default(),
406            peers: migrate::Peers::default(),
407            stats: Stats::default(),
408            cmdstats: CommandStats::default(),
409            dir: working_dir(),
410            backup: backup::State::default(),
411            stopping: false,
412        }
413    }
414
415    /// One database, by index.
416    ///
417    /// # Panics
418    ///
419    /// If `i` is not a database. `SELECT` is the only way a client changes the
420    /// index and it checks, so an index that is out of range here is a bug in
421    /// the caller and not something a client can ask for.
422    pub fn db(&mut self, i: usize) -> &mut Keyspace {
423        // The borrow is mutable, so assume it is used. Anything that only reads
424        // has [`Server::db_ref`] and does not come through here.
425        self.dirty |= 1u64 << i;
426        &mut self.dbs[i]
427    }
428
429    /// Where `BACKUP` writes and what `CONFIG GET dir` answers.
430    #[must_use]
431    pub fn dir(&self) -> &Path {
432        &self.dir
433    }
434
435    /// Point the server at a different directory, which `yodb serve --dir` does.
436    ///
437    /// Only before it is serving. There is no `CONFIG SET dir` here and there
438    /// is none on a real server either without turning protected configs on,
439    /// for the good reason that moving it out from under a running backup would
440    /// leave files nothing can find again.
441    pub fn set_dir(&mut self, dir: PathBuf) {
442        self.dir = dir;
443    }
444
445    /// Drop a sealed backup that has outlived `backup-sealed-ttl`.
446    ///
447    /// Once per batch, from the same maintenance turn that collects the arena.
448    /// It reads two fields and returns on a server that has never taken a
449    /// backup, which is nearly all of them.
450    pub fn backup_expire(&mut self) {
451        backup::expire(self);
452    }
453
454    /// Ask for the server to stop, which is what `SHUTDOWN` does.
455    ///
456    /// It sets a flag and returns. Nothing here closes a socket, flushes a file
457    /// or ends the process, because none of those belong to this layer, and a
458    /// batch that is halfway through still has to finish and be written out.
459    pub fn stop(&mut self) {
460        self.stopping = true;
461    }
462
463    /// Whether somebody has asked the server to stop.
464    ///
465    /// Read once per turn by the loop, next to the flag a signal sets. The two
466    /// mean the same thing and are separate only because one arrives from the
467    /// operating system and the other from a client.
468    #[must_use]
469    pub fn stopping(&self) -> bool {
470        self.stopping
471    }
472
473    /// One database, by index, without taking it mutably.
474    ///
475    /// What the prefetch stage needs. It runs for all 64 commands in a batch
476    /// before any of them executes, so it cannot hold the mutable borrow `run`
477    /// is about to want, and it does not need one: warming a cache line reads
478    /// nothing and changes nothing.
479    ///
480    /// # Panics
481    ///
482    /// As [`Server::db`].
483    #[must_use]
484    pub fn db_ref(&self, i: usize) -> &Keyspace {
485        &self.dbs[i]
486    }
487
488    /// Take a new clock reading and give it to every database.
489    ///
490    /// Once per turn of the event loop, which is the only place time moves. A
491    /// command asking what the time is gets the answer the whole batch got, so
492    /// two keys written by the same batch expire together (`04` section 3).
493    pub fn refresh_clock(&mut self) {
494        self.clock.refresh();
495        let now = self.clock.now_ms();
496        for db in &mut self.dbs {
497            db.clock_mut().set(now);
498        }
499    }
500
501    /// Move every clock here to `ms` by hand, for tests about expiry.
502    ///
503    /// A test cannot wait a hundred seconds and a test that waits a hundred
504    /// milliseconds is a test that fails on a loaded machine, so time moves on
505    /// request. The system clock underneath will overwrite this on the next
506    /// [`Server::refresh_clock`], which is why this is only useful in a test
507    /// that drives commands directly rather than through the event loop.
508    pub fn set_clock_ms(&mut self, ms: u64) {
509        self.clock.set(ms);
510        for db in &mut self.dbs {
511            db.clock_mut().set(ms);
512        }
513    }
514
515    /// Seconds since this server was built.
516    #[must_use]
517    pub fn uptime_secs(&self) -> u64 {
518        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
519    }
520
521    /// Bytes held by every database's index and arena, plus the read and reply
522    /// buffers of every connection.
523    ///
524    /// The buffers are in here because they are real and because Redis counts
525    /// its own, so leaving them out would make the one number people compare
526    /// flattering rather than true. They are not a database, so nothing in the
527    /// keyspace can change them and the engine has to say when they move.
528    #[must_use]
529    pub fn memory_bytes(&self) -> usize {
530        self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
531    }
532
533    /// What the keyspace itself is holding, live records only.
534    ///
535    /// `used_memory` minus this is what the store costs to run: the index, the
536    /// space dead records are sitting in until compaction gets to them, and the
537    /// connections' buffers.
538    #[must_use]
539    pub fn dataset_bytes(&self) -> usize {
540        self.dbs
541            .iter()
542            .map(|db| db.map().arena().live_bytes() as usize)
543            .sum()
544    }
545
546    /// Bytes the arenas are holding, live and dead together.
547    #[must_use]
548    pub fn arena_bytes(&self) -> usize {
549        self.dbs
550            .iter()
551            .map(|db| db.map().arena().reserved_bytes() as usize)
552            .sum()
553    }
554
555    /// Bytes the indexes are holding.
556    #[must_use]
557    pub fn index_bytes(&self) -> usize {
558        self.dbs
559            .iter()
560            .map(|db| db.map().index().memory_bytes())
561            .sum()
562    }
563
564    /// What arena compaction has cost, across every database.
565    ///
566    /// The write amplification of value separation, which is invisible from the
567    /// outside otherwise: a client that writes a megabyte can leave the store
568    /// copying several more, and the only sign of it without these is that the
569    /// writes got slower.
570    #[must_use]
571    pub fn compaction(&self) -> yo_kv::Compaction {
572        self.dbs.iter().map(|db| db.map().compaction()).fold(
573            yo_kv::Compaction::default(),
574            |a, b| yo_kv::Compaction {
575                walked: a.walked + b.walked,
576                moved: a.moved + b.moved,
577                bytes: a.bytes + b.bytes,
578            },
579        )
580    }
581
582    /// Arena segments whose pages are real, across every database.
583    #[must_use]
584    pub fn segment_count(&self) -> usize {
585        self.dbs
586            .iter()
587            .map(|db| db.map().arena().resident_segments())
588            .sum()
589    }
590
591    /// What the connections' read and reply buffers are holding.
592    #[must_use]
593    pub const fn conn_bytes(&self) -> usize {
594        self.conn_bytes
595    }
596
597    /// Note that the connections are holding `delta` bytes more than they were,
598    /// or fewer when it is negative.
599    ///
600    /// A delta and not a total because the alternative is a walk over every
601    /// connection, and the walk would have to happen on a turn of the loop
602    /// rather than when `INFO` asks, which puts the cost of a report on the
603    /// command path of a server nobody is asking.
604    pub fn note_conn_bytes(&mut self, delta: isize) {
605        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
606    }
607
608    /// Keys reclaimed by running into them after their deadline.
609    #[must_use]
610    pub fn expired_keys(&self) -> u64 {
611        self.dbs.iter().map(Keyspace::expired_keys).sum()
612    }
613
614    /// Keys thrown away to make room, which is the other number entirely.
615    #[must_use]
616    pub fn evicted_keys(&self) -> u64 {
617        self.dbs.iter().map(Keyspace::evicted_keys).sum()
618    }
619
620    /// Every command that has been seen, with its counters.
621    ///
622    /// Only the ones that have. A server reports a handful of lines rather than
623    /// one per command in the table, which is what Redis does and is the
624    /// difference between a section a person can read and one they cannot.
625    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
626        self.cmdstats
627            .0
628            .iter()
629            .enumerate()
630            .filter(|(_, row)| row.seen())
631            .map(|(at, row)| (table::name_at(at), *row))
632    }
633
634    /// The `maxmemory` limit in bytes, zero when there is not one.
635    #[must_use]
636    pub const fn maxmemory(&self) -> u64 {
637        self.maxmemory
638    }
639
640    /// Set the limit, and take a reading straight away.
641    ///
642    /// The reading is here rather than left to the next maintenance turn because
643    /// a client that sets the limit and sends a write in the same batch expects
644    /// the write to be judged against the limit it just set, and because the
645    /// cached number is meaningless until the first time there is a limit to
646    /// compare it with.
647    ///
648    /// Turning the limit on also turns on the running total every slab keeps of
649    /// what its collections hold, and turning it off turns that back off, so a
650    /// server with no limit is not paying to count something nobody reads. The
651    /// first reading after switching it on is the walk that the total starts
652    /// from, and it is the only walk.
653    pub fn set_maxmemory(&mut self, bytes: u64) {
654        self.maxmemory = bytes;
655        for db in &mut self.dbs {
656            db.track_memory(bytes != 0);
657        }
658        self.used = self.settled_memory();
659    }
660
661    /// Say where a database should get its store from when it needs one.
662    ///
663    /// This is what turns the eviction inversion on. Until it is called every
664    /// database answers a memory limit by evicting, which is Redis, and after it
665    /// is called a database under memory pressure moves values to whatever the
666    /// closure hands back instead of throwing keys away.
667    ///
668    /// Called at most once per database and only under pressure, so a server
669    /// that is given a file and never fills memory never touches it.
670    pub fn set_store_source(
671        &mut self,
672        source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
673    ) {
674        self.store = Some(Box::new(source));
675    }
676
677    /// Whether this server has been given somewhere to put cold values.
678    #[must_use]
679    pub const fn has_store_source(&self) -> bool {
680        self.store.is_some()
681    }
682
683    /// Open database `at`'s store, if it has not got one and there is one to be
684    /// had.
685    ///
686    /// A store that will not open leaves the database where it was, which is
687    /// evicting, because a memory limit that cannot be answered by moving data
688    /// still has to be answered.
689    fn attach_store(&mut self, at: usize) {
690        if self.dbs[at].store_bytes().is_some() {
691            return;
692        }
693        let Some(source) = self.store.as_mut() else {
694            return;
695        };
696        if let Some(blocks) = source(at) {
697            self.dbs[at].attach(blocks);
698        }
699    }
700
701    /// The `maxstore` limit in bytes, `None` when there is not one.
702    #[must_use]
703    pub const fn maxstore(&self) -> Option<u64> {
704        self.maxstore
705    }
706
707    /// Set the storage limit, or clear it with `None`.
708    ///
709    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
710    /// total, because this limit is compared against a number the store keeps
711    /// and answers on demand, not against a walk.
712    pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
713        self.maxstore = bytes;
714    }
715
716    /// What every attached store is holding, for `INFO memory`.
717    ///
718    /// Zero on a server with nothing attached, which is not the same as a server
719    /// whose file is empty, and [`Server::regime`] is the field that tells those
720    /// two apart.
721    #[must_use]
722    pub fn store_bytes(&self) -> u64 {
723        self.dbs.iter().filter_map(Keyspace::store_bytes).sum()
724    }
725
726    /// What the file has been asked to do, added up over every database.
727    ///
728    /// Counters and not levels, so they only ever go up and a run is the
729    /// difference between two readings. G9 is a ratio over these: the faults a
730    /// run took, divided by the point reads it issued, has to come out at 1.05
731    /// or less with a working set ten times memory. There is no way to work that
732    /// out from outside the server, so it is reported rather than inferred.
733    ///
734    /// A fault is a read that went to the store. Whether it also went to the
735    /// device depends on the store: a log serves a read out of a resident page
736    /// without touching anything. At ten times memory almost every fault is a
737    /// real read, which is why the gate is written against this number, but the
738    /// two are not the same thing and a run tight against the bar should be
739    /// checked against what the operating system says.
740    #[must_use]
741    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
742        let mut total = yo_kv::tier::Stats::default();
743        for db in &self.dbs {
744            let Some(tier) = db.tier() else { continue };
745            let s = tier.stats();
746            total.demoted += s.demoted;
747            total.promoted += s.promoted;
748            total.faults += s.faults;
749            total.served += s.served;
750            total.bytes_out += s.bytes_out;
751            total.bytes_in += s.bytes_in;
752        }
753        total
754    }
755
756    /// Which way this server answers a memory limit, in one word for `INFO`.
757    ///
758    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
759    /// inversion: a memory limit moves values to the file and nothing stored is
760    /// lost. A server reports one word rather than leaving an operator to work
761    /// it out from a limit, a setting and whether a file happens to be open.
762    #[must_use]
763    pub fn regime(&self) -> &'static str {
764        if (0..self.dbs.len()).any(|at| self.migrates(at)) {
765            "migrate"
766        } else {
767            "evict"
768        }
769    }
770
771    /// Whether database `at` answers a memory limit by moving values to the
772    /// file rather than by throwing keys away.
773    ///
774    /// Three things have to hold. There has to be somewhere to move them, which
775    /// is a store attached to that database or a source that can open one, and
776    /// on a server that was never given a file this is false everywhere and
777    /// every database behaves exactly as it did.
778    /// The storage budget has to be more than nothing, which is what
779    /// `maxstore 0` says it is not. And the file has to be under that budget,
780    /// because a full file is a storage limit reached and eviction is the right
781    /// answer to a storage limit.
782    fn migrates(&self, at: usize) -> bool {
783        if self.maxstore == Some(0) {
784            return false;
785        }
786        match self.dbs[at].store_bytes() {
787            Some(held) => self.maxstore.is_none_or(|cap| held < cap),
788            // Nothing attached, but somewhere to get one from the moment this
789            // database needs it, which is what makes the answer yes rather than
790            // no. Opening it here would mean `INFO` opened files.
791            None => self.store.is_some(),
792        }
793    }
794
795    /// Take a fresh memory reading, which the maintenance turn does once a batch.
796    ///
797    /// Nothing at all when there is no limit, which is the default and is every
798    /// server that has not asked for one.
799    pub fn refresh_memory(&mut self) {
800        if self.maxmemory != 0 {
801            self.used = self.settled_memory();
802        }
803    }
804
805    /// [`Server::memory_bytes`], asked the cheap way.
806    ///
807    /// The same number. The difference is that this asks each database only
808    /// about the collections that could have moved since the last time, which is
809    /// what a batch touched rather than what the server holds, so it can be
810    /// asked once a batch and again on every command that is over the limit.
811    fn settled_memory(&mut self) -> usize {
812        self.dbs
813            .iter_mut()
814            .map(Keyspace::settled_memory_bytes)
815            .sum::<usize>()
816            + self.conn_bytes
817    }
818
819    /// Make room under the `maxmemory` limit, throwing keys away if that is what
820    /// it takes. Answers whether there is anything left it could throw away.
821    ///
822    /// Redis runs the same thing from `processCommand` before every command and
823    /// so does this: a client that writes has to be judged at the moment it
824    /// writes, not a batch later, or the limit is a suggestion.
825    ///
826    /// Three things happen in the loop and all three are needed. Eviction picks
827    /// a key and drops it. Compaction gives the pages back, because dropping a
828    /// key marks its record dead and returns nothing on its own, so a loop that
829    /// only evicted would throw the whole keyspace away and watch the number
830    /// stay where it was. The reading is taken again each time round, because
831    /// the two of them together are the only thing that moves it.
832    ///
833    /// # Why running out of budget is not a no
834    ///
835    /// `false` means there was nothing left to evict, which is `noeviction`, or
836    /// a `volatile` policy on a database where nothing has a deadline, or a
837    /// keyspace that is already empty. It does not mean the server is still over
838    /// its limit, and that difference is Redis's: `performEvictions` answers
839    /// `EVICT_FAIL` only when it has run out of things to delete, and
840    /// `processCommand` refuses the client on that and on nothing else. Running
841    /// out of time part way through a job it is doing well comes back as
842    /// `EVICT_RUNNING` and the command goes through, because a server that is
843    /// evicting steadily and refusing every write while it does it is worse for
844    /// the client than a little overshoot.
845    ///
846    /// # What the limit is worth
847    ///
848    /// Space comes back a segment at a time and a segment is two megabytes, so
849    /// this holds a server to its limit give or take a segment. A `maxmemory` of
850    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
851    /// megabytes is asking for a precision this store does not have.
852    pub fn make_room(&mut self) -> bool {
853        if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
854            return true;
855        }
856        // The cached reading is a batch old and the batch may have compacted
857        // since, so take a fresh one before throwing anything away. It is the
858        // settled reading and not the walk, so what this costs is the handful of
859        // collections the last batch touched and not the whole database.
860        self.used = self.settled_memory();
861        let mut budget = EVICT_BUDGET;
862        while self.used as u64 > self.maxmemory {
863            let over = self.used - self.maxmemory as usize;
864            if !self.relieve_step(over) {
865                return false;
866            }
867            self.compact_hard_step();
868            self.used = self.settled_memory();
869            budget -= 1;
870            if budget == 0 {
871                break;
872            }
873        }
874        true
875    }
876
877    /// Give back `over` bytes from whichever database can, by moving values to
878    /// the file where there is one and by throwing keys away where there is not.
879    ///
880    /// The two answers are the eviction inversion and which one a database gets
881    /// is [`Server::migrates`]. Answers whether anything was given back at all,
882    /// and `false` is what refuses the client's write.
883    ///
884    /// A store that will not take the bytes counts as nothing given back, so the
885    /// write is refused rather than turned into a deletion. A disk that is
886    /// misbehaving is a reason to stop accepting writes and it is not a reason
887    /// to start losing data that was accepted already.
888    ///
889    /// Round robin from a cursor rather than always starting at database zero,
890    /// so a server using more than one of them does not empty the first before
891    /// touching the second. Almost every server is on database zero only, where
892    /// this is one call that answers and fifteen that say the map is empty.
893    fn relieve_step(&mut self, over: usize) -> bool {
894        for turn in 0..self.dbs.len() {
895            let i = (self.evict_db + turn) % self.dbs.len();
896            // An empty database has nothing to move and opening a log for one
897            // would cost a resident page window to find that out.
898            let gave = if !self.dbs[i].is_empty() && self.migrates(i) {
899                self.attach_store(i);
900                // Whether it made room and not whether it moved a key. A round
901                // that demoted nothing and handed back a segment is a round
902                // that made room, and reading only the count refuses the write
903                // that provoked it.
904                self.dbs[i]
905                    .relieve(over)
906                    .is_ok_and(yo_kv::tier::Relief::made_room)
907            } else {
908                self.dbs[i].evict_one()
909            };
910            if gave {
911                self.evict_db = (i + 1) % self.dbs.len();
912                self.dirty |= 1u64 << i;
913                return true;
914            }
915        }
916        false
917    }
918
919    /// The sweep the shard loop calls, at most once a millisecond.
920    ///
921    /// The gate is the whole difference between this and [`Server::expire_step`].
922    /// A maintenance slice runs on every turn of the loop and a turn is a
923    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
924    /// thousand times per millisecond and spend a real share of the shard on
925    /// looking for keys that cannot have died since the last look. Nothing in a
926    /// database changes fast enough to be worth asking about more often than the
927    /// clock can tell the difference, and the clock here is milliseconds.
928    ///
929    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
930    /// hertz, so this is not the thing that decides how promptly memory comes
931    /// back. What it decides is that an idle server sweeps a thousand times a
932    /// second rather than a million.
933    pub fn expire_slice(&mut self, budget: usize) -> usize {
934        let now = self.clock.now_ms();
935        if now == self.expire_ms {
936            return 0;
937        }
938        self.expire_ms = now;
939        self.expire_step(budget)
940    }
941
942    /// Sweep dead keys out of the databases, spending at most `budget` looks.
943    ///
944    /// Answers what it spent, so the caller can charge its maintenance slice for
945    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
946    ///
947    /// Round robin from its own cursor, and every database gets offered whatever
948    /// is left of the budget rather than a sixteenth of it each, so a server on
949    /// database zero only, which is nearly every server, spends the whole slice
950    /// where the keys are. The fifteen empty ones cost a comparison apiece
951    /// because a database with no key carrying a deadline says so without
952    /// drawing anything.
953    ///
954    /// The cursor moves to the database after whichever one did the work, so two
955    /// busy databases take turns instead of the lower numbered one starving the
956    /// other.
957    pub fn expire_step(&mut self, budget: usize) -> usize {
958        let mut spent = 0;
959        for turn in 0..self.dbs.len() {
960            if spent >= budget {
961                break;
962            }
963            let i = (self.expire_db + turn) % self.dbs.len();
964            let c = self.dbs[i].expire_cycle(budget - spent);
965            spent += c.examined;
966            if c.expired > 0 {
967                self.expire_db = (i + 1) % self.dbs.len();
968                self.dirty |= 1u64 << i;
969            }
970        }
971        spent
972    }
973
974    /// One slice of compaction for a server that is over its limit.
975    ///
976    /// Takes the databases in the same order [`Server::compact_step`] does and
977    /// stops at the first one that had something to move, and it asks with the
978    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
979    fn compact_hard_step(&mut self) -> Option<usize> {
980        for turn in 0..self.dbs.len() {
981            let i = (self.next_db + turn) % self.dbs.len();
982            if let Some(moved) = self.dbs[i].compact_hard() {
983                self.next_db = (i + 1) % self.dbs.len();
984                return Some(moved);
985            }
986        }
987        None
988    }
989
990    /// Give one database's dead space back, if any database has enough of it to
991    /// be worth the move. `None` when no database had a candidate.
992    ///
993    /// Once per batch, next to the clock. Overwriting a key writes a new record
994    /// and counts the old one dead, so without this a server holds everything
995    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
996    /// a key against Redis at 144 for the same load, and the whole difference
997    /// was dead records nothing ever came back for.
998    ///
999    /// At most one segment moves per call and the search starts one database
1000    /// further along each time, so the cost of asking is a comparison per
1001    /// database and the cost of acting is bounded by a segment.
1002    pub fn compact_step(&mut self) -> Option<usize> {
1003        for turn in 0..self.dbs.len() {
1004            let i = (self.next_db + turn) % self.dbs.len();
1005            // Nothing has run against this database since it last said it had
1006            // nothing to collect, so it still has nothing to collect and the
1007            // line it lives on stays where it is.
1008            if self.dirty & (1 << i) == 0 {
1009                continue;
1010            }
1011            if let Some(moved) = self.dbs[i].compact_step() {
1012                self.next_db = (i + 1) % self.dbs.len();
1013                return Some(moved);
1014            }
1015            self.dirty &= !(1u64 << i);
1016        }
1017        None
1018    }
1019}
1020
1021impl Default for Server {
1022    fn default() -> Server {
1023        Server::new()
1024    }
1025}
1026
1027/// What one connection has chosen.
1028pub struct Session {
1029    db: usize,
1030    id: u64,
1031    name: Vec<u8>,
1032    /// The `HIMPORT` fieldsets this connection has prepared.
1033    ///
1034    /// Connection state and not keyspace state, which is the reference's design
1035    /// and not a shortcut: a fieldset is invisible to every other connection and
1036    /// the keys built from one outlive it.
1037    sets: himport::Fieldsets,
1038}
1039
1040impl Session {
1041    /// A new connection, on database zero with no name.
1042    #[must_use]
1043    pub fn new(id: u64) -> Session {
1044        Session {
1045            db: 0,
1046            id,
1047            name: Vec::new(),
1048            sets: himport::Fieldsets::default(),
1049        }
1050    }
1051
1052    /// The connection id, which `HELLO` reports and `CLIENT` will.
1053    #[must_use]
1054    pub const fn id(&self) -> u64 {
1055        self.id
1056    }
1057
1058    /// Which database this connection is working in.
1059    #[must_use]
1060    pub const fn db(&self) -> usize {
1061        self.db
1062    }
1063
1064    /// The name the client gave itself, empty if it gave none.
1065    #[must_use]
1066    pub fn name(&self) -> &[u8] {
1067        &self.name
1068    }
1069
1070    /// Put everything back the way it was when the connection was opened.
1071    ///
1072    /// The protocol is not here because it is not here: it lives in the reply
1073    /// buffer, and `RESET` sets it back there.
1074    pub fn reset(&mut self) {
1075        self.db = 0;
1076        self.name.clear();
1077        // `SELECT` leaves these alone and `RESET` does not, both checked
1078        // against 8.10.1, which is the one pair of answers you could not guess
1079        // from what the command is for.
1080        self.sets.clear();
1081    }
1082
1083    /// Record the name from `HELLO ... SETNAME`.
1084    fn set_name(&mut self, name: &[u8]) {
1085        yo_alloc::allow(|| {
1086            self.name.clear();
1087            self.name.extend_from_slice(name);
1088        });
1089    }
1090}
1091
1092/// Run one command and write its reply.
1093///
1094/// The name is looked up and the arity is checked here, once, so that no body
1095/// has to. Everything after that is the command's own.
1096pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
1097    // The decoder never produces a command with no name. If one ever arrives,
1098    // it is not something to answer.
1099    if args.is_empty() {
1100        return Flow::Continue;
1101    }
1102    resolved(server, session, lookup(args.name()), args, out)
1103}
1104
1105/// The same, for a caller that has already found the command.
1106///
1107/// The engine frames a command before it runs it, and between those two it also
1108/// asks which key the command touches so the record can be prefetched. That is
1109/// two more chances to look the name up, and looking it up three times to run it
1110/// once is three times the cost of the cheapest thing in the path. So the engine
1111/// resolves the name where it frames the command, carries the answer on the
1112/// framed command, and both the other two take it from there.
1113///
1114/// `spec` is `None` for a name that is not a command, which is the same thing
1115/// [`lookup`] says and lands in the same reply.
1116pub fn resolved(
1117    server: &mut Server,
1118    session: &mut Session,
1119    spec: Option<&'static Spec>,
1120    args: Args<'_>,
1121    out: &mut Out,
1122) -> Flow {
1123    if args.is_empty() {
1124        return Flow::Continue;
1125    }
1126    server.stats.commands += 1;
1127
1128    let Some(spec) = spec else {
1129        write_error(out, &args::unknown_command(args));
1130        return Flow::Continue;
1131    };
1132    if !arity_ok(spec, args.len()) {
1133        server.cmdstats.at(spec).rejected += 1;
1134        write_error(out, &args::wrong_arity(spec.name));
1135        return Flow::Continue;
1136    }
1137
1138    // The limit first, so a server with no `maxmemory`, which is the default and
1139    // is nearly all of them, pays one comparison against a field that is already
1140    // warm. Every command and not only the writes, because that is where Redis
1141    // puts it: making room is the server's job whatever the client asked for,
1142    // and the flag only decides who gets told no when there is no room to make.
1143    //
1144    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1145    // Redis's list, so a command that only frees is let through with nothing
1146    // left, which is what lets a client dig itself out with `DEL`.
1147    if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1148        server.cmdstats.at(spec).rejected += 1;
1149        out.error_line(b"OOM ", OOM);
1150        return Flow::Continue;
1151    }
1152
1153    // Which databases the maintenance turn after this batch has to ask. Marked
1154    // for every command and not only for the writes, because a read can make
1155    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1156    // record it dropped is exactly the kind of thing the collector is for.
1157    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1158    // two groups that hold them mark all of them rather than the session's.
1159    server.dirty |= match spec.group {
1160        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1161        | "array" | "stream" | "bloom" | "cuckoo" | "cms" | "topk" | "tdigest" => {
1162            1u64 << session.db
1163        }
1164        _ => ALL_DATABASES,
1165    };
1166
1167    let mark = out.len();
1168    // Before the group, because the five that block are list commands and would
1169    // otherwise land in `lists`, which is handed one database and nothing that
1170    // could park a client. The flag is the right thing to branch on rather than
1171    // a list of names: it is what `COMMAND INFO` reports about exactly these
1172    // commands, and the sorted set and stream ones that arrive later carry it
1173    // too.
1174    let done = if spec.flags.contains(&"blocking") {
1175        blocking::execute(server, session, spec, args, out)
1176    } else {
1177        match spec.group {
1178            "string" => {
1179                let db = session.db;
1180                strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1181            }
1182            // Its own group and its own file, and the same values underneath:
1183            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1184            // something a `SET` left behind works.
1185            "bitmap" => {
1186                let db = session.db;
1187                bits::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1188            }
1189            // The same again: a sketch is a string with a documented layout, so
1190            // `GET` hands one to a client and `SET` takes it back.
1191            "hyperloglog" => {
1192                let db = session.db;
1193                hll::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1194            }
1195            "set" => {
1196                let db = session.db;
1197                sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1198            }
1199            // The one hash command whose state is not in the keyspace. A
1200            // fieldset belongs to the connection, so this is handed the session
1201            // as well as the database, the same exception `MIGRATE` gets in the
1202            // keyspace group for the socket it keeps.
1203            "hash" if spec.name == "himport" => {
1204                let db = session.db;
1205                himport::execute(&mut server.dbs[db], &mut session.sets, args, out)
1206                    .map(|()| Flow::Continue)
1207            }
1208            "hash" => {
1209                let db = session.db;
1210                hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1211            }
1212            "list" => {
1213                let db = session.db;
1214                lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1215            }
1216            "zset" => {
1217                let db = session.db;
1218                zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1219            }
1220            // A geo key is a sorted set and these are sorted set commands with
1221            // arithmetic on the way in and on the way out, so a client can ZREM
1222            // a place out of one and ZCARD it to count them.
1223            "geo" => {
1224                let db = session.db;
1225                geo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1226            }
1227            "array" => {
1228                let db = session.db;
1229                arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1230            }
1231            "graph" => {
1232                let db = session.db;
1233                graph::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1234            }
1235            // A document under a key, reached by a path. The group is Redis's
1236            // module surface and the storage is ours, the same trade the vector
1237            // set group makes.
1238            "json" => {
1239                let db = session.db;
1240                json::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1241            }
1242            "vector" => {
1243                let db = session.db;
1244                vectors::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1245            }
1246            "bloom" => {
1247                let db = session.db;
1248                bloom::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1249            }
1250            "cuckoo" => {
1251                let db = session.db;
1252                cuckoo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1253            }
1254            "cms" => {
1255                let db = session.db;
1256                cms::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1257            }
1258            "topk" => {
1259                let db = session.db;
1260                topk::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1261            }
1262            "tdigest" => {
1263                let db = session.db;
1264                tdigest::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1265            }
1266            // The clock is read before the database is borrowed, because every
1267            // stream command needs the time and it lives on the server. An
1268            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1269            // `XINFO` reporting it all have to agree about what moment this is.
1270            "stream" => {
1271                let db = session.db;
1272                let now = server.now_ms();
1273                streams::execute(&mut server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1274            }
1275            // The one keyspace command that needs more than the databases,
1276            // because the socket it talks down is held on the server between
1277            // commands and not opened again for each one.
1278            "keyspace" if spec.name == "migrate" => {
1279                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1280            }
1281            // Every database and not the one the session is on, because `COPY` takes
1282            // a `DB n` and writes into a database nobody selected.
1283            "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
1284                .map(|()| Flow::Continue),
1285            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1286            _ => server::execute(server, session, spec, args, out),
1287        }
1288    };
1289    let flow = match done {
1290        Ok(flow) => flow,
1291        Err(e) => {
1292            out.truncate(mark);
1293            write_error(out, &e);
1294            Flow::Continue
1295        }
1296    };
1297
1298    // Counted here and not before the call, which is where Redis counts it, so
1299    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1300    // same way theirs does.
1301    //
1302    // Failure is read off the reply rather than off the `Result`, because the
1303    // two are not the same set. A command that ran out of arguments comes back
1304    // as an `Err` and a command that was sent the wrong password writes its own
1305    // error line and comes back `Ok`, and both of those are a call that failed.
1306    // The first byte at the mark is what a client would branch on, and it is `-`
1307    // for an error on either protocol and `!` for RESP3's long form.
1308    let row = server.cmdstats.at(spec);
1309    row.calls += 1;
1310    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1311        row.failed += 1;
1312    }
1313    flow
1314}
1315
1316/// The error line for an error value.
1317///
1318/// The prefix is what a client branches on, and there are three of them:
1319/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1320/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1321/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1322/// than routed through here. `OOM` is not a [`Code`] of its own because
1323/// [`Code::Full`] already covers the string that is too long for
1324/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1325fn write_error(out: &mut Out, e: &Error) {
1326    let prefix: &[u8] = match e.code() {
1327        Code::WrongType => b"WRONGTYPE ",
1328        // Only the HyperLogLog commands answer this one, and the prefix is the
1329        // sentence a client branches on to tell a sketch it cannot read from a
1330        // sketch it sent wrong.
1331        Code::Corrupt => b"INVALIDOBJ ",
1332        _ => b"ERR ",
1333    };
1334    out.error_line(prefix, e.message().as_bytes());
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339    use super::*;
1340    use crate::proto::{Limits, Proto};
1341    use crate::request::Argv;
1342
1343    /// Build the wire bytes for a command.
1344    ///
1345    /// Tests go through the codec rather than around it, so an argument in a
1346    /// test is the same borrowed slice a connection produces.
1347    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1348        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1349        for p in parts {
1350            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1351            wire.extend_from_slice(p);
1352            wire.extend_from_slice(b"\r\n");
1353        }
1354        wire
1355    }
1356
1357    /// A server, a connection and a buffer, driven the way the reactor will.
1358    struct Fixture {
1359        server: Server,
1360        session: Session,
1361        argv: Argv,
1362        out: Out,
1363    }
1364
1365    impl Fixture {
1366        fn new() -> Fixture {
1367            Fixture {
1368                server: Server::new(),
1369                session: Session::new(7),
1370                argv: Argv::new(),
1371                out: Out::new(Proto::Resp2),
1372            }
1373        }
1374
1375        /// Run one command and answer with the bytes it wrote.
1376        fn run(&mut self, parts: &[&[u8]]) -> String {
1377            self.flow(parts).1
1378        }
1379
1380        /// Run one command and answer with the bytes exactly as written.
1381        ///
1382        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
1383        /// every reply that is text and destroys a `DUMP` payload, since a
1384        /// payload is arbitrary bytes and a checksum on the end of them.
1385        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1386            let wire = encode(parts);
1387            self.argv.decode(&wire, &Limits::default()).unwrap();
1388            self.out.clear();
1389            execute(
1390                &mut self.server,
1391                &mut self.session,
1392                Args::new(&self.argv, &wire),
1393                &mut self.out,
1394            );
1395            self.out.as_slice().to_vec()
1396        }
1397
1398        /// Move every clock in the server on by `ms`.
1399        fn advance(&mut self, ms: u64) {
1400            for db in 0..DATABASES {
1401                self.server.db(db).clock_mut().advance(ms);
1402            }
1403        }
1404
1405        /// The same, with what the connection should do next.
1406        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1407            let wire = encode(parts);
1408            self.argv.decode(&wire, &Limits::default()).unwrap();
1409            self.out.clear();
1410            let flow = execute(
1411                &mut self.server,
1412                &mut self.session,
1413                Args::new(&self.argv, &wire),
1414                &mut self.out,
1415            );
1416            (
1417                flow,
1418                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1419            )
1420        }
1421    }
1422
1423    /// What a client does all day: write the same keys again and again. Every
1424    /// one of those writes leaves the previous record behind, so a server that
1425    /// never compacts holds every version of every key it has ever been sent.
1426    #[test]
1427    fn rewriting_the_same_keys_does_not_grow_the_server() {
1428        let mut f = Fixture::new();
1429        let val = vec![b'v'; 1024];
1430        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1431
1432        for k in &keys {
1433            f.run(&[b"SET", k, &val]);
1434        }
1435        f.server.compact_step();
1436        let after_first = f.server.memory_bytes();
1437
1438        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
1439        // of it. Thirty two megabytes written to hold sixty four kilobytes,
1440        // which is the shape of a real workload and is enough churn to fill
1441        // sixteen segments if nothing ever comes back.
1442        for _ in 0..500 {
1443            for k in &keys {
1444                f.run(&[b"SET", k, &val]);
1445            }
1446            f.server.compact_step();
1447        }
1448
1449        assert!(
1450            f.server.memory_bytes() <= after_first * 2,
1451            "held {} after five hundred passes against {after_first} after one",
1452            f.server.memory_bytes()
1453        );
1454        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1455        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1456    }
1457
1458    /// The same churn on a database nobody starts on, either side of a quiet
1459    /// spell long enough for the maintenance turn to stop asking about it.
1460    ///
1461    /// The turn after each batch skips a database that has already said it has
1462    /// nothing to collect and has not been touched since, which is what keeps a
1463    /// server whose clients are all on database zero from loading and storing
1464    /// in the other fifteen every batch to be told no. Two things could go
1465    /// wrong with that. A database might never be marked at all, so this uses
1466    /// database nine, which nothing marks by accident. And a database whose
1467    /// mark was cleared might never get it back, so this drains the collector
1468    /// until it says there is nothing left, checks the mark really is gone, and
1469    /// then writes another thirty two megabytes through the same sixty four
1470    /// keys. If either went wrong the server would hold all of it.
1471    #[test]
1472    fn a_database_nobody_started_on_is_still_collected() {
1473        let mut f = Fixture::new();
1474        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1475        let val = vec![b'v'; 1024];
1476        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1477
1478        for k in &keys {
1479            f.run(&[b"SET", k, &val]);
1480        }
1481        while f.server.compact_step().is_some() {}
1482        assert_eq!(
1483            f.server.dirty & (1 << 9),
1484            0,
1485            "database nine was drained and should not be asked again until it is written to"
1486        );
1487        let after_first = f.server.memory_bytes();
1488
1489        for _ in 0..500 {
1490            for k in &keys {
1491                f.run(&[b"SET", k, &val]);
1492            }
1493            f.server.compact_step();
1494        }
1495
1496        assert!(
1497            f.server.memory_bytes() <= after_first * 2,
1498            "held {} after five hundred passes against {after_first} after one",
1499            f.server.memory_bytes()
1500        );
1501        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1502        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1503        // And nothing landed anywhere else on the way.
1504        f.run(&[b"SELECT", b"0"]);
1505        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1506    }
1507
1508    #[test]
1509    fn a_command_goes_from_bytes_to_bytes() {
1510        let mut f = Fixture::new();
1511        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1512        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1513        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1514        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1515        // The name is matched whatever case it came in, and so are the options.
1516        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1517        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1518    }
1519
1520    #[test]
1521    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1522        let mut f = Fixture::new();
1523        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1524        // A key named twice exists twice and can only be deleted once, and both
1525        // of those are Redis's answers rather than tidier ones.
1526        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1527        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1528        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1529        // UNLINK is the same body and reports the same way.
1530        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1531        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1532    }
1533
1534    #[test]
1535    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1536        let mut f = Fixture::new();
1537        f.run(&[b"SET", b"k", b"v"]);
1538        // A simple string on both protocols, which is unusual: most replies
1539        // that carry a word are bulk strings.
1540        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1541        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1542    }
1543
1544    #[test]
1545    fn touch_counts_the_way_exists_counts() {
1546        let mut f = Fixture::new();
1547        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1548        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1549        assert_eq!(
1550            f.run(&[b"TOUCH", b"a", b"a"]),
1551            ":2\r\n",
1552            "twice counts twice"
1553        );
1554        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1555        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1556    }
1557
1558    #[test]
1559    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1560        let mut f = Fixture::new();
1561        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1562        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1563
1564        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1565        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1566        assert_eq!(
1567            f.run(&[b"TTL", b"b"]),
1568            ":100\r\n",
1569            "the source's and not b's"
1570        );
1571        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1572    }
1573
1574    #[test]
1575    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1576        let mut f = Fixture::new();
1577        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1578        // The source is checked before the destination, so this is the error
1579        // and not the zero RENAMENX would otherwise answer for a taken name.
1580        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1581    }
1582
1583    #[test]
1584    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1585        let mut f = Fixture::new();
1586        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1587
1588        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1589        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1590        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
1591        // one call the two disagree about and neither does any work for.
1592        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1593        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1594        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1595        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1596    }
1597
1598    #[test]
1599    fn renaming_a_set_does_not_touch_a_member() {
1600        let mut f = Fixture::new();
1601        for i in 0..300 {
1602            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1603        }
1604        let before = f.server.memory_bytes();
1605
1606        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1607        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1608        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1609        assert!(
1610            f.server.memory_bytes().abs_diff(before) < 256,
1611            "the members were copied: {} against {before}",
1612            f.server.memory_bytes()
1613        );
1614    }
1615
1616    #[test]
1617    fn a_copy_is_a_second_value_and_not_a_second_name() {
1618        let mut f = Fixture::new();
1619        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1620
1621        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1622        f.run(&[b"SADD", b"t", b"m3"]);
1623        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1624        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1625    }
1626
1627    /// Every type a key can hold, copied, because two of them used to panic.
1628    ///
1629    /// `COPY` reads the value out of the source through one match on the type
1630    /// tag, and that match had a catch all at the bottom from back when a set
1631    /// and a hash were the only bodies. The list and the sorted set landed after
1632    /// it and nobody came back, so `COPY mylist other` took the shard down. It
1633    /// is an ordinary command against a type the server supports everywhere
1634    /// else, so this walks all five rather than the two that were broken: the
1635    /// point is that the next type cannot land the same way.
1636    #[test]
1637    fn every_type_can_be_copied() {
1638        let mut f = Fixture::new();
1639        f.run(&[b"SET", b"str", b"v1"]);
1640        f.run(&[b"SADD", b"set", b"m1"]);
1641        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1642        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1643        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1644
1645        for name in [
1646            &b"str"[..],
1647            &b"set"[..],
1648            &b"hash"[..],
1649            &b"list"[..],
1650            &b"zset"[..],
1651        ] {
1652            let dst = [name, b":copy"].concat();
1653            assert_eq!(
1654                f.run(&[b"COPY", name, &dst]),
1655                ":1\r\n",
1656                "copying {}",
1657                String::from_utf8_lossy(name)
1658            );
1659            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1660        }
1661
1662        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1663            let mut want = String::from("*2\r\n");
1664            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1665            want
1666        });
1667        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1668
1669        // And the copy is its own value, not a second name for the source.
1670        f.run(&[b"RPUSH", b"list:copy", b"c"]);
1671        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1672        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1673    }
1674
1675    #[test]
1676    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1677        let mut f = Fixture::new();
1678        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1679        f.run(&[b"SET", b"b", b"v2"]);
1680
1681        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1682        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1683        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1684        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1685        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1686        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1687    }
1688
1689    #[test]
1690    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1691        let mut f = Fixture::new();
1692        f.run(&[b"SET", b"a", b"v1"]);
1693
1694        // Same key, different database, so this is not the same object and is
1695        // an ordinary copy. Same key in the same database is the error below.
1696        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1697        f.run(&[b"SELECT", b"1"]);
1698        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1699        assert_eq!(
1700            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1701            ":0\r\n",
1702            "taken"
1703        );
1704        assert_eq!(
1705            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1706            ":1\r\n"
1707        );
1708    }
1709
1710    #[test]
1711    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1712        let mut f = Fixture::new();
1713        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1714        assert_eq!(
1715            f.run(&[b"SORT", b"l"]),
1716            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1717        );
1718        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
1719        assert_eq!(
1720            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1721            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1722        );
1723        assert_eq!(
1724            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1725            "*1\r\n$1\r\n2\r\n"
1726        );
1727    }
1728
1729    #[test]
1730    fn sort_reads_a_key_per_element_for_by_and_for_get() {
1731        let mut f = Fixture::new();
1732        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1733        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1734        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
1735        // misses, which is a nil in the middle of the array and not a short one.
1736        assert_eq!(
1737            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1738            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1739        );
1740    }
1741
1742    #[test]
1743    fn sort_store_writes_a_list_and_answers_its_length() {
1744        let mut f = Fixture::new();
1745        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1746        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1747        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1748        assert_eq!(
1749            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1750            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1751        );
1752        // An empty result takes the destination with it rather than leaving a
1753        // list that holds nothing.
1754        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1755        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1756    }
1757
1758    #[test]
1759    fn sort_ro_does_not_know_the_word_store() {
1760        let mut f = Fixture::new();
1761        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1762        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1763        assert_eq!(
1764            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1765            "-ERR syntax error\r\n"
1766        );
1767        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1768    }
1769
1770    #[test]
1771    fn sort_refuses_what_it_cannot_sort() {
1772        let mut f = Fixture::new();
1773        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1774        f.run(&[b"SET", b"s", b"x"]);
1775        assert_eq!(
1776            f.run(&[b"SORT", b"s"]),
1777            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1778        );
1779        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1780        assert_eq!(
1781            f.run(&[b"SORT", b"words"]),
1782            "-ERR One or more scores can't be converted into double\r\n"
1783        );
1784        assert_eq!(
1785            f.run(&[b"SORT", b"words", b"ALPHA"]),
1786            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1787        );
1788        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1789    }
1790
1791    #[test]
1792    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1793        let mut f = Fixture::new();
1794        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1795        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1796        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1797        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1798        assert_eq!(
1799            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1800            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1801        );
1802        // And back, which proves the body survived the trip rather than being
1803        // rebuilt from a copy that happened to look the same.
1804        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1805        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1806    }
1807
1808    #[test]
1809    fn move_answers_zero_when_either_end_says_no() {
1810        let mut f = Fixture::new();
1811        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1812        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1813        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1814        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1815        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1816        // The destination is taken, so nothing moves and the source is still
1817        // there with what it had.
1818        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1819        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1820        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1821        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1822    }
1823
1824    #[test]
1825    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1826        let mut f = Fixture::new();
1827        assert_eq!(
1828            f.run(&[b"MOVE", b"a", b"0"]),
1829            "-ERR source and destination objects are the same\r\n"
1830        );
1831        assert_eq!(
1832            f.run(&[b"MOVE", b"a", b"99"]),
1833            "-ERR DB index is out of range\r\n"
1834        );
1835        assert_eq!(
1836            f.run(&[b"MOVE", b"a", b"-1"]),
1837            "-ERR DB index is out of range\r\n"
1838        );
1839        assert_eq!(
1840            f.run(&[b"MOVE", b"a", b"x"]),
1841            "-ERR value is not an integer or out of range\r\n"
1842        );
1843    }
1844
1845    #[test]
1846    fn swapdb_swaps_what_two_connections_would_see() {
1847        let mut f = Fixture::new();
1848        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1849        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1850        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1851        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1852
1853        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1854        // Still on database zero, and database zero is a different database.
1855        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1856        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1857        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1858        // A database swapped with itself is fine and changes nothing.
1859        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1860        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1861    }
1862
1863    #[test]
1864    fn swapdb_says_which_index_it_could_not_read() {
1865        let mut f = Fixture::new();
1866        assert_eq!(
1867            f.run(&[b"SWAPDB", b"x", b"1"]),
1868            "-ERR invalid first DB index\r\n"
1869        );
1870        assert_eq!(
1871            f.run(&[b"SWAPDB", b"0", b"y"]),
1872            "-ERR invalid second DB index\r\n"
1873        );
1874        // A number too big to be an index on a server that keeps one in an int
1875        // is the same complaint, and a plausible one that is not ours is the
1876        // range complaint instead. The split is Redis's.
1877        assert_eq!(
1878            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
1879            "-ERR invalid first DB index\r\n"
1880        );
1881        assert_eq!(
1882            f.run(&[b"SWAPDB", b"0", b"99"]),
1883            "-ERR DB index is out of range\r\n"
1884        );
1885        assert_eq!(
1886            f.run(&[b"SWAPDB", b"-1", b"0"]),
1887            "-ERR DB index is out of range\r\n"
1888        );
1889    }
1890
1891    #[test]
1892    fn wait_answers_zero_replicas_without_waiting() {
1893        let mut f = Fixture::new();
1894        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
1895        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
1896        // A replica that is never going to arrive, and a timeout that would be
1897        // a real wait on a server that had one.
1898        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
1899        // Negative replicas is not an error, because zero is already more than
1900        // it asked for.
1901        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
1902        assert_eq!(
1903            f.run(&[b"WAIT", b"x", b"0"]),
1904            "-ERR value is not an integer or out of range\r\n"
1905        );
1906        assert_eq!(
1907            f.run(&[b"WAIT", b"0", b"-1"]),
1908            "-ERR timeout is negative\r\n"
1909        );
1910        assert_eq!(
1911            f.run(&[b"WAIT", b"0", b"1.5"]),
1912            "-ERR timeout is not an integer or out of range\r\n"
1913        );
1914    }
1915
1916    #[test]
1917    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
1918        let mut f = Fixture::new();
1919        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
1920        assert_eq!(
1921            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
1922            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
1923        );
1924        assert_eq!(
1925            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
1926            "-ERR value is out of range, value must between 0 and 1\r\n"
1927        );
1928        assert_eq!(
1929            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
1930            "-ERR value is out of range, must be positive\r\n"
1931        );
1932        // The arguments are all read before the server looks at itself, so a
1933        // bad timeout beats the append only complaint even with numlocal set.
1934        assert_eq!(
1935            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
1936            "-ERR timeout is negative\r\n"
1937        );
1938    }
1939
1940    /// The bytes inside a bulk reply, with the header and the trailing break
1941    /// taken off. Every `DUMP` test needs this and none of them care how the
1942    /// length was written.
1943    fn payload(reply: &[u8]) -> Vec<u8> {
1944        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
1945        reply[head + 2..reply.len() - 2].to_vec()
1946    }
1947
1948    #[test]
1949    fn a_value_survives_a_dump_and_a_restore() {
1950        let mut f = Fixture::new();
1951        f.run(&[b"SET", b"s", b"hello"]);
1952        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
1953        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
1954        f.run(&[b"SADD", b"u", b"x", b"y"]);
1955        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
1956        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
1957
1958        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
1959            let mut copy = key.to_vec();
1960            copy.push(b'2');
1961            let bytes = payload(&f.raw(&[b"DUMP", key]));
1962            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
1963            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
1964        }
1965
1966        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
1967        assert_eq!(
1968            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
1969            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
1970        );
1971        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
1972        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
1973        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
1974        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
1975        // The encoding survives too, since the payload names the plainest legal
1976        // type and the loader puts the value back on the rung it belongs on.
1977        assert_eq!(
1978            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
1979            f.run(&[b"OBJECT", b"ENCODING", b"t"])
1980        );
1981    }
1982
1983    #[test]
1984    fn a_dumped_hash_keeps_its_field_deadlines() {
1985        let mut f = Fixture::new();
1986        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
1987        assert_eq!(
1988            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
1989            "*1\r\n:1\r\n"
1990        );
1991        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
1992        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
1993        assert_eq!(
1994            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
1995            "*2\r\n:-1\r\n:100\r\n"
1996        );
1997    }
1998
1999    #[test]
2000    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
2001        let mut f = Fixture::new();
2002        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
2003        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2004        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
2005        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
2006        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
2007        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
2008        // An absolute deadline that has already gone is not an error. The key is
2009        // not created and the reply is the same OK a live one gets.
2010        assert_eq!(
2011            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
2012            "+OK\r\n"
2013        );
2014        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
2015    }
2016
2017    #[test]
2018    fn dump_answers_nothing_for_a_key_that_is_not_there() {
2019        let mut f = Fixture::new();
2020        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
2021        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
2022        f.advance(50);
2023        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
2024    }
2025
2026    #[test]
2027    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
2028        let mut f = Fixture::new();
2029        f.run(&[b"SET", b"a", b"first"]);
2030        f.run(&[b"SET", b"b", b"second"]);
2031        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
2032        assert_eq!(
2033            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
2034            "-BUSYKEY Target key name already exists.\r\n"
2035        );
2036        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
2037        assert_eq!(
2038            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
2039            "+OK\r\n"
2040        );
2041        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
2042    }
2043
2044    /// The busy key comes before the payload, which is not the order the
2045    /// arguments read in. Whether a key is taken should not depend on whether
2046    /// the bytes behind it happened to be good.
2047    #[test]
2048    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
2049        let mut f = Fixture::new();
2050        f.run(&[b"SET", b"a", b"v"]);
2051        assert_eq!(
2052            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
2053            "-BUSYKEY Target key name already exists.\r\n"
2054        );
2055        // And the options come before even that, so a bad FREQ beats the busy
2056        // key the same way a bad DB beats a missing source in COPY.
2057        assert_eq!(
2058            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
2059            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2060        );
2061    }
2062
2063    #[test]
2064    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
2065        let mut f = Fixture::new();
2066        f.run(&[b"SET", b"a", b"hello"]);
2067        let good = payload(&f.raw(&[b"DUMP", b"a"]));
2068
2069        let mut flipped = good.clone();
2070        flipped[2] ^= 0x40;
2071        assert_eq!(
2072            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
2073            "-ERR DUMP payload version or checksum are wrong\r\n"
2074        );
2075        assert_eq!(
2076            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
2077            "-ERR DUMP payload version or checksum are wrong\r\n"
2078        );
2079        // A footer that is right over a body that is not. The type byte says
2080        // string and there is nothing behind it, so the checksum agrees and the
2081        // value does not exist.
2082        let mut truncated = good[..1].to_vec();
2083        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
2084        let crc = yo_common::crc::crc64(0, &truncated);
2085        truncated.extend_from_slice(&crc.to_le_bytes());
2086        assert_eq!(
2087            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
2088            "-ERR Bad data format\r\n"
2089        );
2090        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
2091    }
2092
2093    #[test]
2094    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
2095        let mut f = Fixture::new();
2096        f.run(&[b"SET", b"a", b"v"]);
2097        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2098        assert_eq!(
2099            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
2100            "-ERR Invalid TTL value, must be >= 0\r\n"
2101        );
2102        assert_eq!(
2103            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
2104            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
2105        );
2106        assert_eq!(
2107            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
2108            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
2109        );
2110        // Both are accepted and both are then dropped, which is D-26.
2111        assert_eq!(
2112            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
2113            "+OK\r\n"
2114        );
2115        assert_eq!(
2116            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
2117            "+OK\r\n"
2118        );
2119    }
2120
2121    /// Neither word is refused for being the wrong one. Each is only accepted
2122    /// while the other is unset, so the second of the two falls through to the
2123    /// plain syntax error rather than getting a message of its own.
2124    #[test]
2125    fn restore_takes_idletime_or_freq_and_not_both() {
2126        let mut f = Fixture::new();
2127        f.run(&[b"SET", b"a", b"v"]);
2128        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
2129        assert_eq!(
2130            f.run(&[
2131                b"RESTORE",
2132                b"b",
2133                b"0",
2134                &bytes,
2135                b"IDLETIME",
2136                b"1",
2137                b"FREQ",
2138                b"2"
2139            ]),
2140            "-ERR syntax error\r\n"
2141        );
2142        assert_eq!(
2143            f.run(&[
2144                b"RESTORE",
2145                b"b",
2146                b"0",
2147                &bytes,
2148                b"FREQ",
2149                b"2",
2150                b"IDLETIME",
2151                b"1"
2152            ]),
2153            "-ERR syntax error\r\n"
2154        );
2155        assert_eq!(
2156            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
2157            "-ERR syntax error\r\n"
2158        );
2159        assert_eq!(
2160            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
2161            "-ERR syntax error\r\n"
2162        );
2163    }
2164
2165    #[test]
2166    fn copy_checks_its_options_before_it_looks_for_anything() {
2167        let mut f = Fixture::new();
2168        // No key exists at all, and every one of these is still the option
2169        // complaint rather than a zero, which is the order a real server uses.
2170        assert_eq!(
2171            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2172            "-ERR DB index is out of range\r\n"
2173        );
2174        assert_eq!(
2175            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2176            "-ERR DB index is out of range\r\n"
2177        );
2178        assert_eq!(
2179            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2180            "-ERR value is not an integer or out of range\r\n"
2181        );
2182        assert_eq!(
2183            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2184            "-ERR syntax error\r\n"
2185        );
2186        assert_eq!(
2187            f.run(&[b"COPY", b"a", b"a"]),
2188            "-ERR source and destination objects are the same\r\n"
2189        );
2190        // Repeated, reordered and lowercased, and the last DB wins.
2191        assert_eq!(
2192            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2193            ":0\r\n"
2194        );
2195    }
2196
2197    #[test]
2198    fn time_is_two_bulk_strings_and_moves() {
2199        let mut f = Fixture::new();
2200        let first = f.run(&[b"TIME"]);
2201        assert!(first.starts_with("*2\r\n$"), "got {first}");
2202        let parts: Vec<&str> = first.split("\r\n").collect();
2203        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2204        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2205        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2206        assert!((0..1_000_000).contains(&micros), "got {micros}");
2207        // The coarse clock the keyspace uses is a cached millisecond that a
2208        // background tick refreshes, so a TIME built on it would answer the
2209        // same microsecond twice in a row here.
2210        assert_ne!(first, f.run(&[b"TIME"]));
2211    }
2212
2213    #[test]
2214    fn a_keyspace_scan_walks_every_key_once() {
2215        let mut f = Fixture::new();
2216        for i in 0..500 {
2217            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2218        }
2219
2220        let mut seen: Vec<String> = Vec::new();
2221        let mut cursor = "0".to_owned();
2222        let mut calls = 0;
2223        loop {
2224            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2225            seen.extend(keys);
2226            cursor = next;
2227            calls += 1;
2228            assert!(calls < 10_000, "the cursor is not advancing");
2229            if cursor == "0" {
2230                break;
2231            }
2232        }
2233
2234        seen.sort();
2235        seen.dedup();
2236        assert_eq!(seen.len(), 500, "every key once and only once");
2237        // And more than one call to get them, or the COUNT is being ignored and
2238        // the loop above proved nothing about resuming.
2239        assert!(calls > 1, "500 keys came back in one batch");
2240    }
2241
2242    #[test]
2243    fn a_scan_narrows_by_pattern_and_by_type() {
2244        let mut f = Fixture::new();
2245        f.run(&[b"SET", b"str", b"v"]);
2246        f.run(&[b"SADD", b"members", b"a"]);
2247        f.run(&[b"HSET", b"fields", b"f", b"v"]);
2248
2249        let all = |f: &mut Fixture, args: &[&[u8]]| {
2250            let mut out: Vec<String> = Vec::new();
2251            let mut cursor = "0".to_owned();
2252            loop {
2253                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2254                line.extend_from_slice(args);
2255                let (next, keys) = scan_reply(&f.run(&line));
2256                out.extend(keys);
2257                cursor = next;
2258                if cursor == "0" {
2259                    break;
2260                }
2261            }
2262            out.sort();
2263            out
2264        };
2265
2266        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2267        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2268        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2269        // Case insensitive, the same as Redis's own comparison.
2270        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2271        // A type nothing can hold is not an error, it just matches nothing.
2272        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2273        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2274        // Both filters at once, and they are an and rather than an or.
2275        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2276    }
2277
2278    #[test]
2279    fn a_scan_says_what_is_wrong_with_it() {
2280        let mut f = Fixture::new();
2281        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2282        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2283        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2284        assert_eq!(
2285            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2286            "-ERR syntax error\r\n"
2287        );
2288        assert_eq!(
2289            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2290            "-ERR value is not an integer or out of range\r\n"
2291        );
2292        assert_eq!(
2293            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2294            "-ERR syntax error\r\n"
2295        );
2296        // A cursor the client made up is a cursor. It resumes somewhere
2297        // arbitrary and answers whatever is there, which is what Redis does and
2298        // is the only behaviour that does not need the server to remember every
2299        // cursor it has handed out.
2300        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2301    }
2302
2303    #[test]
2304    fn keys_and_randomkey_look_at_the_whole_database() {
2305        let mut f = Fixture::new();
2306        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2307        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2308
2309        for name in ["one", "two", "three"] {
2310            f.run(&[b"SET", name.as_bytes(), b"v"]);
2311        }
2312        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2313        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2314        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2315
2316        for _ in 0..50 {
2317            let got = f.run(&[b"RANDOMKEY"]);
2318            assert!(
2319                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2320                "got {got}"
2321            );
2322        }
2323    }
2324
2325    #[test]
2326    fn a_walk_does_not_answer_keys_that_have_expired() {
2327        let mut f = Fixture::new();
2328        f.run(&[b"SET", b"alive", b"v"]);
2329        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2330        f.server.db(0).clock_mut().advance(2);
2331        assert_eq!(
2332            f.run(&[b"DBSIZE"]),
2333            ":2\r\n",
2334            "nothing has collected it yet"
2335        );
2336
2337        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2338        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2339        assert_eq!(keys, ["alive"]);
2340        for _ in 0..20 {
2341            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2342        }
2343        // The walk collected it on the way past, which is what makes DBSIZE
2344        // here answer what Redis answers once its own cycle has been round.
2345        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2346    }
2347
2348    #[test]
2349    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2350        let mut f = Fixture::new();
2351        f.run(&[b"SET", b"k", b"v"]);
2352        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2353        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2354
2355        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2356        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2357        let ms = int(&f.run(&[b"PTTL", b"k"]));
2358        assert!((99_000..=100_000).contains(&ms), "got {ms}");
2359
2360        // The absolute pair, derived from the same one number the store kept.
2361        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2362        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2363        assert_eq!(at, (at_ms + 500) / 1000);
2364        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2365
2366        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2367        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2368        assert_eq!(
2369            f.run(&[b"PERSIST", b"k"]),
2370            ":0\r\n",
2371            "nothing to take off the second time"
2372        );
2373        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2374        assert_eq!(
2375            f.run(&[b"GET", b"k"]),
2376            "$1\r\nv\r\n",
2377            "and the value went through all of that untouched"
2378        );
2379    }
2380
2381    #[test]
2382    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2383        let mut f = Fixture::new();
2384        f.run(&[b"SET", b"str", b"v"]);
2385        f.run(&[b"SADD", b"set", b"a", b"b"]);
2386        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2387
2388        for key in [b"str".as_slice(), b"set", b"hash"] {
2389            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2390            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2391        }
2392        // The body is not touched by any of that, which is the whole reason the
2393        // deadline lives in the record and the body lives somewhere else.
2394        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2395        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2396        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2397    }
2398
2399    #[test]
2400    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2401        let mut f = Fixture::new();
2402        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2403            f.run(&[b"SET", key, b"v"]);
2404        }
2405        // Four ways of naming a moment that has passed, and all four are a
2406        // delete answering 1 rather than an error. Zero is a moment, minus one
2407        // is a moment, and the hash field commands refuse the negative one.
2408        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2409        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2410        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2411        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2412        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2413        assert_eq!(
2414            f.run(&[b"EXPIRE", b"a", b"100"]),
2415            ":0\r\n",
2416            "and the key really went, so there is nothing to put a deadline on"
2417        );
2418    }
2419
2420    #[test]
2421    fn the_four_conditions_decide_whether_the_deadline_moves() {
2422        let mut f = Fixture::new();
2423        f.run(&[b"SET", b"k", b"v"]);
2424
2425        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2426        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2427        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2428        assert_eq!(
2429            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2430            ":1\r\n",
2431            "no deadline reads as infinitely far away, so LT passes where GT fails"
2432        );
2433
2434        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2435        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2436        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2437        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2438        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2439        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2440
2441        // The condition is answered before the past check, so this is a 0 and
2442        // the key survives. The other order would delete it.
2443        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2444        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2445        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2446        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2447    }
2448
2449    #[test]
2450    fn the_conditions_are_a_set_and_not_a_keyword() {
2451        let mut f = Fixture::new();
2452        f.run(&[b"SET", b"k", b"v"]);
2453
2454        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2455        assert_eq!(
2456            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2457            ":0\r\n",
2458            "the same keyword twice means it once, and NX now has a deadline to fail on"
2459        );
2460
2461        // XX with LT is the one pair that is not either of them on its own: LT
2462        // alone would accept a key with no deadline and this does not.
2463        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2464        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2465        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2466        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2467        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2468        f.run(&[b"PERSIST", b"k"]);
2469        assert_eq!(
2470            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2471            ":0\r\n",
2472            "where LT on its own would have taken it"
2473        );
2474        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2475    }
2476
2477    #[test]
2478    fn a_key_is_gone_once_its_moment_passes() {
2479        let mut f = Fixture::new();
2480        f.run(&[b"SET", b"k", b"v"]);
2481        f.run(&[b"EXPIRE", b"k", b"100"]);
2482
2483        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2484        f.server.set_clock_ms(at as u64 + 1);
2485        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2486        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2487        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2488        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2489    }
2490
2491    #[test]
2492    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2493        let mut f = Fixture::new();
2494        f.run(&[b"SET", b"k", b"v"]);
2495        for (bad, want) in [
2496            (
2497                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2498                "-ERR value is not an integer or out of range\r\n",
2499            ),
2500            (
2501                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2502                "-ERR Unsupported option MAYBE\r\n",
2503            ),
2504            (
2505                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2506                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2507            ),
2508            (
2509                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2510                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2511            ),
2512            (
2513                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2514                "-ERR GT and LT options at the same time are not compatible\r\n",
2515            ),
2516            // Seconds that overflow when multiplied into milliseconds. Every
2517            // message names the command it came from.
2518            (
2519                &[b"EXPIRE", b"k", b"9223372036854775807"],
2520                "-ERR invalid expire time in 'expire' command\r\n",
2521            ),
2522            (
2523                &[b"EXPIREAT", b"k", b"9223372036854775807"],
2524                "-ERR invalid expire time in 'expireat' command\r\n",
2525            ),
2526            (
2527                &[b"PEXPIRE", b"k", b"9223372036854775807"],
2528                "-ERR invalid expire time in 'pexpire' command\r\n",
2529            ),
2530        ] {
2531            assert_eq!(f.run(bad), want, "for {bad:?}");
2532        }
2533        assert_eq!(
2534            f.run(&[b"TTL", b"k"]),
2535            ":-1\r\n",
2536            "and none of those put a deadline on anything"
2537        );
2538
2539        // The one of the four that has no arithmetic to overflow. Redis takes
2540        // it and holds the number as given, and a record here holds forty six
2541        // bits, so it lands in the year 4199 instead. D-17.
2542        assert_eq!(
2543            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2544            ":1\r\n"
2545        );
2546        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2547    }
2548
2549    #[test]
2550    fn flushing_empties_this_database_or_every_one_of_them() {
2551        let mut f = Fixture::new();
2552        f.run(&[b"SELECT", b"0"]);
2553        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2554        f.run(&[b"SELECT", b"1"]);
2555        f.run(&[b"SET", b"c", b"3"]);
2556        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2557        // ASYNC and SYNC are both taken and neither changes anything, since the
2558        // keyspace is empty before the OK goes out either way.
2559        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2560        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2561        // Only database one was emptied.
2562        f.run(&[b"SELECT", b"0"]);
2563        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2564        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2565        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2566        f.run(&[b"SELECT", b"1"]);
2567        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2568        // Anything else after the name is a syntax error, and so is a third
2569        // argument even when the second one is a word we take.
2570        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2571        assert_eq!(
2572            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2573            "-ERR syntax error\r\n"
2574        );
2575    }
2576
2577    #[test]
2578    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2579        let mut f = Fixture::new();
2580        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2581        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2582        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2583        // Nothing is cached, so nothing is there, one answer per hash asked
2584        // about.
2585        assert_eq!(
2586            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2587            "*2\r\n:0\r\n:0\r\n"
2588        );
2589        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2590        assert_eq!(
2591            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2592            "*0\r\n"
2593        );
2594        assert_eq!(
2595            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2596            "-ERR Library not found\r\n"
2597        );
2598
2599        // Redis's two messages here are its own, one per container, and one of
2600        // them reads like a typo.
2601        assert_eq!(
2602            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2603            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2604        );
2605        assert_eq!(
2606            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2607            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2608        );
2609        // A second argument after the mode is the generic one instead, because
2610        // the count is checked before the word is looked at.
2611        assert_eq!(
2612            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2613            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2614        );
2615        assert_eq!(
2616            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2617            "-ERR Unknown argument bogus\r\n"
2618        );
2619        assert_eq!(
2620            f.run(&[b"SCRIPT", b"EXISTS"]),
2621            "-ERR wrong number of arguments for 'script|exists' command\r\n"
2622        );
2623
2624        // The ones that need an interpreter are not here, and say so rather
2625        // than answering OK to a load that loaded nothing.
2626        assert_eq!(
2627            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2628            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2629        );
2630        assert_eq!(
2631            f.run(&[b"FUNCTION", b"STATS"]),
2632            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2633        );
2634    }
2635
2636    #[test]
2637    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2638        let mut f = Fixture::new();
2639        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2640        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2641        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2642        // Read back as a string it is still an integer, written out as digits
2643        // only because somebody asked for them.
2644        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2645        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2646        // A counter that is not a number is the error the store raises and this
2647        // layer only spells, which is the whole point of the split.
2648        f.run(&[b"SET", b"k", b"hello"]);
2649        assert_eq!(
2650            f.run(&[b"INCR", b"k"]),
2651            "-ERR value is not an integer or out of range\r\n"
2652        );
2653        assert_eq!(
2654            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2655            "-ERR increment would produce NaN or Infinity\r\n"
2656        );
2657    }
2658
2659    /// Every one of these was read off a running 8.8. They are the answers a
2660    /// client library's own test suite checks, and the shapes are not
2661    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
2662    /// integer, `INCREX` is a pair.
2663    #[test]
2664    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2665        let mut f = Fixture::new();
2666        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2667        // The same digest a real 8.8 answers for the same five bytes, which is
2668        // what makes `IFDEQ` usable against a mixed deployment.
2669        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2670        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2671        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2672        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2673        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2674        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2675        assert_eq!(
2676            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2677            "*2\r\n:1\r\n:0\r\n",
2678            "a refused increment reports the value it left alone and applied nothing"
2679        );
2680        assert_eq!(
2681            f.run(&[
2682                b"INCREX",
2683                b"n",
2684                b"BYINT",
2685                b"5",
2686                b"UBOUND",
2687                b"3",
2688                b"SATURATE"
2689            ]),
2690            "*2\r\n:3\r\n:2\r\n"
2691        );
2692        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2693        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2694    }
2695
2696    #[test]
2697    fn the_same_answers_come_out_in_resp3_spelling() {
2698        let mut f = Fixture::new();
2699        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2700        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2701        // A float counter is a double on RESP3 and the digits in a bulk string
2702        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
2703        assert_eq!(
2704            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2705            "*2\r\n,1.5\r\n,1.5\r\n"
2706        );
2707        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2708        // `RESET` puts the protocol back, which is the part that is easy to
2709        // miss and leaves a pooled connection speaking the wrong one.
2710        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2711        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2712    }
2713
2714    #[test]
2715    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2716        let mut f = Fixture::new();
2717        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2718        assert_eq!(flow, Flow::Continue);
2719        assert_eq!(
2720            reply,
2721            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2722        );
2723        // A name with a line ending in it cannot write its own frame into the
2724        // stream, which is the reason the error writer maps them to spaces.
2725        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2726        assert_eq!(reply.matches("\r\n").count(), 1);
2727    }
2728
2729    #[test]
2730    fn arity_is_checked_before_the_command_is() {
2731        let mut f = Fixture::new();
2732        assert_eq!(
2733            f.run(&[b"GET"]),
2734            "-ERR wrong number of arguments for 'get' command\r\n"
2735        );
2736        assert_eq!(
2737            f.run(&[b"MSET", b"k"]),
2738            "-ERR wrong number of arguments for 'mset' command\r\n"
2739        );
2740        // The table says `PING` takes one or more and a real server then
2741        // refuses three, which is the sort of thing that only shows up against
2742        // the real thing.
2743        assert_eq!(
2744            f.run(&[b"PING", b"a", b"b"]),
2745            "-ERR wrong number of arguments for 'ping' command\r\n"
2746        );
2747        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2748        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2749        // `DELEX` takes two or four and nothing between.
2750        assert_eq!(
2751            f.run(&[b"DELEX", b"k", b"IFEQ"]),
2752            "-ERR wrong number of arguments for 'delex' command\r\n"
2753        );
2754    }
2755
2756    /// The option rules, all of them measured against 8.8 rather than read off
2757    /// the documentation. The surprising one is that `SET` accepts the same
2758    /// keyword twice and `INCREX` does not.
2759    #[test]
2760    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2761        let mut f = Fixture::new();
2762        let syntax = "-ERR syntax error\r\n";
2763        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2764        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2765        assert_eq!(
2766            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2767            syntax
2768        );
2769        assert_eq!(
2770            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2771            syntax
2772        );
2773        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2774        // Twice is fine, and the last one wins.
2775        assert_eq!(
2776            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2777            "+OK\r\n"
2778        );
2779        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2780        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2781        // `INCREX` refuses what `SET` allows.
2782        assert_eq!(
2783            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2784            syntax
2785        );
2786        assert_eq!(
2787            f.run(&[b"INCREX", b"n", b"ENX"]),
2788            "-ERR ENX flag requires an expiration\r\n"
2789        );
2790        assert_eq!(
2791            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2792            "-ERR UBOUND is not an integer or out of range\r\n"
2793        );
2794        assert_eq!(
2795            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2796            "-ERR LBOUND can't be greater than UBOUND\r\n"
2797        );
2798        assert_eq!(
2799            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2800            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2801        );
2802    }
2803
2804    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
2805    /// key that is not there, which answers null without ever looking at the
2806    /// expiration it was given.
2807    #[test]
2808    fn the_expiry_rules_are_redis_own() {
2809        let mut f = Fixture::new();
2810        let bad = "-ERR invalid expire time in 'set' command\r\n";
2811        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2812        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2813        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2814        assert_eq!(
2815            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2816            bad
2817        );
2818        assert_eq!(
2819            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2820            "-ERR value is not an integer or out of range\r\n"
2821        );
2822        assert_eq!(
2823            f.run(&[b"SETEX", b"k", b"0", b"v"]),
2824            "-ERR invalid expire time in 'setex' command\r\n"
2825        );
2826        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2827        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2828        assert_eq!(
2829            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2830            "-ERR syntax error\r\n",
2831            "the option list is still checked before the key is looked up"
2832        );
2833        // A deadline in the past is accepted and the key goes with it.
2834        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2835        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2836        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2837    }
2838
2839    #[test]
2840    fn mset_takes_its_pairs_from_the_read_buffer() {
2841        let mut f = Fixture::new();
2842        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2843        assert_eq!(
2844            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2845            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2846        );
2847        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2848        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2849        assert_eq!(
2850            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2851            "-ERR wrong number of key-value pairs\r\n"
2852        );
2853        assert_eq!(
2854            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2855            "-ERR invalid numkeys value\r\n"
2856        );
2857        assert_eq!(
2858            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2859            "-ERR invalid numkeys value\r\n"
2860        );
2861    }
2862
2863    #[test]
2864    fn lcs_answers_the_length_the_string_and_the_runs() {
2865        let mut f = Fixture::new();
2866        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2867        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2868        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2869        assert_eq!(
2870            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2871            "*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"
2872        );
2873        // Without `IDX` the two options that only mean something with it are
2874        // accepted and ignored, which is what a real server does.
2875        assert_eq!(
2876            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
2877            "$6\r\nmytext\r\n"
2878        );
2879    }
2880
2881    #[test]
2882    fn select_moves_the_connection_and_the_databases_stay_apart() {
2883        let mut f = Fixture::new();
2884        f.run(&[b"SET", b"k", b"zero"]);
2885        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
2886        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2887        f.run(&[b"SET", b"k", b"four"]);
2888        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2889        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2890        assert_eq!(
2891            f.run(&[b"SELECT", b"99"]),
2892            "-ERR DB index is out of range\r\n"
2893        );
2894        assert_eq!(
2895            f.run(&[b"SELECT", b"-1"]),
2896            "-ERR DB index is out of range\r\n"
2897        );
2898        assert_eq!(
2899            f.run(&[b"SELECT", b"abc"]),
2900            "-ERR value is not an integer or out of range\r\n"
2901        );
2902        // `RESET` brings it back to zero.
2903        f.run(&[b"SELECT", b"4"]);
2904        f.run(&[b"RESET"]);
2905        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2906    }
2907
2908    #[test]
2909    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
2910        let mut f = Fixture::new();
2911        let reply = f.run(&[b"HELLO"]);
2912        assert!(reply.starts_with("*14\r\n"), "{reply}");
2913        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
2914        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
2915        assert!(
2916            reply.contains(":7\r\n"),
2917            "the connection id is in there: {reply}"
2918        );
2919        assert_eq!(
2920            f.run(&[b"HELLO", b"4"]),
2921            "-NOPROTO unsupported protocol version\r\n"
2922        );
2923        assert_eq!(
2924            f.run(&[b"HELLO", b"abc"]),
2925            "-ERR Protocol version is not an integer or out of range\r\n"
2926        );
2927        assert_eq!(
2928            f.run(&[b"HELLO", b"3", b"SETNAME"]),
2929            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
2930        );
2931        assert!(
2932            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
2933                .starts_with("%7\r\n")
2934        );
2935        assert_eq!(f.session.name(), b"bob");
2936        f.run(&[b"RESET"]);
2937        assert_eq!(f.session.name(), b"");
2938    }
2939
2940    #[test]
2941    fn command_describes_this_server_in_the_shape_a_driver_reads() {
2942        let mut f = Fixture::new();
2943        let count = format!(":{}\r\n", COMMANDS.len());
2944        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
2945        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
2946        assert_eq!(
2947            info,
2948            "*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\
2949             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
2950        );
2951        // A null in the list, and the plain one: `$-1` and not `*-1`.
2952        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
2953        assert_eq!(
2954            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
2955            "*1\r\n$8\r\ngetrange\r\n"
2956        );
2957        assert_eq!(
2958            f.run(&[b"COMMAND", b"NOPE"]),
2959            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
2960        );
2961    }
2962
2963    /// A cluster aware client asks this question and then routes on the
2964    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
2965    /// that matters.
2966    #[test]
2967    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
2968        let mut f = Fixture::new();
2969        assert_eq!(
2970            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
2971            "*1\r\n$1\r\nk\r\n"
2972        );
2973        assert_eq!(
2974            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
2975            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2976        );
2977        assert_eq!(
2978            f.run(&[
2979                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
2980            ]),
2981            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2982        );
2983        assert_eq!(
2984            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
2985            "-ERR The command has no key arguments\r\n"
2986        );
2987        assert_eq!(
2988            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
2989            "-ERR Invalid number of arguments specified for command\r\n"
2990        );
2991    }
2992
2993    #[test]
2994    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
2995        let mut f = Fixture::new();
2996        assert_eq!(
2997            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2998            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
2999        );
3000        // A pattern matches more than one, and a setting two patterns both ask
3001        // for is still sent once.
3002        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
3003        assert!(both.starts_with("*6\r\n"), "{both}");
3004        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
3005        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
3006        assert_eq!(
3007            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
3008            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
3009        );
3010        assert_eq!(
3011            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
3012            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
3013        );
3014        assert_eq!(
3015            f.run(&[b"CONFIG", b"GET"]),
3016            "-ERR wrong number of arguments for 'config|get' command\r\n"
3017        );
3018        // Too few arguments and an odd number of them are different
3019        // complaints, which is the sort of thing only the real server tells
3020        // you.
3021        assert_eq!(
3022            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
3023            "-ERR wrong number of arguments for 'config|set' command\r\n"
3024        );
3025        assert_eq!(
3026            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
3027            "-ERR syntax error\r\n"
3028        );
3029        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
3030        assert_eq!(
3031            f.run(&[b"CONFIG", b"REWRITE"]),
3032            "-ERR The server is running without a config file\r\n"
3033        );
3034    }
3035
3036    #[test]
3037    fn the_eviction_policy_reads_back_what_was_written_to_it() {
3038        let mut f = Fixture::new();
3039        assert_eq!(
3040            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3041            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
3042        );
3043        assert_eq!(
3044            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
3045            "+OK\r\n",
3046            "the name is matched without regard to case, like every other one"
3047        );
3048        assert_eq!(
3049            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3050            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3051        );
3052        // And INFO agrees with CONFIG, which it did not when it was a literal.
3053        assert!(
3054            f.run(&[b"INFO", b"memory"])
3055                .contains("maxmemory_policy:allkeys-lfu"),
3056            "INFO and CONFIG disagree about the policy"
3057        );
3058        // The refusal names every legal value in the order the real server's
3059        // enum table lists them, because a client comparing the message compares
3060        // the whole string.
3061        assert_eq!(
3062            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
3063            "-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"
3064        );
3065        // A bad pair leaves the good one in the same command alone, and the
3066        // policy is checked by the same pass that checks the numbers.
3067        assert_eq!(
3068            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
3069            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
3070        );
3071        f.run(&[
3072            b"CONFIG",
3073            b"SET",
3074            b"hash-max-listpack-entries",
3075            b"7",
3076            b"maxmemory-policy",
3077            b"nonsense",
3078        ]);
3079        assert_eq!(
3080            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3081            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
3082        );
3083    }
3084
3085    #[test]
3086    fn the_three_eviction_numbers_read_back_too() {
3087        let mut f = Fixture::new();
3088        for (name, default, set) in [
3089            ("maxmemory-samples", "5", "12"),
3090            ("lfu-log-factor", "10", "3"),
3091            ("lfu-decay-time", "1", "60"),
3092        ] {
3093            let get = || {
3094                format!(
3095                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
3096                    name.len(),
3097                    default.len()
3098                )
3099            };
3100            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
3101            assert_eq!(
3102                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
3103                "+OK\r\n"
3104            );
3105            assert_eq!(
3106                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
3107                format!(
3108                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
3109                    name.len(),
3110                    set.len()
3111                )
3112            );
3113            // A number that is not a number is refused with the same sentence
3114            // every other number gets, which names the setting the client typed.
3115            assert_eq!(
3116                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
3117                format!(
3118                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
3119                )
3120            );
3121        }
3122    }
3123
3124    #[test]
3125    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
3126        let mut f = Fixture::new();
3127        assert_eq!(
3128            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3129            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
3130            "no limit is the default"
3131        );
3132        // The pairing is Redis's and it is a trap: the bare letter is a power of
3133        // ten and the one with the b is a power of two.
3134        for (typed, bytes) in [
3135            (&b"1024"[..], "1024"),
3136            (b"1k", "1000"),
3137            (b"1kb", "1024"),
3138            (b"1M", "1000000"),
3139            (b"1Mb", "1048576"),
3140            (b"1gb", "1073741824"),
3141            (b"100mb", "104857600"),
3142        ] {
3143            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
3144            assert_eq!(
3145                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
3146                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
3147                "set {}",
3148                String::from_utf8_lossy(typed)
3149            );
3150        }
3151        assert!(
3152            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3153            "the report agrees with the setting"
3154        );
3155
3156        // A unit nobody has heard of, and a negative number, which is not a very
3157        // large one however it is spelled.
3158        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
3159            assert_eq!(
3160                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
3161                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
3162                "refused {}",
3163                String::from_utf8_lossy(bad)
3164            );
3165        }
3166        assert!(
3167            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3168            "and the refusal left the old one alone"
3169        );
3170    }
3171
3172    #[test]
3173    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3174        let mut f = Fixture::new();
3175        f.run(&[b"SET", b"here", b"already"]);
3176        // A byte, which is under what an empty server holds, so nothing this
3177        // command could do would get it under. The default policy is
3178        // `noeviction`, so nothing is what it does.
3179        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3180        assert_eq!(
3181            f.run(&[b"SET", b"k", b"v"]),
3182            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3183        );
3184        assert_eq!(
3185            f.run(&[b"LPUSH", b"l", b"v"]),
3186            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3187        );
3188        // Reading is allowed, and so is the one thing that would help.
3189        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3190        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3191        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3192
3193        // Taking the limit away lets the write through again.
3194        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3195        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3196    }
3197
3198    #[test]
3199    fn an_allkeys_policy_makes_room_instead_of_refusing() {
3200        let mut f = Fixture::new();
3201        let val = vec![b'v'; 256];
3202        for i in 0..24000u32 {
3203            let k = format!("key:{i:08}");
3204            f.run(&[b"SET", k.as_bytes(), &val]);
3205        }
3206        let full = f.server.memory_bytes();
3207        assert!(
3208            full > 3 * 1024 * 1024,
3209            "the arena is several segments: {full}"
3210        );
3211
3212        // Two megabytes under what it is holding, which is one segment's worth,
3213        // so getting there means giving a whole segment back and not just
3214        // dropping a few records.
3215        let limit = full - 2 * 1024 * 1024;
3216        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3217        f.run(&[
3218            b"CONFIG",
3219            b"SET",
3220            b"maxmemory",
3221            limit.to_string().as_bytes(),
3222        ]);
3223
3224        // Writes keep working the whole way down. The budget means one command
3225        // does not do it all, so this runs until the server has settled and
3226        // checks that nothing was refused on the way.
3227        for i in 0..2000u32 {
3228            let k = format!("new:{i:08}");
3229            assert_eq!(
3230                f.run(&[b"SET", k.as_bytes(), &val]),
3231                "+OK\r\n",
3232                "write {i} was refused"
3233            );
3234            f.server.refresh_memory();
3235            if f.server.memory_bytes() <= limit {
3236                break;
3237            }
3238        }
3239        assert!(
3240            f.server.memory_bytes() <= limit,
3241            "it never got under: {} against {limit}",
3242            f.server.memory_bytes()
3243        );
3244        let info = f.run(&[b"INFO", b"stats"]);
3245        assert!(!info.contains("evicted_keys:0"), "{info}");
3246        assert!(
3247            f.run(&[b"DBSIZE"]) != ":0\r\n",
3248            "and it did not empty the database to get there"
3249        );
3250    }
3251
3252    #[test]
3253    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3254        // The limit is judged against a number kept as the collections move,
3255        // rather than found by asking all of them, and the two have to be the
3256        // same number or the limit is enforced against a fiction. This does the
3257        // things that move it, which is growing a collection, shrinking one,
3258        // changing its representation, deleting it and reusing its slot, across
3259        // all five types, and checks the two against each other as it goes.
3260        let mut f = Fixture::new();
3261        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3262        let big = vec![b'v'; 200];
3263
3264        for i in 0..400u32 {
3265            let n = i.to_string();
3266            let n = n.as_bytes();
3267            f.run(&[b"SADD", b"s", n]);
3268            f.run(&[b"SADD", b"s2", &big]);
3269            f.run(&[b"HSET", b"h", n, &big]);
3270            f.run(&[b"RPUSH", b"l", &big]);
3271            f.run(&[b"ZADD", b"z", n, n]);
3272            f.run(&[b"ARSET", b"a", n, &big]);
3273            if i % 7 == 0 {
3274                f.run(&[b"SREM", b"s", n]);
3275                f.run(&[b"HDEL", b"h", n]);
3276                f.run(&[b"LPOP", b"l"]);
3277                f.run(&[b"ZREM", b"z", n]);
3278                f.run(&[b"ARDEL", b"a", n]);
3279            }
3280            if i % 53 == 0 {
3281                // Every type deleted and made again, so a slot goes on the free
3282                // list and comes back holding something else.
3283                f.run(&[b"DEL", b"s2"]);
3284            }
3285            assert_eq!(
3286                f.server.settled_memory(),
3287                f.server.memory_bytes(),
3288                "after round {i}"
3289            );
3290        }
3291
3292        // The run has to have built something, or the two numbers agreeing is
3293        // two zeroes agreeing.
3294        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3295        assert!(
3296            f.server.memory_bytes() > 512 * 1024,
3297            "{}",
3298            f.server.memory_bytes()
3299        );
3300
3301        // And it survives the collections going away entirely.
3302        f.run(&[b"FLUSHALL"]);
3303        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3304    }
3305
3306    #[test]
3307    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3308        // A server with no limit does not keep the running total, so setting a
3309        // limit on a database that is already full has to start it from a walk.
3310        // If it did not, the first reading would be zero and the server would
3311        // think it had all the room in the world.
3312        let mut f = Fixture::new();
3313        for i in 0..200u32 {
3314            let n = i.to_string();
3315            f.run(&[b"SADD", b"s", n.as_bytes()]);
3316            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3317        }
3318        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3319        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3320
3321        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3322        for i in 200..400u32 {
3323            let n = i.to_string();
3324            f.run(&[b"SADD", b"s", n.as_bytes()]);
3325        }
3326        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3327        assert_eq!(
3328            f.server.settled_memory(),
3329            f.server.memory_bytes(),
3330            "the writes it was not watching are in the number it started from"
3331        );
3332    }
3333
3334    #[test]
3335    fn evicted_keys_and_expired_keys_are_different_numbers() {
3336        let mut f = Fixture::new();
3337        // Nothing has been evicted and nothing can be under the default policy,
3338        // so this stays at zero while the other one moves.
3339        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3340        f.server.db(0).clock_mut().advance(20);
3341        f.run(&[b"GET", b"gone"]);
3342        let info = f.run(&[b"INFO", b"stats"]);
3343        assert!(info.contains("expired_keys:1"), "{info}");
3344        assert!(info.contains("evicted_keys:0"), "{info}");
3345    }
3346
3347    #[test]
3348    fn the_object_subcommands_follow_the_policy() {
3349        let mut f = Fixture::new();
3350        f.run(&[b"SET", b"s", b"v"]);
3351        // Under the default the clock is kept and the counter is not, and under
3352        // an LFU policy it is the other way round. Each subcommand refuses on
3353        // the side where its reading of the three bytes means nothing.
3354        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3355        assert!(
3356            f.run(&[b"OBJECT", b"FREQ", b"s"])
3357                .starts_with("-ERR An LFU maxmemory policy is not selected"),
3358        );
3359
3360        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3361        assert!(
3362            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3363                .starts_with("-ERR An LFU maxmemory policy is selected"),
3364        );
3365        // The key was written under a clock policy, so what comes back is that
3366        // clock read as a counter. It is a number and not an error, which is the
3367        // point: switching at runtime does not invalidate anything, it only makes
3368        // the old field mean something else until the key is used again.
3369        assert!(
3370            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3371            "FREQ should answer under an LFU policy"
3372        );
3373    }
3374
3375    #[test]
3376    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3377        let mut f = Fixture::new();
3378        f.run(&[b"SET", b"s", b"hello"]);
3379        f.run(&[b"SET", b"n", b"123"]);
3380        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3381        f.run(&[b"SADD", b"ss", b"a", b"b"]);
3382        f.run(&[b"HSET", b"h", b"f", b"v"]);
3383        for (key, want) in [
3384            (b"s".as_slice(), "embstr"),
3385            (b"n", "int"),
3386            (b"si", "intset"),
3387            (b"ss", "listpack"),
3388            (b"h", "listpack"),
3389        ] {
3390            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3391            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3392        }
3393
3394        // A field deadline widens the blob rather than promoting it, and this
3395        // is the only place a client can see that happen.
3396        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3397        assert_eq!(
3398            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3399            "$10\r\nlistpackex\r\n"
3400        );
3401
3402        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3403        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3404        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3405    }
3406
3407    #[test]
3408    fn object_answers_nil_for_a_key_that_is_not_there() {
3409        let mut f = Fixture::new();
3410        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3411            assert_eq!(
3412                f.run(&[b"OBJECT", sub, b"nokey"]),
3413                "$-1\r\n",
3414                "a nil and not an error, which is what 8.10.1 does"
3415            );
3416        }
3417        // And the key is looked up before FREQ has its complaint, so the
3418        // complaint only reaches a key that exists.
3419        f.run(&[b"SET", b"s", b"v"]);
3420        assert!(
3421            f.run(&[b"OBJECT", b"FREQ", b"s"])
3422                .starts_with("-ERR An LFU maxmemory policy is not"),
3423        );
3424        assert_eq!(
3425            f.run(&[b"OBJECT", b"NOPE", b"s"]),
3426            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3427        );
3428        assert_eq!(
3429            f.run(&[b"OBJECT", b"ENCODING"]),
3430            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3431        );
3432        assert_eq!(
3433            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3434            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3435        );
3436        assert_eq!(
3437            f.run(&[b"OBJECT"]),
3438            "-ERR wrong number of arguments for 'object' command\r\n"
3439        );
3440    }
3441
3442    #[test]
3443    fn config_moves_the_ladder_and_object_encoding_agrees() {
3444        let mut f = Fixture::new();
3445        assert_eq!(
3446            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3447            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3448            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3449        );
3450        // The old spelling is the same number under a different name, and a
3451        // glob that catches both sends both.
3452        assert_eq!(
3453            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3454            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3455        );
3456        assert!(
3457            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3458                .starts_with("*8\r\n")
3459        );
3460        assert!(
3461            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3462                .starts_with("*6\r\n")
3463        );
3464
3465        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3466        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3467
3468        assert_eq!(
3469            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3470            "+OK\r\n",
3471            "written under the old name and read back under the new one"
3472        );
3473        assert_eq!(
3474            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3475            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3476        );
3477        assert_eq!(
3478            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3479            "$8\r\nlistpack\r\n",
3480            "the hash that already exists is left exactly where it was"
3481        );
3482        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3483        assert_eq!(
3484            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3485            "$9\r\nhashtable\r\n",
3486            "and the next one built goes straight to a table"
3487        );
3488
3489        // The set has three of these and all three move.
3490        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3491        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3492        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3493        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3494        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3495        assert_eq!(
3496            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3497            "$9\r\nhashtable\r\n"
3498        );
3499    }
3500
3501    #[test]
3502    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3503        let mut f = Fixture::new();
3504        assert_eq!(
3505            f.run(&[
3506                b"CONFIG",
3507                b"SET",
3508                b"hash-max-listpack-entries",
3509                b"7",
3510                b"set-max-listpack-entries",
3511                b"abc"
3512            ]),
3513            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3514        );
3515        assert_eq!(
3516            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3517            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3518            "the pair in front of the bad one did not go in"
3519        );
3520        // The name in the complaint is the one that was typed, so the old
3521        // spelling comes back as the old spelling.
3522        assert_eq!(
3523            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3524            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3525        );
3526        assert_eq!(
3527            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3528            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3529        );
3530        // A number past what an i64 holds is the parse complaint and not the
3531        // range one, which is upstream reading it before it checks it.
3532        assert_eq!(
3533            f.run(&[
3534                b"CONFIG",
3535                b"SET",
3536                b"set-max-intset-entries",
3537                b"99999999999999999999"
3538            ]),
3539            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3540        );
3541        assert_eq!(
3542            f.run(&[
3543                b"CONFIG",
3544                b"SET",
3545                b"set-max-intset-entries",
3546                b"9223372036854775807"
3547            ]),
3548            "+OK\r\n"
3549        );
3550    }
3551
3552    #[test]
3553    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3554        let mut f = Fixture::new();
3555        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3556        f.run(&[b"SELECT", b"3"]);
3557        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3558        assert_eq!(
3559            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3560            "$9\r\nhashtable\r\n",
3561            "these are one server wide number in Redis, whatever a Keyspace carries"
3562        );
3563    }
3564
3565    #[test]
3566    fn info_reports_the_numbers_it_can_stand_behind() {
3567        let mut f = Fixture::new();
3568        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3569        let all = f.run(&[b"INFO"]);
3570        assert!(all.contains("redis_version:8.8.0"), "{all}");
3571        assert!(
3572            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3573            "{all}"
3574        );
3575        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3576        assert!(all.contains("role:master"), "{all}");
3577        // One section is one section.
3578        let clients = f.run(&[b"INFO", b"clients"]);
3579        assert!(clients.contains("connected_clients:0"), "{clients}");
3580        assert!(!clients.contains("redis_version"), "{clients}");
3581        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3582    }
3583
3584    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
3585    ///
3586    /// This is Redis's `unit/info-command` written against the fixture. Every
3587    /// assertion in it is one of theirs, in their order, and the two fields it
3588    /// turns on are the two that suite was failing on: `master_repl_offset`,
3589    /// which is in the default set, and `rejected_calls`, which is not.
3590    #[test]
3591    fn commandstats_is_asked_for_and_replication_is_not() {
3592        let mut f = Fixture::new();
3593        for arg in ["", "all", "default", "everything"] {
3594            let info = if arg.is_empty() {
3595                f.run(&[b"INFO"])
3596            } else {
3597                f.run(&[b"INFO", arg.as_bytes()])
3598            };
3599            assert!(info.contains("redis_version"), "{arg}: {info}");
3600            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3601            assert!(info.contains("used_memory"), "{arg}: {info}");
3602            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3603            let asked = arg == "all" || arg == "everything";
3604            assert_eq!(
3605                info.contains("rejected_calls"),
3606                asked,
3607                "{arg} should{} carry the command counters: {info}",
3608                if asked { "" } else { " not" }
3609            );
3610        }
3611
3612        let cpu = f.run(&[b"INFO", b"cpu"]);
3613        assert!(cpu.contains("used_cpu_user"), "{cpu}");
3614        assert!(!cpu.contains("used_memory"), "{cpu}");
3615
3616        // Their case, to make the point that a section name is not case
3617        // sensitive any more than a command name is.
3618        let stats = f.run(&[b"INFO", b"commandSTATS"]);
3619        assert!(!stats.contains("used_memory"), "{stats}");
3620        assert!(stats.contains("rejected_calls"), "{stats}");
3621
3622        // Two sections named, and neither of them pulls in a third.
3623        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3624        assert!(pair.contains("used_cpu_user"), "{pair}");
3625        assert!(!pair.contains("master_repl_offset"), "{pair}");
3626
3627        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3628        assert!(with_all.contains("used_memory"), "{with_all}");
3629        assert!(with_all.contains("master_repl_offset"), "{with_all}");
3630        assert!(with_all.contains("rejected_calls"), "{with_all}");
3631        // A section named twice is still written once.
3632        assert_eq!(
3633            with_all.matches("used_cpu_user_children").count(),
3634            1,
3635            "{with_all}"
3636        );
3637
3638        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3639        assert!(with_default.contains("used_memory"), "{with_default}");
3640        assert!(
3641            with_default.contains("master_repl_offset"),
3642            "{with_default}"
3643        );
3644        assert!(!with_default.contains("rejected_calls"), "{with_default}");
3645        assert_eq!(
3646            with_default.matches("used_cpu_user_children").count(),
3647            1,
3648            "{with_default}"
3649        );
3650    }
3651
3652    /// The memory section says what this process may use, not what the machine
3653    /// has.
3654    ///
3655    /// The distinction is the whole point of it. A server inside a container
3656    /// that reports the host's memory is a server whose operator sizes it for
3657    /// memory it will be killed for touching, so all three numbers are there:
3658    /// what the machine has, what the cgroup allows, and the quarter of the
3659    /// tighter one that pools are sized from.
3660    #[test]
3661    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3662        let mut f = Fixture::new();
3663        let info = f.run(&[b"INFO", b"memory"]);
3664        for field in [
3665            "total_system_memory:",
3666            "mem_cgroup_limit:",
3667            "mem_limit:",
3668            "mem_budget:",
3669        ] {
3670            assert!(info.contains(field), "no {field} in {info}");
3671        }
3672
3673        let field = |name: &str| -> u64 {
3674            info.lines()
3675                .find_map(|l| l.strip_prefix(name))
3676                .unwrap_or_else(|| panic!("no {name} in {info}"))
3677                .trim()
3678                .parse()
3679                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3680        };
3681        let limit = field("mem_limit:");
3682        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3683        // Zero means there is no limit to report, which is a real answer on a
3684        // machine with no cgroups and no way to ask how big it is.
3685        if limit != 0 {
3686            let host = field("total_system_memory:");
3687            let cgroup = field("mem_cgroup_limit:");
3688            assert!(
3689                limit == host || limit == cgroup,
3690                "the limit came from neither number: {info}"
3691            );
3692        }
3693    }
3694
3695    /// The three counters, each on the path that raises it.
3696    ///
3697    /// `calls` on a command that worked, `failed_calls` on one that ran and
3698    /// answered with an error, and `rejected_calls` on one that never ran at
3699    /// all. The last two are the pair that is easy to collapse into one number
3700    /// and that Redis keeps apart, because a client sending the wrong number of
3701    /// arguments and a client asking for a list element that is not there are
3702    /// not the same problem.
3703    #[test]
3704    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3705        let mut f = Fixture::new();
3706        f.run(&[b"SET", b"k", b"v"]);
3707        f.run(&[b"SET", b"k", b"w"]);
3708        // Ran, and answered with an error, because `k` is not a list.
3709        f.run(&[b"LPUSH", b"k", b"x"]);
3710        // Never ran: `LPUSH` takes at least three arguments.
3711        f.run(&[b"LPUSH", b"k"]);
3712
3713        let stats = f.run(&[b"INFO", b"commandstats"]);
3714        assert!(
3715            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3716            "{stats}"
3717        );
3718        assert!(
3719            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3720            "{stats}"
3721        );
3722        assert!(
3723            !stats.contains("cmdstat_zadd"),
3724            "a command nobody has sent has no row: {stats}"
3725        );
3726    }
3727
3728    /// A cache that writes with a deadline and never reads back used to hold
3729    /// every key it had ever written, because lazy expiry needs somebody to walk
3730    /// past a key before it can reclaim it and nobody ever did.
3731    #[test]
3732    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3733        let mut f = Fixture::new();
3734        for i in 0..3_000u32 {
3735            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3736        }
3737        for i in 0..1_000u32 {
3738            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3739        }
3740        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3741        f.advance(100);
3742        assert_eq!(
3743            f.run(&[b"DBSIZE"]),
3744            ":4000\r\n",
3745            "DBSIZE counts records and nothing has read past the dead ones yet"
3746        );
3747
3748        // What the shard loop does, one slice at a time.
3749        let mut spent = 0;
3750        for _ in 0..2_000 {
3751            spent += f.server.expire_step(4096);
3752            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3753                break;
3754            }
3755        }
3756        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3757        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3758        for i in 0..1_000u32 {
3759            assert_eq!(
3760                f.run(&[b"GET", format!("k{i}").as_bytes()]),
3761                "$1\r\nv\r\n",
3762                "it took a key that had no deadline"
3763            );
3764        }
3765    }
3766
3767    #[test]
3768    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3769        let mut f = Fixture::new();
3770        for i in 0..2_000u32 {
3771            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3772        }
3773        assert_eq!(f.server.expire_step(4096), 0);
3774        // And one database having them does not make the other fifteen pay.
3775        f.run(&[b"SELECT", b"3"]);
3776        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3777        f.advance(100);
3778        for _ in 0..64 {
3779            f.server.expire_step(4096);
3780        }
3781        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3782        f.run(&[b"SELECT", b"0"]);
3783        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3784        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3785    }
3786
3787    /// The gate, which is what stops a maintenance slice that runs every hundred
3788    /// nanoseconds from drawing a sample every hundred nanoseconds.
3789    #[test]
3790    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3791        let mut f = Fixture::new();
3792        for i in 0..500u32 {
3793            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3794        }
3795        f.advance(100);
3796        let at = f.server.db(0).clock().now_ms();
3797        f.server.set_clock_ms(at);
3798        // A small budget, so that one slice cannot finish the job and a second
3799        // one having nothing to do would mean the gate and not an empty
3800        // database.
3801        assert!(f.server.expire_slice(8) > 0, "the first one works");
3802        for _ in 0..1_000 {
3803            assert_eq!(
3804                f.server.expire_slice(8),
3805                0,
3806                "the millisecond has not moved and neither should this"
3807            );
3808        }
3809        assert!(
3810            f.server.db(0).expires() > 400,
3811            "there is plenty left to take"
3812        );
3813        f.server.set_clock_ms(at + 1);
3814        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3815    }
3816
3817    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
3818    /// how much of a cache is volatile was reading a constant.
3819    #[test]
3820    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3821        let mut f = Fixture::new();
3822        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3823        assert!(
3824            f.run(&[b"INFO", b"keyspace"])
3825                .contains("db0:keys=3,expires=0"),
3826            "none of them has one yet"
3827        );
3828        f.run(&[b"EXPIRE", b"a", b"1000"]);
3829        f.run(&[b"EXPIRE", b"b", b"1000"]);
3830        let two = f.run(&[b"INFO", b"keyspace"]);
3831        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3832        f.run(&[b"PERSIST", b"a"]);
3833        f.run(&[b"DEL", b"b"]);
3834        let none = f.run(&[b"INFO", b"keyspace"]);
3835        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3836
3837        // Each database answers for itself, the way Redis reports it.
3838        f.run(&[b"SELECT", b"1"]);
3839        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3840        let both = f.run(&[b"INFO", b"keyspace"]);
3841        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3842        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3843    }
3844
3845    #[cfg(unix)]
3846    #[test]
3847    fn info_cpu_reports_processor_time_that_was_really_measured() {
3848        let mut f = Fixture::new();
3849        let cpu = f.run(&[b"INFO", b"cpu"]);
3850        assert!(cpu.contains("# CPU"), "{cpu}");
3851        // Redis's unit/info-command asks for this one by name in three tests.
3852        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3853        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3854        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3855        assert!(!cpu.contains("redis_version"), "{cpu}");
3856
3857        // It is a measurement and not a constant, so it goes up when work
3858        // happens. A tight loop rather than a sleep, because sleeping is the
3859        // one thing that does not move this number.
3860        let before = used_cpu_user(&cpu);
3861        let mut n = 0u64;
3862        let mut rounds = 0;
3863        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3864            for i in 0..1_000_000u64 {
3865                n = n.wrapping_add(i.wrapping_mul(i));
3866            }
3867            rounds += 1;
3868            // A bound rather than a spin, so a platform where this number does
3869            // not move fails here instead of hanging. Even a clock with whole
3870            // millisecond granularity gets there in the first round or two.
3871            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3872        }
3873    }
3874
3875    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
3876    #[cfg(unix)]
3877    fn used_cpu_user(info: &str) -> f64 {
3878        info.lines()
3879            .find_map(|l| l.strip_prefix("used_cpu_user:"))
3880            .expect("no used_cpu_user in the reply")
3881            .trim()
3882            .parse()
3883            .expect("used_cpu_user is not a number")
3884    }
3885
3886    /// The safety net under the rule that a body checks its arguments before
3887    /// it writes anything. `MGET` writes its array header first and then reads
3888    /// each key, so if a later argument could fail the header would already be
3889    /// out. Nothing in the string group does that today and this is what would
3890    /// catch the first one that did.
3891    #[test]
3892    fn a_command_that_fails_leaves_nothing_half_written() {
3893        let mut f = Fixture::new();
3894        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
3895        assert_eq!(reply, "-ERR offset is out of range\r\n");
3896        assert!(!reply.contains(':'), "no integer went out in front of it");
3897    }
3898
3899    #[test]
3900    fn quit_answers_first_and_closes_after() {
3901        let mut f = Fixture::new();
3902        let (flow, reply) = f.flow(&[b"QUIT"]);
3903        assert_eq!(reply, "+OK\r\n");
3904        assert_eq!(flow, Flow::Close);
3905    }
3906
3907    /// A server that has not been asked to stop is not stopping, and one that
3908    /// has says so without writing anything back.
3909    ///
3910    /// The empty reply is the point. Redis answers nothing at all here and the
3911    /// client sees the socket close, and an `OK` would be a promise from a
3912    /// process that is about to not exist.
3913    #[test]
3914    fn shutdown_writes_nothing_and_sets_the_flag() {
3915        let mut f = Fixture::new();
3916        assert!(!f.server.stopping(), "nobody has asked yet");
3917
3918        let (flow, reply) = f.flow(&[b"SHUTDOWN"]);
3919        assert_eq!(reply, "");
3920        assert_eq!(flow, Flow::Close);
3921        assert!(f.server.stopping());
3922    }
3923
3924    /// Every flag combination 8.10.1 takes, and every one it refuses.
3925    ///
3926    /// The refusals are the half worth pinning down. `SAVE` and `NOSAVE`
3927    /// contradict each other, `ABORT` says to do nothing so it cannot be
3928    /// combined with a word about how to do it, and repeating any one of them
3929    /// is fine. All of it was read off a running 8.10.1 rather than worked out
3930    /// from the documentation, which does not say.
3931    #[test]
3932    fn shutdown_takes_the_flags_redis_takes() {
3933        for flags in [
3934            &[b"NOSAVE".as_slice()][..],
3935            &[b"SAVE"],
3936            &[b"NOW"],
3937            &[b"FORCE"],
3938            &[b"nosave"],
3939            &[b"NOW", b"NOW"],
3940            &[b"SAVE", b"SAVE"],
3941            &[b"NOSAVE", b"NOW", b"FORCE"],
3942        ] {
3943            let mut f = Fixture::new();
3944            let mut parts = vec![b"SHUTDOWN".as_slice()];
3945            parts.extend_from_slice(flags);
3946            let (flow, reply) = f.flow(&parts);
3947            assert_eq!(reply, "", "SHUTDOWN {flags:?} answered something");
3948            assert_eq!(flow, Flow::Close, "SHUTDOWN {flags:?} did not close");
3949            assert!(f.server.stopping(), "SHUTDOWN {flags:?} did not stop");
3950        }
3951
3952        for flags in [
3953            &[b"BOGUS".as_slice()][..],
3954            &[b"SAVE", b"NOSAVE"],
3955            &[b"NOSAVE", b"SAVE"],
3956            &[b"ABORT", b"NOW"],
3957            &[b"NOSAVE", b"ABORT"],
3958            &[b"NOW", b"FORCE", b"ABORT"],
3959        ] {
3960            let mut f = Fixture::new();
3961            let mut parts = vec![b"SHUTDOWN".as_slice()];
3962            parts.extend_from_slice(flags);
3963            assert_eq!(
3964                f.run(&parts),
3965                "-ERR syntax error\r\n",
3966                "SHUTDOWN {flags:?} was accepted"
3967            );
3968            assert!(!f.server.stopping(), "SHUTDOWN {flags:?} stopped anyway");
3969        }
3970    }
3971
3972    /// `ABORT` has nothing to call off, ever.
3973    ///
3974    /// A shutdown here is decided and done inside one turn of the loop, so
3975    /// there is no window in which one is in progress. That makes Redis's
3976    /// message for a cancel with nothing to cancel the right answer every time
3977    /// rather than only when nothing happens to be pending. Two `ABORT`s is
3978    /// still one `ABORT`, which is what 8.10.1 does.
3979    #[test]
3980    fn shutdown_abort_never_has_anything_to_abort() {
3981        let mut f = Fixture::new();
3982        for parts in [
3983            &[b"SHUTDOWN".as_slice(), b"ABORT"][..],
3984            &[b"SHUTDOWN", b"ABORT", b"ABORT"],
3985        ] {
3986            assert_eq!(f.run(parts), "-ERR No shutdown in progress.\r\n");
3987            assert!(!f.server.stopping(), "an abort stopped the server");
3988        }
3989    }
3990
3991    /// A fixture whose server writes into a directory of its own.
3992    ///
3993    /// Every test here really writes files, because the whole point of the
3994    /// command is the files and a backup that is only a state machine would
3995    /// pass a test suite and fail the first person who tried to restore one.
3996    /// The directory carries the test's name so that the suite can run its
3997    /// tests in parallel the way it always does.
3998    struct Backups {
3999        f: Fixture,
4000        dir: PathBuf,
4001    }
4002
4003    impl Backups {
4004        fn new(name: &str) -> Backups {
4005            let dir = std::env::temp_dir().join(format!("yo-backup-{name}-{}", std::process::id()));
4006            let _ = std::fs::remove_dir_all(&dir);
4007            std::fs::create_dir_all(&dir).expect("could not make a temporary directory");
4008            let mut f = Fixture::new();
4009            f.server.set_dir(dir.clone());
4010            Backups { f, dir }
4011        }
4012
4013        fn run(&mut self, parts: &[&[u8]]) -> String {
4014            self.f.run(parts)
4015        }
4016
4017        /// The names in `backupdir`, sorted, so a test can say what is on disk.
4018        fn files(&self) -> Vec<String> {
4019            let mut names: Vec<String> = match std::fs::read_dir(self.dir.join("backupdir")) {
4020                Ok(entries) => entries
4021                    .filter_map(|e| e.ok())
4022                    .map(|e| e.file_name().to_string_lossy().into_owned())
4023                    .collect(),
4024                Err(_) => Vec::new(),
4025            };
4026            names.sort();
4027            names
4028        }
4029
4030        fn read(&self, name: &str) -> Vec<u8> {
4031            std::fs::read(self.dir.join("backupdir").join(name)).expect("could not read")
4032        }
4033    }
4034
4035    impl Drop for Backups {
4036        fn drop(&mut self) {
4037            let _ = std::fs::remove_dir_all(&self.dir);
4038        }
4039    }
4040
4041    /// The four states and the moves between them, in the order a client walks
4042    /// them, with the files checked at every step.
4043    #[test]
4044    fn backup_walks_the_states_the_reference_walks() {
4045        let mut b = Backups::new("states");
4046        let status = |b: &mut Backups| b.run(&[b"BACKUP", b"STATUS"]);
4047
4048        assert!(status(&mut b).contains("idle"));
4049        assert!(b.files().is_empty(), "an idle server has written a backup");
4050
4051        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4052        assert!(status(&mut b).contains("incrementing"));
4053        assert_eq!(b.files(), ["appendonly.aof.1.base.rdb"]);
4054
4055        assert_eq!(b.run(&[b"BACKUP", b"SEAL"]), "+OK\r\n");
4056        assert!(status(&mut b).contains("sealed"));
4057        assert_eq!(
4058            b.files(),
4059            [
4060                "appendonly.aof.1.base.rdb",
4061                "appendonly.aof.1.incr.aof",
4062                "appendonly.aof.manifest",
4063            ]
4064        );
4065
4066        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4067        assert!(status(&mut b).contains("idle"));
4068        assert!(b.files().is_empty(), "cleanup left something behind");
4069    }
4070
4071    /// Every move that is refused, in the reference's words.
4072    #[test]
4073    fn backup_refuses_the_moves_the_reference_refuses() {
4074        let mut b = Backups::new("refusals");
4075
4076        assert_eq!(
4077            b.run(&[b"BACKUP", b"SEAL"]),
4078            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4079        );
4080        assert_eq!(
4081            b.run(&[b"BACKUP", b"ABORT"]),
4082            "-ERR No backup in progress\r\n"
4083        );
4084        // Cleanup from idle is not an error, it is a way of saying there was
4085        // nothing to clean up.
4086        assert_eq!(b.run(&[b"BACKUP", b"CLEANUP"]), "+OK\r\n");
4087
4088        b.run(&[b"BACKUP", b"START"]);
4089        assert_eq!(
4090            b.run(&[b"BACKUP", b"START"]),
4091            "-ERR A backup is already in progress, ABORT it first\r\n"
4092        );
4093        assert_eq!(
4094            b.run(&[b"BACKUP", b"CLEANUP"]),
4095            "-ERR Backup is in progress\r\n"
4096        );
4097
4098        b.run(&[b"BACKUP", b"SEAL"]);
4099        assert_eq!(
4100            b.run(&[b"BACKUP", b"START"]),
4101            "-ERR A sealed backup exists, CLEANUP it first\r\n"
4102        );
4103        assert_eq!(
4104            b.run(&[b"BACKUP", b"SEAL"]),
4105            "-ERR No backup ready to seal (must be in the incrementing state)\r\n"
4106        );
4107        assert_eq!(
4108            b.run(&[b"BACKUP", b"ABORT"]),
4109            "-ERR No backup in progress\r\n"
4110        );
4111    }
4112
4113    /// An abort takes the base file away and leaves a state saying who did it.
4114    ///
4115    /// The next backup takes the next sequence number rather than reusing the
4116    /// one whose files were just thrown away, so a directory somebody copied a
4117    /// half finished backup out of cannot end up with two different files under
4118    /// one name.
4119    #[test]
4120    fn backup_abort_removes_the_file_and_says_who_did_it() {
4121        let mut b = Backups::new("abort");
4122        b.run(&[b"BACKUP", b"START"]);
4123        assert_eq!(b.run(&[b"BACKUP", b"ABORT"]), "+OK\r\n");
4124
4125        let status = b.run(&[b"BACKUP", b"STATUS"]);
4126        assert!(status.contains("failed"), "{status}");
4127        assert!(status.contains("aborted by user"), "{status}");
4128        assert!(b.files().is_empty(), "abort left the base file behind");
4129        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4130
4131        // A start from failed works, and is the second backup.
4132        assert_eq!(b.run(&[b"BACKUP", b"START"]), "+OK\r\n");
4133        assert_eq!(b.files(), ["appendonly.aof.2.base.rdb"]);
4134        let status = b.run(&[b"BACKUP", b"STATUS"]);
4135        assert!(status.contains("incrementing"), "{status}");
4136        assert!(!status.contains("aborted"), "the old error was kept");
4137    }
4138
4139    /// `LIST` names nothing, then one file, then three, and they are absolute.
4140    #[test]
4141    fn backup_list_names_the_files_that_are_pinned_so_far() {
4142        let mut b = Backups::new("list");
4143        assert_eq!(b.run(&[b"BACKUP", b"LIST"]), "*0\r\n");
4144
4145        b.run(&[b"BACKUP", b"START"]);
4146        let base = b.dir.join("backupdir").join("appendonly.aof.1.base.rdb");
4147        let base = base.to_string_lossy().into_owned();
4148        assert_eq!(
4149            b.run(&[b"BACKUP", b"LIST"]),
4150            format!("*1\r\n${}\r\n{base}\r\n", base.len())
4151        );
4152
4153        b.run(&[b"BACKUP", b"SEAL"]);
4154        let listed = b.run(&[b"BACKUP", b"LIST"]);
4155        assert!(listed.starts_with("*3\r\n"), "{listed}");
4156        // The order is the manifest's order, base then incremental then the
4157        // manifest itself, which is the order a restore needs them in.
4158        let names: Vec<&str> = listed
4159            .lines()
4160            .filter(|l| l.starts_with('/') || l.contains(":\\"))
4161            .collect();
4162        assert_eq!(names.len(), 3, "{listed}");
4163        assert!(names[0].ends_with("appendonly.aof.1.base.rdb"), "{listed}");
4164        assert!(names[1].ends_with("appendonly.aof.1.incr.aof"), "{listed}");
4165        assert!(names[2].ends_with("appendonly.aof.manifest"), "{listed}");
4166    }
4167
4168    /// The base file is the dataset as it was at `START` and not at `SEAL`.
4169    ///
4170    /// That is D-46 and it is the one thing about this a client can notice, so
4171    /// it is pinned here rather than left to be discovered by whoever restores
4172    /// one. The incremental file is empty for the same reason: there is no
4173    /// append only log underneath this server to copy the writes in between out
4174    /// of.
4175    #[test]
4176    fn a_backup_holds_the_dataset_as_it_was_at_start() {
4177        let mut b = Backups::new("contents");
4178        b.run(&[b"SET", b"bk", b"v1"]);
4179        b.run(&[b"BACKUP", b"START"]);
4180        b.run(&[b"SET", b"bk", b"v2"]);
4181        b.run(&[b"BACKUP", b"SEAL"]);
4182
4183        let base = b.read("appendonly.aof.1.base.rdb");
4184        assert!(base.starts_with(b"REDIS"), "not an RDB file");
4185        assert!(base.windows(2).any(|w| w == b"v1"), "the value is missing");
4186        assert!(
4187            !base.windows(2).any(|w| w == b"v2"),
4188            "the base file moved on after START"
4189        );
4190        // The aux field a loader acts on, and the one that says this file is
4191        // the base of an append only file rather than a standalone dump. Its
4192        // value is the one byte string 1, which the encoder writes as an
4193        // integer the way a real server writes it.
4194        let at = base
4195            .windows(8)
4196            .position(|w| w == b"aof-base")
4197            .expect("no aof-base aux field");
4198        assert_eq!(&base[at + 8..at + 10], b"\xc0\x01", "{:?}", &base[at..]);
4199
4200        assert!(b.read("appendonly.aof.1.incr.aof").is_empty());
4201        assert_eq!(
4202            String::from_utf8(b.read("appendonly.aof.manifest")).expect("the manifest is text"),
4203            "file appendonly.aof.1.base.rdb seq 1 type b\n\
4204             file appendonly.aof.1.incr.aof seq 1 type i startoffset 0 endoffset 0\n"
4205        );
4206    }
4207
4208    /// `STATUS` is a map of four pairs on RESP3 and the same pairs flat on
4209    /// RESP2, which is what every other map shaped reply in this server does.
4210    #[test]
4211    fn backup_status_is_a_map_on_resp3_and_a_flat_array_on_resp2() {
4212        let mut b = Backups::new("status");
4213        b.f.server.set_clock_ms(1_700_000_000_000);
4214
4215        assert_eq!(
4216            b.run(&[b"BACKUP", b"STATUS"]),
4217            "*8\r\n$5\r\nstate\r\n$4\r\nidle\r\n$5\r\nerror\r\n$0\r\n\r\n\
4218             $10\r\nstart_time\r\n:0\r\n$8\r\nend_time\r\n:0\r\n"
4219        );
4220
4221        b.f.out = Out::new(Proto::Resp3);
4222        b.run(&[b"BACKUP", b"START"]);
4223        assert_eq!(
4224            b.run(&[b"BACKUP", b"STATUS"]),
4225            "%4\r\n$5\r\nstate\r\n$12\r\nincrementing\r\n$5\r\nerror\r\n$0\r\n\r\n\
4226             $10\r\nstart_time\r\n:1700000000\r\n$8\r\nend_time\r\n:0\r\n"
4227        );
4228
4229        b.run(&[b"BACKUP", b"SEAL"]);
4230        let sealed = b.run(&[b"BACKUP", b"STATUS"]);
4231        assert!(sealed.contains("end_time\r\n:1700000000"), "{sealed}");
4232    }
4233
4234    /// A sealed backup that nobody cleans up goes away on its own once
4235    /// `backup-sealed-ttl` seconds have passed since the seal.
4236    #[test]
4237    fn a_sealed_backup_is_swept_away_after_the_timeout() {
4238        let mut b = Backups::new("ttl");
4239        b.f.server.set_clock_ms(1_000_000);
4240        assert_eq!(
4241            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"60"]),
4242            "+OK\r\n"
4243        );
4244        b.run(&[b"BACKUP", b"START"]);
4245        b.run(&[b"BACKUP", b"SEAL"]);
4246
4247        // A minute short of the deadline, nothing happens.
4248        b.f.server.set_clock_ms(1_000_000 + 59_000);
4249        b.f.server.backup_expire();
4250        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4251        assert_eq!(b.files().len(), 3);
4252
4253        b.f.server.set_clock_ms(1_000_000 + 60_000);
4254        b.f.server.backup_expire();
4255        let status = b.run(&[b"BACKUP", b"STATUS"]);
4256        assert!(status.contains("idle"), "{status}");
4257        assert!(b.files().is_empty(), "the timeout left the files behind");
4258
4259        // Zero is the default and means a sealed backup is kept for ever.
4260        b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"0"]);
4261        b.run(&[b"BACKUP", b"START"]);
4262        b.run(&[b"BACKUP", b"SEAL"]);
4263        b.f.server.set_clock_ms(9_000_000_000);
4264        b.f.server.backup_expire();
4265        assert!(b.run(&[b"BACKUP", b"STATUS"]).contains("sealed"));
4266    }
4267
4268    /// The three settings around the command, read and written the way 8.10.1
4269    /// reads and writes them.
4270    #[test]
4271    fn the_backup_settings_behave_the_way_the_reference_does() {
4272        let mut b = Backups::new("config");
4273        let dir = b.dir.to_string_lossy().into_owned();
4274
4275        assert_eq!(
4276            b.run(&[b"CONFIG", b"GET", b"dir"]),
4277            format!("*2\r\n$3\r\ndir\r\n${}\r\n{dir}\r\n", dir.len())
4278        );
4279        assert_eq!(
4280            b.run(&[b"CONFIG", b"GET", b"backupdirname"]),
4281            "*2\r\n$13\r\nbackupdirname\r\n$9\r\nbackupdir\r\n"
4282        );
4283        assert_eq!(
4284            b.run(&[b"CONFIG", b"GET", b"backup-sealed-ttl"]),
4285            "*2\r\n$17\r\nbackup-sealed-ttl\r\n$1\r\n0\r\n"
4286        );
4287
4288        // `dir` is a protected config, so it is refused even for the value it
4289        // already holds, and `backupdirname` is immutable.
4290        assert_eq!(
4291            b.run(&[b"CONFIG", b"SET", b"dir", dir.as_bytes()]),
4292            "-ERR CONFIG SET failed (possibly related to argument 'dir') - can't set protected config\r\n"
4293        );
4294        assert_eq!(
4295            b.run(&[b"CONFIG", b"SET", b"backupdirname", b"other"]),
4296            "-ERR CONFIG SET failed (possibly related to argument 'backupdirname') - can't set immutable config\r\n"
4297        );
4298        assert!(
4299            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"abc"])
4300                .contains("argument couldn't be parsed into an integer")
4301        );
4302        assert!(
4303            b.run(&[b"CONFIG", b"SET", b"backup-sealed-ttl", b"-1"])
4304                .contains("argument must be between 0 and 9223372036854775807 inclusive")
4305        );
4306    }
4307
4308    /// The help text, which has `HELP` in it twice because the reference's does.
4309    #[test]
4310    fn backup_help_is_the_text_the_reference_sends() {
4311        let mut f = Fixture::new();
4312        let help = f.run(&[b"BACKUP", b"HELP"]);
4313        assert!(help.starts_with("*17\r\n"), "{help}");
4314        assert!(
4315            help.contains("+BACKUP <subcommand> [<arg> [value] [opt] ...]. Subcommands are:\r\n")
4316        );
4317        assert!(help.contains("+    Start a new backup into the configured 'backupdirname'.\r\n"));
4318        assert!(help.contains("+    Freeze the current backup (BASE + INCR + manifest).\r\n"));
4319        assert!(help.contains("+    Return this help.\r\n+HELP\r\n+    Print this help.\r\n"));
4320    }
4321
4322    /// What a mistyped `BACKUP` gets told.
4323    ///
4324    /// The arity error names `backup` where the reference names `backup|start`,
4325    /// which is D-46: the table reports one arity for the container the way the
4326    /// reference does, and the per subcommand table that would carry the better
4327    /// name is not built yet. Every subcommand is exactly two words, so nothing
4328    /// legal is refused by it.
4329    #[test]
4330    fn backup_refuses_what_it_cannot_read() {
4331        let mut f = Fixture::new();
4332        assert_eq!(
4333            f.run(&[b"BACKUP"]),
4334            "-ERR wrong number of arguments for 'backup' command\r\n"
4335        );
4336        assert_eq!(
4337            f.run(&[b"BACKUP", b"START", b"x"]),
4338            "-ERR wrong number of arguments for 'backup' command\r\n"
4339        );
4340        assert_eq!(
4341            f.run(&[b"BACKUP", b"NOPE"]),
4342            "-ERR unknown subcommand 'NOPE'. Try BACKUP HELP.\r\n"
4343        );
4344    }
4345
4346    #[test]
4347    fn the_command_counter_counts_every_command_including_the_bad_ones() {
4348        let mut f = Fixture::new();
4349        f.run(&[b"PING"]);
4350        f.run(&[b"NOPE"]);
4351        f.run(&[b"GET"]);
4352        assert_eq!(f.server.stats.commands, 3);
4353    }
4354
4355    #[test]
4356    fn a_set_goes_from_bytes_to_bytes() {
4357        let mut f = Fixture::new();
4358        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
4359        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
4360        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
4361        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
4362        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
4363        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
4364        assert_eq!(
4365            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
4366            "*3\r\n:1\r\n:0\r\n:1\r\n"
4367        );
4368        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
4369        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4370    }
4371
4372    #[test]
4373    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
4374        let mut f = Fixture::new();
4375        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
4376        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
4377        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
4378        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
4379        assert_eq!(
4380            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
4381            "*2\r\n:0\r\n:0\r\n"
4382        );
4383        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
4384    }
4385
4386    #[test]
4387    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
4388        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
4389        // and one that gets a `*` hands it a list, without either of them being
4390        // told which command was sent.
4391        let mut f = Fixture::new();
4392        f.run(&[b"SADD", b"s", b"one"]);
4393        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
4394
4395        f.run(&[b"HELLO", b"3"]);
4396        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
4397    }
4398
4399    #[test]
4400    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
4401        // An intset holds the number, so these digits exist for the first time
4402        // in the reply buffer.
4403        let mut f = Fixture::new();
4404        f.run(&[b"SADD", b"s", b"42"]);
4405        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
4406        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
4407        assert_eq!(
4408            f.run(&[b"SISMEMBER", b"s", b"042"]),
4409            ":0\r\n",
4410            "the member is the bytes and not the number they parse to"
4411        );
4412    }
4413
4414    #[test]
4415    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
4416        let mut f = Fixture::new();
4417        f.run(&[b"SET", b"str", b"v"]);
4418        f.run(&[b"SADD", b"set", b"a"]);
4419
4420        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4421        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
4422        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
4423        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
4424        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
4425        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
4426        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
4427        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
4428        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
4429
4430        // MGET is the one that does not, because Redis gives nil for the odd
4431        // key out rather than failing the good keys next to it.
4432        assert_eq!(
4433            f.run(&[b"MGET", b"str", b"set", b"nope"]),
4434            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
4435        );
4436        // And plain SET overwrites any type, which takes the body with it.
4437        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
4438        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
4439    }
4440
4441    #[test]
4442    fn a_wrongtype_leaves_nothing_half_written() {
4443        // SMISMEMBER writes an array header and then one reply per member, so
4444        // it is the first command in the server that could get a header out in
4445        // front of an error if it checked its key in the wrong order.
4446        let mut f = Fixture::new();
4447        f.run(&[b"SET", b"k", b"v"]);
4448        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
4449        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
4450        assert!(!reply.contains('*'), "an array header went out in front");
4451    }
4452
4453    #[test]
4454    fn emptying_a_set_takes_the_key_with_it() {
4455        let mut f = Fixture::new();
4456        f.run(&[b"SADD", b"s", b"a", b"b"]);
4457        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
4458        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
4459        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4460        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
4461        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4462    }
4463
4464    /// Pull the cursor and the members out of one `SSCAN` reply.
4465    ///
4466    /// Crude on purpose. A test that walked a set through a real client would
4467    /// be testing the client, and what these tests are about is the shape of
4468    /// the bytes and the fact that a walk sees every member once.
4469    fn split_scan(reply: &str) -> (String, Vec<String>) {
4470        let mut lines = reply.split("\r\n");
4471        assert_eq!(lines.next(), Some("*2"), "got {reply}");
4472        lines.next().expect("the cursor header");
4473        let cursor = lines.next().expect("the cursor").to_owned();
4474        let header = lines.next().expect("the member header");
4475        let n: usize = header[1..].parse().expect("a member count");
4476        let mut members = Vec::with_capacity(n);
4477        for _ in 0..n {
4478            lines.next().expect("a member header");
4479            members.push(lines.next().expect("a member").to_owned());
4480        }
4481        (cursor, members)
4482    }
4483
4484    #[test]
4485    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
4486        let mut f = Fixture::new();
4487        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
4488
4489        let one = f.run(&[b"SPOP", b"s"]);
4490        assert!(
4491            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
4492            "got {one}"
4493        );
4494        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
4495
4496        // A count takes that many, and the last one takes the key with it.
4497        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
4498        assert!(rest.starts_with("*3\r\n"), "got {rest}");
4499        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
4500        // And a pop at a key that is not there is a nil, not an empty bulk.
4501        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
4502        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
4503    }
4504
4505    #[test]
4506    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
4507        // The one place in the server where the reply type carries something
4508        // the command name does not. SPOP's members are distinct so a RESP3
4509        // client can build a set out of them. SRANDMEMBER with a negative count
4510        // can hand back the same member three times, and a set would lose two.
4511        let mut f = Fixture::new();
4512        f.run(&[b"HELLO", b"3"]);
4513        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
4514
4515        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
4516        // And a positive count is an array too, since Redis makes it one.
4517        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
4518
4519        // A negative count against a set of one is where the difference bites:
4520        // the same member three times, which is a three element reply and would
4521        // have been a one element reply if it had gone out as a set.
4522        f.run(&[b"SADD", b"one", b"z"]);
4523        assert_eq!(
4524            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
4525            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
4526        );
4527    }
4528
4529    #[test]
4530    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
4531        let mut f = Fixture::new();
4532        f.run(&[b"SADD", b"s", b"only"]);
4533        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4534        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
4535        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
4536
4537        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
4538        // The count form answers an empty array rather than a nil, which is the
4539        // pair of answers Redis gives and is not the pair it looks like.
4540        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
4541        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
4542        // Asking for more than is there answers all of it once and not padding.
4543        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
4544    }
4545
4546    #[test]
4547    fn a_pop_count_that_is_not_a_positive_number_says_so() {
4548        let mut f = Fixture::new();
4549        f.run(&[b"SADD", b"s", b"a"]);
4550        let bad = "-ERR value is out of range, must be positive\r\n";
4551        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
4552        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
4553        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
4554        // Zero is allowed and is a real answer rather than an error.
4555        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
4556        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
4557    }
4558
4559    #[test]
4560    fn a_scan_walks_a_set_of_any_size_exactly_once() {
4561        let mut f = Fixture::new();
4562        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
4563        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
4564            .into_iter()
4565            .chain(members.iter().map(Vec::as_slice))
4566            .collect();
4567        f.run(&args);
4568
4569        let mut seen = Vec::new();
4570        let mut cursor = "0".to_owned();
4571        loop {
4572            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
4573            let (next, got) = split_scan(&reply);
4574            seen.extend(got);
4575            cursor = next;
4576            if cursor == "0" {
4577                break;
4578            }
4579        }
4580        seen.sort();
4581        seen.dedup();
4582        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
4583
4584        // A set small enough to be a listpack answers in one call whatever
4585        // cursor it was handed, which is what Redis does for that encoding.
4586        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
4587        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
4588        assert_eq!(cursor, "0");
4589        assert_eq!(got.len(), 3);
4590        // And a key that is not there is a finished scan of nothing.
4591        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
4592    }
4593
4594    #[test]
4595    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
4596        let mut f = Fixture::new();
4597        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
4598
4599        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
4600        let mut got = got;
4601        got.sort();
4602        assert_eq!(got, ["aa", "ab"]);
4603
4604        // An integer member has no digits stored anywhere, so MATCH is the one
4605        // place a scan pays to write some.
4606        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4607        let mut got = got;
4608        got.sort();
4609        assert_eq!(got, ["12", "13"]);
4610
4611        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4612        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4613        assert_eq!(
4614            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4615            "-ERR syntax error\r\n"
4616        );
4617        // A count under one is a syntax error and not a range error, which is
4618        // the odder of Redis's two answers and the reason it is copied exactly.
4619        assert_eq!(
4620            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4621            "-ERR syntax error\r\n"
4622        );
4623    }
4624
4625    #[test]
4626    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4627        let mut f = Fixture::new();
4628        f.run(&[b"SADD", b"src", b"a", b"b"]);
4629        f.run(&[b"SADD", b"dst", b"c"]);
4630
4631        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4632        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4633        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4634        // A member that is not in the source is a zero and moves nothing.
4635        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4636        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4637
4638        // A destination that does not exist gets made, and a source that runs
4639        // out goes away.
4640        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4641        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4642        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4643    }
4644
4645    #[test]
4646    fn moving_checks_the_types_in_the_order_redis_checks_them() {
4647        // Not the order it looks like it should be. A source that is not there
4648        // answers zero without ever looking at the destination, so this is a
4649        // zero and not a WRONGTYPE even though the destination is a string.
4650        let mut f = Fixture::new();
4651        f.run(&[b"SET", b"str", b"v"]);
4652        f.run(&[b"SADD", b"set", b"a"]);
4653
4654        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4655        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4656        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4657        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4658        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4659        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4660        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4661        assert_eq!(
4662            f.run(&[b"SISMEMBER", b"set", b"a"]),
4663            ":1\r\n",
4664            "and none of that moved anything"
4665        );
4666    }
4667
4668    #[test]
4669    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4670        // SSCAN writes an outer array header before it walks, so it is the
4671        // command most likely to get bytes out in front of an error.
4672        let mut f = Fixture::new();
4673        f.run(&[b"SADD", b"s", b"a"]);
4674        for bad in [
4675            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4676            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4677            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4678        ] {
4679            let reply = f.run(bad);
4680            assert!(reply.starts_with("-ERR"), "got {reply}");
4681            assert!(!reply.contains('*'), "an array header went out in front");
4682        }
4683    }
4684
4685    #[test]
4686    fn a_hash_writes_reads_and_deletes_its_fields() {
4687        let mut f = Fixture::new();
4688        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4689        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4690        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4691        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4692        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4693        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4694        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4695        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4696        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4697        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4698
4699        // The value the client sent is `9`, so HGET h b must not find the `2`
4700        // that is a value. A search with a step of one would have.
4701        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4702
4703        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4704        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4705        assert_eq!(
4706            f.run(&[b"EXISTS", b"h"]),
4707            ":0\r\n",
4708            "and losing the last field lost the key"
4709        );
4710    }
4711
4712    #[test]
4713    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4714        let mut f = Fixture::new();
4715        f.run(&[b"HSET", b"h", b"a", b"1"]);
4716        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4717        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4718        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4719        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4720        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4721
4722        f.run(&[b"HELLO", b"3"]);
4723        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4724        assert_eq!(
4725            f.run(&[b"HGETALL", b"nokey"]),
4726            "%0\r\n",
4727            "a missing key is the empty hash and never a nil"
4728        );
4729        assert_eq!(
4730            f.run(&[b"HKEYS", b"h"]),
4731            "*1\r\n$1\r\na\r\n",
4732            "and the two that answer one side stay arrays"
4733        );
4734    }
4735
4736    #[test]
4737    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4738        let mut f = Fixture::new();
4739        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4740        assert_eq!(
4741            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4742            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4743            "the reply is positional, so b is a nil and not a gap"
4744        );
4745        assert_eq!(
4746            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4747            "*2\r\n$-1\r\n$-1\r\n",
4748            "and a missing key is all nils rather than an empty array"
4749        );
4750
4751        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4752        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4753        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4754    }
4755
4756    #[test]
4757    fn a_hash_counts_up_and_says_so_when_it_cannot() {
4758        let mut f = Fixture::new();
4759        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4760        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4761        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4762        assert_eq!(
4763            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4764            "$4\r\n10.5\r\n",
4765            "a bulk string and not a double, on both protocols"
4766        );
4767
4768        f.run(&[b"HSET", b"h", b"s", b"words"]);
4769        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4770        assert!(
4771            bad.starts_with("-ERR hash value is not an integer"),
4772            "{bad}"
4773        );
4774        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4775        assert!(
4776            bad.starts_with("-ERR value is not an integer"),
4777            "a bad argument is not yet a hash value, {bad}"
4778        );
4779        assert_eq!(
4780            f.run(&[b"HGET", b"h", b"s"]),
4781            "$5\r\nwords\r\n",
4782            "and neither of them wrote anything"
4783        );
4784    }
4785
4786    #[test]
4787    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4788        let mut f = Fixture::new();
4789        for i in 0..500 {
4790            let field = format!("field-{i}");
4791            let value = format!("value-{i}");
4792            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4793        }
4794
4795        let mut seen: Vec<String> = Vec::new();
4796        let mut cursor = "0".to_owned();
4797        loop {
4798            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4799            let (next, items) = scan_reply(&reply);
4800            assert_eq!(items.len() % 2, 0, "a pair went out half written");
4801            for pair in items.chunks(2) {
4802                assert_eq!(
4803                    pair[0].strip_prefix("field-"),
4804                    pair[1].strip_prefix("value-"),
4805                    "a field came back with someone else's value"
4806                );
4807                seen.push(pair[0].clone());
4808            }
4809            cursor = next;
4810            if cursor == "0" {
4811                break;
4812            }
4813        }
4814        seen.sort();
4815        seen.dedup();
4816        assert_eq!(seen.len(), 500, "every field once and only once");
4817
4818        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4819        assert!(
4820            items.iter().all(|s| s.starts_with("field-")),
4821            "NOVALUES still sent the values"
4822        );
4823
4824        let (_, one) = scan_reply(&f.run(&[
4825            b"HSCAN",
4826            b"h",
4827            b"0",
4828            b"MATCH",
4829            b"field-499",
4830            b"COUNT",
4831            b"1000",
4832        ]));
4833        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4834    }
4835
4836    #[test]
4837    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4838        let mut f = Fixture::new();
4839        f.run(&[b"HSET", b"h", b"a", b"1"]);
4840        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4841        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4842        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4843        assert_eq!(
4844            f.run(&[b"HRANDFIELD", b"h", b"3"]),
4845            "*1\r\n$1\r\na\r\n",
4846            "a positive count is capped at the size of the hash"
4847        );
4848        assert_eq!(
4849            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4850            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4851            "and a negative one repeats itself"
4852        );
4853        assert_eq!(
4854            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4855            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4856            "flat on RESP2"
4857        );
4858
4859        f.run(&[b"HELLO", b"3"]);
4860        assert_eq!(
4861            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4862            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4863            "and nested on RESP3, but still an array and never a map"
4864        );
4865    }
4866
4867    #[test]
4868    fn every_hash_command_says_wrongtype_and_writes_nothing() {
4869        let mut f = Fixture::new();
4870        f.run(&[b"SET", b"str", b"v"]);
4871        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4872
4873        for cmd in [
4874            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
4875            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
4876            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
4877            &[b"HGET".as_slice(), b"str", b"f"][..],
4878            &[b"HMGET".as_slice(), b"str", b"f"][..],
4879            &[b"HDEL".as_slice(), b"str", b"f"][..],
4880            &[b"HLEN".as_slice(), b"str"][..],
4881            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
4882            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
4883            &[b"HGETALL".as_slice(), b"str"][..],
4884            &[b"HKEYS".as_slice(), b"str"][..],
4885            &[b"HVALS".as_slice(), b"str"][..],
4886            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
4887            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
4888            &[b"HRANDFIELD".as_slice(), b"str"][..],
4889            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
4890            &[b"HSCAN".as_slice(), b"str", b"0"][..],
4891        ] {
4892            let reply = f.run(cmd);
4893            assert_eq!(reply, wrong, "{:?}", cmd[0]);
4894        }
4895        assert_eq!(
4896            f.run(&[b"GET", b"str"]),
4897            "$1\r\nv\r\n",
4898            "and none of them touched the value"
4899        );
4900    }
4901
4902    #[test]
4903    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4904        let mut f = Fixture::new();
4905        f.run(&[b"HSET", b"h", b"f", b"v"]);
4906        for bad in [
4907            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
4908            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
4909            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
4910            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
4911        ] {
4912            let reply = f.run(bad);
4913            assert!(reply.starts_with("-ERR"), "got {reply}");
4914            assert!(!reply.contains('*'), "an array header went out in front");
4915        }
4916    }
4917
4918    #[test]
4919    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
4920        let mut f = Fixture::new();
4921        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4922        assert_eq!(
4923            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
4924            "*1\r\n:1\r\n"
4925        );
4926        assert_eq!(
4927            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4928            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
4929            "one answer per field, and the two sentinels are TTL's own"
4930        );
4931
4932        // The same deadline in the other three units, all of them derived from
4933        // the one number the store kept.
4934        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
4935        assert!((99_000..=100_000).contains(&ms), "got {ms}");
4936        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4937        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4938        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
4939        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
4940
4941        assert_eq!(
4942            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4943            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
4944            "one for the deadline taken off, and it does not say what it was"
4945        );
4946        assert_eq!(
4947            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4948            "*1\r\n:-1\r\n"
4949        );
4950        assert_eq!(
4951            f.run(&[b"HGET", b"h", b"a"]),
4952            "$1\r\n1\r\n",
4953            "and the field is still there with the value it had"
4954        );
4955    }
4956
4957    #[test]
4958    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
4959        let mut f = Fixture::new();
4960        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4961        assert_eq!(
4962            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
4963            "*1\r\n:2\r\n",
4964            "two, and not one, because nothing was stored"
4965        );
4966        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
4967        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4968
4969        assert_eq!(
4970            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
4971            "*1\r\n:2\r\n"
4972        );
4973        assert_eq!(
4974            f.run(&[b"EXISTS", b"h"]),
4975            ":0\r\n",
4976            "and the last field going took the key with it"
4977        );
4978
4979        // Zero is a delete and not an error, where minus one is an error. That
4980        // is Redis's split and it is easy to get backwards.
4981        f.run(&[b"HSET", b"h", b"a", b"1"]);
4982        assert_eq!(
4983            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
4984            "*1\r\n:2\r\n"
4985        );
4986    }
4987
4988    #[test]
4989    fn a_field_is_gone_once_its_moment_passes() {
4990        let mut f = Fixture::new();
4991        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4992        assert_eq!(
4993            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
4994            "*1\r\n:1\r\n"
4995        );
4996        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
4997
4998        // Time moves once per turn of the event loop and nowhere else, so a
4999        // test moves it by hand rather than by sleeping. There is nothing to
5000        // sleep for: the deadline is a number and so is the clock.
5001        f.server.db(0).clock_mut().advance(60);
5002        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5003        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
5004        assert_eq!(
5005            f.run(&[b"HGETALL", b"h"]),
5006            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
5007            "and the walks do not hand back a field that has expired"
5008        );
5009    }
5010
5011    #[test]
5012    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
5013        let mut f = Fixture::new();
5014        for cmd in [
5015            &[
5016                b"HEXPIRE".as_slice(),
5017                b"nokey",
5018                b"100",
5019                b"FIELDS",
5020                b"2",
5021                b"a",
5022                b"b",
5023            ][..],
5024            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5025            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
5026            &[
5027                b"HEXPIRETIME".as_slice(),
5028                b"nokey",
5029                b"FIELDS",
5030                b"2",
5031                b"a",
5032                b"b",
5033            ][..],
5034            &[
5035                b"HPERSIST".as_slice(),
5036                b"nokey",
5037                b"FIELDS",
5038                b"2",
5039                b"a",
5040                b"b",
5041            ][..],
5042        ] {
5043            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
5044        }
5045    }
5046
5047    #[test]
5048    fn writing_a_field_clears_the_deadline_that_was_on_it() {
5049        let mut f = Fixture::new();
5050        f.run(&[b"HSET", b"h", b"a", b"1"]);
5051        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
5052        f.run(&[b"HSET", b"h", b"a", b"2"]);
5053        assert_eq!(
5054            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5055            "*1\r\n:-1\r\n",
5056            "Redis has done this since 7.4, and it is why HGETEX exists"
5057        );
5058    }
5059
5060    #[test]
5061    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
5062        let mut f = Fixture::new();
5063        f.run(&[b"HSET", b"h", b"a", b"1"]);
5064        assert_eq!(
5065            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
5066            "*1\r\n:0\r\n",
5067            "XX on a field with no deadline changes nothing"
5068        );
5069        assert_eq!(
5070            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
5071            "*1\r\n:1\r\n"
5072        );
5073        assert_eq!(
5074            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
5075            "*1\r\n:0\r\n",
5076            "and NX will not move one that is already there"
5077        );
5078        assert_eq!(
5079            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
5080            "*1\r\n:0\r\n"
5081        );
5082        assert_eq!(
5083            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
5084            "*1\r\n:1\r\n"
5085        );
5086        assert_eq!(
5087            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
5088            "*1\r\n:1\r\n"
5089        );
5090        assert_eq!(
5091            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5092            "*1\r\n:50\r\n"
5093        );
5094    }
5095
5096    #[test]
5097    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
5098        let mut f = Fixture::new();
5099        f.run(&[b"HSET", b"h", b"a", b"1"]);
5100        for (bad, want) in [
5101            (
5102                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
5103                "-ERR invalid expire time, must be >= 0",
5104            ),
5105            (
5106                &[
5107                    b"HEXPIRE".as_slice(),
5108                    b"h",
5109                    b"9999999999999999",
5110                    b"FIELDS",
5111                    b"1",
5112                    b"a",
5113                ][..],
5114                "-ERR invalid expire time in 'hexpire' command",
5115            ),
5116            (
5117                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
5118                "-ERR wrong number of arguments for 'hexpire' command",
5119            ),
5120            (
5121                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
5122                "-ERR Parameter `numFields` should be greater than 0",
5123            ),
5124            (
5125                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
5126                "-ERR wrong number of arguments",
5127            ),
5128            (
5129                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
5130                "-ERR wrong number of arguments",
5131            ),
5132        ] {
5133            let reply = f.run(bad);
5134            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5135            assert!(!reply.contains('*'), "an array header went out in front");
5136        }
5137        assert_eq!(
5138            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5139            "*1\r\n:-1\r\n",
5140            "and not one of them put a deadline on anything"
5141        );
5142    }
5143
5144    #[test]
5145    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
5146        let mut f = Fixture::new();
5147        f.run(&[b"SET", b"str", b"v"]);
5148        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5149
5150        for cmd in [
5151            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
5152            &[
5153                b"HPEXPIRE".as_slice(),
5154                b"str",
5155                b"100",
5156                b"FIELDS",
5157                b"1",
5158                b"f",
5159            ][..],
5160            &[
5161                b"HEXPIREAT".as_slice(),
5162                b"str",
5163                b"9999999999",
5164                b"FIELDS",
5165                b"1",
5166                b"f",
5167            ][..],
5168            &[
5169                b"HPEXPIREAT".as_slice(),
5170                b"str",
5171                b"9999999999999",
5172                b"FIELDS",
5173                b"1",
5174                b"f",
5175            ][..],
5176            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5177            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5178            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5179            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5180            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5181        ] {
5182            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5183        }
5184        assert_eq!(
5185            f.run(&[b"GET", b"str"]),
5186            "$1\r\nv\r\n",
5187            "and none of them touched the value"
5188        );
5189    }
5190
5191    #[test]
5192    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
5193        let mut f = Fixture::new();
5194        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
5195        assert_eq!(
5196            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
5197            "*2\r\n$1\r\n1\r\n$-1\r\n",
5198            "positional, so the field that was not there is a nil in its place"
5199        );
5200        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
5201        assert_eq!(
5202            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
5203            "*1\r\n$-1\r\n"
5204        );
5205        assert_eq!(
5206            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
5207            "*1\r\n$1\r\n2\r\n"
5208        );
5209        assert_eq!(
5210            f.run(&[b"EXISTS", b"h"]),
5211            ":0\r\n",
5212            "and the last field took the key"
5213        );
5214    }
5215
5216    #[test]
5217    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
5218        let mut f = Fixture::new();
5219        f.run(&[b"HSET", b"h", b"a", b"1"]);
5220        assert_eq!(
5221            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
5222            "*1\r\n$1\r\n1\r\n"
5223        );
5224        assert_eq!(
5225            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5226            "*1\r\n:-1\r\n",
5227            "no option means leave it alone, which is the one place this is not GETEX"
5228        );
5229
5230        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
5231        assert_eq!(
5232            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5233            "*1\r\n:100\r\n"
5234        );
5235        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
5236        assert_eq!(
5237            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5238            "*1\r\n:100\r\n",
5239            "and a plain read really does leave it alone"
5240        );
5241        assert_eq!(
5242            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
5243            "*1\r\n$1\r\n1\r\n"
5244        );
5245        assert_eq!(
5246            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5247            "*1\r\n:-1\r\n"
5248        );
5249
5250        assert_eq!(
5251            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
5252            "*1\r\n$1\r\n1\r\n",
5253            "the value goes out before the deadline that has already gone is applied"
5254        );
5255        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
5256        assert_eq!(
5257            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
5258            "*1\r\n$-1\r\n"
5259        );
5260    }
5261
5262    #[test]
5263    fn hsetex_writes_all_of_it_or_none_of_it() {
5264        let mut f = Fixture::new();
5265        assert_eq!(
5266            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
5267            ":1\r\n"
5268        );
5269        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5270        assert_eq!(
5271            f.run(&[
5272                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
5273            ]),
5274            ":0\r\n",
5275            "FNX wants every field named to be missing"
5276        );
5277        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5278        assert_eq!(
5279            f.run(&[b"HEXISTS", b"h", b"new"]),
5280            ":0\r\n",
5281            "and none of the list was written"
5282        );
5283        assert_eq!(
5284            f.run(&[
5285                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
5286            ]),
5287            ":0\r\n",
5288            "and FXX wants every one of them to be there"
5289        );
5290        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
5291        assert_eq!(
5292            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
5293            ":1\r\n"
5294        );
5295        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
5296
5297        assert_eq!(
5298            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
5299            ":0\r\n"
5300        );
5301        assert_eq!(
5302            f.run(&[b"EXISTS", b"gone"]),
5303            ":0\r\n",
5304            "a key with no fields cannot meet FXX and is not created trying"
5305        );
5306    }
5307
5308    #[test]
5309    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
5310        let mut f = Fixture::new();
5311        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
5312        assert_eq!(
5313            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5314            "*1\r\n:100\r\n"
5315        );
5316
5317        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
5318        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
5319        assert_eq!(
5320            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5321            "*1\r\n:100\r\n",
5322            "KEEPTTL put back what the write cleared"
5323        );
5324
5325        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
5326        assert_eq!(
5327            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5328            "*1\r\n:-1\r\n",
5329            "and without it a write clears the deadline the way HSET does"
5330        );
5331
5332        // Any order, because Redis reads these in a loop and not in a fixed
5333        // sequence.
5334        assert_eq!(
5335            f.run(&[
5336                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
5337            ]),
5338            ":1\r\n"
5339        );
5340        assert_eq!(
5341            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5342            "*1\r\n:100\r\n"
5343        );
5344
5345        assert_eq!(
5346            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
5347            ":1\r\n",
5348            "written, and not the separate code the HEXPIRE family has for this"
5349        );
5350        assert_eq!(
5351            f.run(&[b"EXISTS", b"h"]),
5352            ":0\r\n",
5353            "and storing it and then removing it emptied the hash"
5354        );
5355    }
5356
5357    #[test]
5358    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
5359        let mut f = Fixture::new();
5360        f.run(&[b"HSET", b"h", b"a", b"1"]);
5361        for (bad, want) in [
5362            // HGETDEL has three sentences of its own for these three mistakes.
5363            (
5364                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5365                "-ERR Number of fields must be a positive integer",
5366            ),
5367            (
5368                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5369                "-ERR The `numfields` parameter must match the number of arguments",
5370            ),
5371            (
5372                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5373                "-ERR Mandatory argument FIELDS is missing or not at the right position",
5374            ),
5375            // And HGETEX and HSETEX have three different ones between them.
5376            (
5377                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
5378                "-ERR invalid number of fields",
5379            ),
5380            (
5381                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
5382                "-ERR wrong number of arguments",
5383            ),
5384            (
5385                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
5386                "-ERR unknown argument: FIELD",
5387            ),
5388            (
5389                &[
5390                    b"HGETEX".as_slice(),
5391                    b"h",
5392                    b"KEEPTTL",
5393                    b"FIELDS",
5394                    b"1",
5395                    b"a",
5396                ][..],
5397                "-ERR unknown argument: KEEPTTL",
5398            ),
5399            (
5400                &[
5401                    b"HGETEX".as_slice(),
5402                    b"h",
5403                    b"EX",
5404                    b"100",
5405                    b"PERSIST",
5406                    b"FIELDS",
5407                    b"1",
5408                    b"a",
5409                ][..],
5410                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
5411            ),
5412            (
5413                &[
5414                    b"HSETEX".as_slice(),
5415                    b"h",
5416                    b"EX",
5417                    b"1",
5418                    b"KEEPTTL",
5419                    b"FIELDS",
5420                    b"1",
5421                    b"a",
5422                    b"1",
5423                ][..],
5424                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
5425            ),
5426            (
5427                &[
5428                    b"HSETEX".as_slice(),
5429                    b"h",
5430                    b"FNX",
5431                    b"FXX",
5432                    b"FIELDS",
5433                    b"1",
5434                    b"a",
5435                    b"1",
5436                ][..],
5437                "-ERR Only one of FXX or FNX arguments can be specified",
5438            ),
5439            (
5440                &[
5441                    b"HSETEX".as_slice(),
5442                    b"h",
5443                    b"FIELDS",
5444                    b"2",
5445                    b"a",
5446                    b"1",
5447                    b"b",
5448                ][..],
5449                "-ERR wrong number of arguments",
5450            ),
5451            (
5452                &[
5453                    b"HGETEX".as_slice(),
5454                    b"h",
5455                    b"EX",
5456                    b"-1",
5457                    b"FIELDS",
5458                    b"1",
5459                    b"a",
5460                ][..],
5461                "-ERR invalid expire time, must be >= 0",
5462            ),
5463            (
5464                &[
5465                    b"HGETEX".as_slice(),
5466                    b"h",
5467                    b"PXAT",
5468                    b"99999999999999",
5469                    b"FIELDS",
5470                    b"1",
5471                    b"a",
5472                ][..],
5473                "-ERR invalid expire time in 'hgetex' command",
5474            ),
5475            (
5476                &[
5477                    b"HSETEX".as_slice(),
5478                    b"h",
5479                    b"EX",
5480                    b"abc",
5481                    b"FIELDS",
5482                    b"1",
5483                    b"a",
5484                    b"1",
5485                ][..],
5486                "-ERR value is not an integer or out of range",
5487            ),
5488        ] {
5489            let reply = f.run(bad);
5490            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
5491            assert!(!reply.contains('*'), "an array header went out in front");
5492        }
5493        assert_eq!(
5494            f.run(&[b"HGET", b"h", b"a"]),
5495            "$1\r\n1\r\n",
5496            "and not one of them wrote anything"
5497        );
5498        assert_eq!(
5499            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
5500            "*1\r\n:-1\r\n"
5501        );
5502    }
5503
5504    #[test]
5505    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
5506        let mut f = Fixture::new();
5507        f.run(&[b"SET", b"str", b"v"]);
5508        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5509        for cmd in [
5510            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5511            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
5512            &[
5513                b"HGETEX".as_slice(),
5514                b"str",
5515                b"EX",
5516                b"100",
5517                b"FIELDS",
5518                b"1",
5519                b"f",
5520            ][..],
5521            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
5522        ] {
5523            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5524        }
5525        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5526    }
5527
5528    /// The two orders `HIMPORT` juggles, which are not the same order.
5529    ///
5530    /// Values arrive in the order the fields were declared in and the hash is
5531    /// built in sorted order, so the first value is not generally the first
5532    /// field. And the sort is by length before bytes, which nothing else here
5533    /// sorts names with: `b` comes before `aa` where a plain byte comparison
5534    /// would put `aa` first. Both read off 8.10.1.
5535    #[test]
5536    fn himport_writes_declared_values_into_sorted_fields() {
5537        let mut f = Fixture::new();
5538        assert_eq!(
5539            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"b", b"aa", b"a"]),
5540            "+OK\r\n"
5541        );
5542        assert_eq!(
5543            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2", b"3"]),
5544            "+OK\r\n"
5545        );
5546        assert_eq!(f.run(&[b"HKEYS", b"k"]), bulks(&["a", "b", "aa"]));
5547        assert_eq!(
5548            f.run(&[b"HGETALL", b"k"]),
5549            bulks(&["a", "3", "b", "1", "aa", "2"])
5550        );
5551    }
5552
5553    /// It replaces the key rather than writing over it, so a field the fieldset
5554    /// does not name is gone afterwards and so is the deadline.
5555    #[test]
5556    fn himport_set_replaces_the_whole_key() {
5557        let mut f = Fixture::new();
5558        f.run(&[b"HSET", b"k", b"gone", b"old", b"a", b"old"]);
5559        f.run(&[b"EXPIRE", b"k", b"100"]);
5560        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5561        assert_eq!(
5562            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5563            "+OK\r\n"
5564        );
5565        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5566        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
5567    }
5568
5569    /// A fieldset is connection state. `SELECT` leaves them alone and `RESET`
5570    /// throws them away, and a key built from one outlives it.
5571    #[test]
5572    fn himport_fieldsets_belong_to_the_connection_and_not_to_the_keyspace() {
5573        let mut f = Fixture::new();
5574        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a"]);
5575        f.run(&[b"SELECT", b"1"]);
5576        assert_eq!(
5577            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5578            "+OK\r\n"
5579        );
5580        f.run(&[b"SELECT", b"0"]);
5581        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
5582        assert_eq!(
5583            f.run(&[b"HIMPORT", b"SET", b"k2", b"shape", b"1"]),
5584            "-ERR no such fieldset\r\n"
5585        );
5586    }
5587
5588    /// Which complaint wins when a line is wrong in more than one place.
5589    ///
5590    /// The type of the key beats both of the others, so a `HIMPORT SET` against
5591    /// a string is a WRONGTYPE even when the fieldset is missing too, which is
5592    /// the ordering a real server has and not the one the argument order
5593    /// suggests.
5594    #[test]
5595    fn himport_complains_in_the_order_a_real_server_does() {
5596        let mut f = Fixture::new();
5597        f.run(&[b"SET", b"str", b"v"]);
5598        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5599        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5600        assert_eq!(
5601            f.run(&[b"HIMPORT", b"SET", b"str", b"nope", b"1"]),
5602            wrong,
5603            "the type beats a missing fieldset"
5604        );
5605        assert_eq!(
5606            f.run(&[b"HIMPORT", b"SET", b"str", b"shape", b"1"]),
5607            wrong,
5608            "and it beats a value count that does not fit"
5609        );
5610        assert_eq!(
5611            f.run(&[b"HIMPORT", b"SET", b"k", b"nope", b"1"]),
5612            "-ERR no such fieldset\r\n"
5613        );
5614        // One sentence for too few and for too many alike.
5615        for values in [&[b"1".as_slice()][..], &[b"1".as_slice(), b"2", b"3"][..]] {
5616            let mut line: Vec<&[u8]> = vec![b"HIMPORT", b"SET", b"k", b"shape"];
5617            line.extend_from_slice(values);
5618            assert_eq!(
5619                f.run(&line),
5620                "-ERR value count does not match fieldset field count\r\n",
5621                "{} values into two fields",
5622                values.len()
5623            );
5624        }
5625        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5626    }
5627
5628    /// The arity of each subcommand, and the unknown one.
5629    #[test]
5630    fn himport_checks_each_subcommand_count_under_its_own_name() {
5631        let mut f = Fixture::new();
5632        assert_eq!(
5633            f.run(&[b"HIMPORT"]),
5634            "-ERR wrong number of arguments for 'himport' command\r\n"
5635        );
5636        for (rest, name) in [
5637            (&["PREPARE"][..], "prepare"),
5638            (&["PREPARE", "fs"][..], "prepare"),
5639            (&["SET"][..], "set"),
5640            (&["SET", "k"][..], "set"),
5641            (&["SET", "k", "fs"][..], "set"),
5642            (&["DISCARD"][..], "discard"),
5643            (&["DISCARD", "a", "b"][..], "discard"),
5644            (&["DISCARDALL", "x"][..], "discardall"),
5645        ] {
5646            let mut line: Vec<&[u8]> = vec![b"HIMPORT"];
5647            line.extend(rest.iter().map(|a| a.as_bytes()));
5648            assert_eq!(
5649                f.run(&line),
5650                format!("-ERR wrong number of arguments for 'himport|{name}' command\r\n"),
5651                "HIMPORT {}",
5652                rest.join(" ")
5653            );
5654        }
5655        assert_eq!(
5656            f.run(&[b"HIMPORT", b"NOPE", b"x"]),
5657            "-ERR unknown subcommand 'NOPE'. Try HIMPORT HELP.\r\n"
5658        );
5659    }
5660
5661    /// A `PREPARE` that fails leaves the name pointing where it pointed, which
5662    /// is the answer of the two that could not be guessed from outside.
5663    #[test]
5664    fn a_failed_himport_prepare_leaves_the_old_fieldset_alone() {
5665        let mut f = Fixture::new();
5666        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5667        assert_eq!(
5668            f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"c", b"c"]),
5669            "-ERR duplicate field name in fieldset\r\n"
5670        );
5671        assert_eq!(
5672            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1", b"2"]),
5673            "+OK\r\n"
5674        );
5675        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["a", "1", "b", "2"]));
5676    }
5677
5678    /// Preparing the same name twice replaces it, and the two discards count
5679    /// what they took rather than answering OK.
5680    #[test]
5681    fn himport_prepare_replaces_and_the_discards_count() {
5682        let mut f = Fixture::new();
5683        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"a", b"b"]);
5684        f.run(&[b"HIMPORT", b"PREPARE", b"shape", b"z"]);
5685        assert_eq!(
5686            f.run(&[b"HIMPORT", b"SET", b"k", b"shape", b"1"]),
5687            "+OK\r\n"
5688        );
5689        assert_eq!(f.run(&[b"HGETALL", b"k"]), bulks(&["z", "1"]));
5690
5691        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":1\r\n");
5692        assert_eq!(f.run(&[b"HIMPORT", b"DISCARD", b"shape"]), ":0\r\n");
5693        f.run(&[b"HIMPORT", b"PREPARE", b"one", b"a"]);
5694        f.run(&[b"HIMPORT", b"PREPARE", b"two", b"a"]);
5695        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":2\r\n");
5696        assert_eq!(f.run(&[b"HIMPORT", b"DISCARDALL"]), ":0\r\n");
5697    }
5698
5699    /// The one integer of a single element array reply.
5700    /// The number out of a plain integer reply.
5701    ///
5702    /// [`int_reply`] is the same thing wrapped in a one element array, which is
5703    /// the shape every hash field command answers in.
5704    fn int(reply: &str) -> i64 {
5705        let body = reply
5706            .strip_prefix(':')
5707            .and_then(|s| s.strip_suffix("\r\n"))
5708            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
5709        body.parse().expect("an integer")
5710    }
5711
5712    fn int_reply(reply: &str) -> i64 {
5713        let body = reply
5714            .strip_prefix("*1\r\n:")
5715            .and_then(|s| s.strip_suffix("\r\n"))
5716            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
5717        body.parse().expect("an integer")
5718    }
5719
5720    /// The cursor and the flat items of a scan reply.
5721    fn scan_reply(reply: &str) -> (String, Vec<String>) {
5722        let mut lines = reply.split("\r\n");
5723        assert_eq!(lines.next(), Some("*2"), "got {reply}");
5724        lines.next().expect("the cursor header");
5725        let cursor = lines.next().expect("a cursor").to_owned();
5726        let header = lines.next().expect("an item count");
5727        let n: usize = header[1..].parse().expect("a count");
5728        let mut items = Vec::with_capacity(n);
5729        for _ in 0..n {
5730            lines.next().expect("an item header");
5731            items.push(lines.next().expect("an item").to_owned());
5732        }
5733        (cursor, items)
5734    }
5735
5736    /// The members of a set reply, sorted, since none of these promise an
5737    /// order and a test that asserted one would be asserting an accident.
5738    fn sorted(reply: &str) -> Vec<String> {
5739        let mut lines = reply.split("\r\n");
5740        let header = lines.next().expect("a header");
5741        assert!(
5742            header.starts_with('*') || header.starts_with('~'),
5743            "got {reply}"
5744        );
5745        let n: usize = header[1..].parse().expect("a member count");
5746        let mut got = Vec::with_capacity(n);
5747        for _ in 0..n {
5748            lines.next().expect("a member header");
5749            got.push(lines.next().expect("a member").to_owned());
5750        }
5751        got.sort();
5752        got
5753    }
5754
5755    #[test]
5756    fn the_algebra_answers_what_the_sets_share_and_do_not() {
5757        let mut f = Fixture::new();
5758        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5759        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5760        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
5761
5762        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
5763        assert_eq!(
5764            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
5765            ["1", "2", "3", "4", "5"]
5766        );
5767        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
5768        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
5769
5770        // A key that is not there is an empty set, which empties an
5771        // intersection and does nothing at all to a union.
5772        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
5773        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5774        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5775        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5776    }
5777
5778    #[test]
5779    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5780        let mut f = Fixture::new();
5781        f.run(&[b"SADD", b"a", b"x"]);
5782        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5783        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5784        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5785
5786        f.run(&[b"HELLO", b"3"]);
5787        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5788        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5789        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5790        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5791    }
5792
5793    #[test]
5794    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5795        let mut f = Fixture::new();
5796        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5797        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5798
5799        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5800        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5801        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5802        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5803        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5804        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5805
5806        // An empty answer deletes the destination rather than leaving an empty
5807        // set behind, and the destination may be one of the sources.
5808        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5809        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5810        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5811        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5812
5813        // And a destination holding something else is overwritten, the same way
5814        // SET overwrites, rather than refused.
5815        f.run(&[b"SET", b"str", b"v"]);
5816        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5817        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5818    }
5819
5820    #[test]
5821    fn sintercard_counts_without_building_and_stops_at_a_limit() {
5822        let mut f = Fixture::new();
5823        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5824        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5825
5826        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5827        assert_eq!(
5828            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5829            ":2\r\n"
5830        );
5831        assert_eq!(
5832            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5833            ":3\r\n",
5834            "a limit of zero is no limit"
5835        );
5836        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5837        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5838
5839        // The counted keys are what make its three error messages its own.
5840        assert_eq!(
5841            f.run(&[b"SINTERCARD", b"0", b"a"]),
5842            "-ERR numkeys should be greater than 0\r\n"
5843        );
5844        assert_eq!(
5845            f.run(&[b"SINTERCARD", b"abc", b"a"]),
5846            "-ERR numkeys should be greater than 0\r\n"
5847        );
5848        assert_eq!(
5849            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5850            "-ERR Number of keys can't be greater than number of args\r\n"
5851        );
5852        assert_eq!(
5853            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5854            "-ERR LIMIT can't be negative\r\n"
5855        );
5856        assert_eq!(
5857            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5858            "-ERR syntax error\r\n"
5859        );
5860        // A key really can be called LIMIT, which is why the count exists.
5861        f.run(&[b"SADD", b"LIMIT", b"2"]);
5862        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5863    }
5864
5865    /// The two Redis 8.10 added, which are SINTERCARD's shape over a union and
5866    /// over a difference. Every number here was read off 8.10.1 first.
5867    #[test]
5868    fn sunioncard_and_sdiffcard_count_without_building() {
5869        let mut f = Fixture::new();
5870        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5871        f.run(&[b"SADD", b"b", b"3", b"4", b"5", b"6"]);
5872
5873        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"b"]), ":6\r\n");
5874        assert_eq!(
5875            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5876            ":2\r\n"
5877        );
5878        assert_eq!(
5879            f.run(&[b"SUNIONCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5880            ":6\r\n",
5881            "a limit of zero is no limit"
5882        );
5883        assert_eq!(f.run(&[b"SUNIONCARD", b"1", b"a"]), ":4\r\n");
5884        assert_eq!(
5885            f.run(&[b"SUNIONCARD", b"2", b"a", b"nope"]),
5886            ":4\r\n",
5887            "a missing key adds nothing to a union"
5888        );
5889
5890        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"b"]), ":2\r\n");
5891        assert_eq!(
5892            f.run(&[b"SDIFFCARD", b"2", b"a", b"b", b"LIMIT", b"1"]),
5893            ":1\r\n"
5894        );
5895        assert_eq!(
5896            f.run(&[b"SDIFFCARD", b"2", b"b", b"a"]),
5897            ":2\r\n",
5898            "a difference is not symmetric"
5899        );
5900        assert_eq!(f.run(&[b"SDIFFCARD", b"1", b"a"]), ":4\r\n");
5901        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"nope"]), ":4\r\n");
5902        assert_eq!(
5903            f.run(&[b"SDIFFCARD", b"2", b"nope", b"a"]),
5904            ":0\r\n",
5905            "nothing taken away from nothing"
5906        );
5907
5908        // The same three messages SINTERCARD has, because the line is the same
5909        // line and is parsed once for all three.
5910        for name in [b"SUNIONCARD".as_slice(), b"SDIFFCARD".as_slice()] {
5911            assert_eq!(
5912                f.run(&[name, b"0", b"a"]),
5913                "-ERR numkeys should be greater than 0\r\n"
5914            );
5915            assert_eq!(
5916                f.run(&[name, b"abc", b"a"]),
5917                "-ERR numkeys should be greater than 0\r\n"
5918            );
5919            assert_eq!(
5920                f.run(&[name, b"-1", b"a"]),
5921                "-ERR numkeys should be greater than 0\r\n"
5922            );
5923            assert_eq!(
5924                f.run(&[name, b"3", b"a", b"b"]),
5925                "-ERR Number of keys can't be greater than number of args\r\n"
5926            );
5927            assert_eq!(
5928                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"-1"]),
5929                "-ERR LIMIT can't be negative\r\n"
5930            );
5931            assert_eq!(
5932                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"abc"]),
5933                "-ERR LIMIT can't be negative\r\n",
5934                "a LIMIT that is not a number gets the negative message too"
5935            );
5936            assert_eq!(
5937                f.run(&[name, b"2", b"a", b"b", b"NOPE", b"1"]),
5938                "-ERR syntax error\r\n"
5939            );
5940            assert_eq!(
5941                f.run(&[name, b"2", b"a", b"b", b"LIMIT"]),
5942                "-ERR syntax error\r\n"
5943            );
5944            assert_eq!(
5945                f.run(&[name, b"2", b"a", b"b", b"LIMIT", b"1", b"X"]),
5946                "-ERR syntax error\r\n"
5947            );
5948        }
5949
5950        // And a key called LIMIT is a key, here as much as on SINTERCARD.
5951        f.run(&[b"SADD", b"LIMIT", b"2"]);
5952        assert_eq!(f.run(&[b"SUNIONCARD", b"2", b"a", b"LIMIT"]), ":4\r\n");
5953        assert_eq!(f.run(&[b"SDIFFCARD", b"2", b"a", b"LIMIT"]), ":3\r\n");
5954    }
5955
5956    #[test]
5957    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
5958        let mut f = Fixture::new();
5959        f.run(&[b"SADD", b"a", b"1"]);
5960        f.run(&[b"SADD", b"d", b"old"]);
5961        f.run(&[b"SET", b"str", b"v"]);
5962
5963        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5964        for bad in [
5965            &[b"SINTER".as_slice(), b"a", b"str"][..],
5966            &[b"SUNION".as_slice(), b"str"][..],
5967            &[b"SDIFF".as_slice(), b"a", b"str"][..],
5968            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
5969            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
5970            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
5971            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
5972        ] {
5973            let reply = f.run(bad);
5974            assert_eq!(reply, wrong, "for {:?}", bad[0]);
5975        }
5976        assert_eq!(
5977            f.run(&[b"SMEMBERS", b"d"]),
5978            "*1\r\n$3\r\nold\r\n",
5979            "and the destination was left alone every time"
5980        );
5981    }
5982
5983    /// The leak a set can spring that nothing on the wire would ever show: the
5984    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
5985    #[test]
5986    fn churning_sets_does_not_grow_the_server() {
5987        let mut f = Fixture::new();
5988        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
5989        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
5990            .chain(std::iter::once(&b"s"[..]))
5991            .chain(members.iter().map(Vec::as_slice))
5992            .collect();
5993
5994        f.run(&args);
5995        f.run(&[b"DEL", b"s"]);
5996        f.server.compact_step();
5997        let after_first = f.server.memory_bytes();
5998
5999        for _ in 0..200 {
6000            f.run(&args);
6001            f.run(&[b"DEL", b"s"]);
6002            f.server.compact_step();
6003        }
6004        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6005        assert!(
6006            f.server.memory_bytes() <= after_first * 2,
6007            "held {} after two hundred passes against {after_first} after one",
6008            f.server.memory_bytes()
6009        );
6010    }
6011
6012    // --------------------------------------------------------------- bitmaps
6013
6014    /// The two single bit commands, and the encoding rule underneath them.
6015    ///
6016    /// A write always leaves the value `raw` and a read never re-encodes, which
6017    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
6018    /// with its first digit changed after a `SETBIT`.
6019    #[test]
6020    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
6021        let mut f = Fixture::new();
6022        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
6023        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
6024        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
6025        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
6026        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
6027        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
6028
6029        // Writing a nought past the end still creates the key and still pads.
6030        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
6031        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
6032        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
6033
6034        f.run(&[b"SET", b"num", b"12345"]);
6035        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
6036        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
6037        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
6038        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
6039        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
6040    }
6041
6042    /// Counting, in bytes and in bits.
6043    ///
6044    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
6045    /// says 22 for it. The server is the thing being copied here.
6046    #[test]
6047    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
6048        let mut f = Fixture::new();
6049        f.run(&[b"SET", b"mykey", b"foobar"]);
6050        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
6051        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
6052        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
6053        assert_eq!(
6054            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
6055            ":6\r\n"
6056        );
6057        assert_eq!(
6058            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
6059            ":25\r\n"
6060        );
6061        assert_eq!(
6062            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
6063            ":17\r\n"
6064        );
6065        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
6066
6067        // A start past the end is left where it is and the end is pulled back,
6068        // so the range comes out backwards and counts nothing.
6069        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
6070
6071        // A lone start is a syntax error here, where BITPOS allows it.
6072        assert_eq!(
6073            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
6074            "-ERR syntax error\r\n"
6075        );
6076        assert_eq!(
6077            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
6078            "-ERR syntax error\r\n"
6079        );
6080    }
6081
6082    /// Searching, and the one place a miss is not minus one.
6083    ///
6084    /// A search for a nought that runs to the end of the string answers the
6085    /// length in bits, because the string is treated as if it had noughts after
6086    /// it forever. Give it an explicit end and it answers minus one instead.
6087    #[test]
6088    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
6089        let mut f = Fixture::new();
6090        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
6091        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
6092        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
6093        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
6094        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
6095        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
6096
6097        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
6098        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
6099        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
6100        assert_eq!(
6101            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
6102            ":8\r\n"
6103        );
6104
6105        // A missing key is all noughts, so a one is never found and a nought is
6106        // at position zero.
6107        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
6108        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
6109    }
6110
6111    /// The eight operations, with the answers a real server gives for them.
6112    #[test]
6113    fn the_eight_combinations_write_what_a_real_server_writes() {
6114        let mut f = Fixture::new();
6115        f.run(&[b"SET", b"a", b"abc"]);
6116        f.run(&[b"SET", b"b", b"abd"]);
6117        let cases: &[(&[u8], &str)] = &[
6118            (b"AND", "ab`"),
6119            (b"OR", "abg"),
6120            (b"XOR", "\u{0}\u{0}\u{7}"),
6121            (b"DIFF", "\u{0}\u{0}\u{3}"),
6122            (b"DIFF1", "\u{0}\u{0}\u{4}"),
6123            (b"ANDOR", "ab`"),
6124            (b"ONE", "\u{0}\u{0}\u{7}"),
6125        ];
6126        for (op, want) in cases {
6127            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
6128            assert_eq!(
6129                f.run(&[b"GET", b"d"]),
6130                format!("$3\r\n{want}\r\n"),
6131                "{op:?}"
6132            );
6133        }
6134        // The one whose answer is not text, so it is compared as bytes.
6135        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
6136        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
6137
6138        // A missing source is a string of noughts as long as it needs to be, so
6139        // an AND against one writes three zero bytes rather than nothing.
6140        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
6141        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
6142
6143        // Every source missing is an empty result, and an empty result takes
6144        // the destination with it.
6145        f.run(&[b"SET", b"dest", b"x"]);
6146        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
6147        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6148    }
6149
6150    /// What `BITOP` says when it is asked for something it cannot do.
6151    #[test]
6152    fn bitop_names_the_operation_in_its_own_complaints() {
6153        let mut f = Fixture::new();
6154        f.run(&[b"SET", b"a", b"abc"]);
6155        assert_eq!(
6156            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
6157            "-ERR syntax error\r\n"
6158        );
6159        assert_eq!(
6160            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
6161            "-ERR BITOP NOT must be called with a single source key.\r\n"
6162        );
6163        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
6164            assert_eq!(
6165                f.run(&[b"BITOP", op, b"d", b"a"]),
6166                format!(
6167                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
6168                    String::from_utf8_lossy(op)
6169                )
6170            );
6171        }
6172        f.run(&[b"LPUSH", b"l", b"x"]);
6173        assert_eq!(
6174            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
6175            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6176        );
6177    }
6178
6179    /// Packed fields, the three overflow policies and the `#` offset.
6180    #[test]
6181    fn bitfield_reads_and_writes_packed_fields() {
6182        let mut f = Fixture::new();
6183        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
6184        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
6185
6186        assert_eq!(
6187            f.run(&[
6188                b"BITFIELD",
6189                b"bf",
6190                b"INCRBY",
6191                b"u2",
6192                b"100",
6193                b"1",
6194                b"GET",
6195                b"u4",
6196                b"0"
6197            ]),
6198            "*2\r\n:1\r\n:0\r\n"
6199        );
6200        // The field at bit 100 is two bits wide, so it ends in the thirteenth
6201        // byte and the value grew to thirteen bytes to hold it.
6202        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
6203
6204        // A `#` offset counts in fields rather than in bits.
6205        assert_eq!(
6206            f.run(&[
6207                b"BITFIELD",
6208                b"bf",
6209                b"SET",
6210                b"u8",
6211                b"#0",
6212                b"255",
6213                b"GET",
6214                b"u8",
6215                b"#0"
6216            ]),
6217            "*2\r\n:0\r\n:255\r\n"
6218        );
6219
6220        assert_eq!(
6221            f.run(&[
6222                b"BITFIELD",
6223                b"bf",
6224                b"OVERFLOW",
6225                b"SAT",
6226                b"INCRBY",
6227                b"i8",
6228                b"0",
6229                b"120",
6230                b"INCRBY",
6231                b"i8",
6232                b"0",
6233                b"120"
6234            ]),
6235            "*2\r\n:119\r\n:127\r\n"
6236        );
6237        assert_eq!(
6238            f.run(&[
6239                b"BITFIELD",
6240                b"bf2",
6241                b"OVERFLOW",
6242                b"FAIL",
6243                b"INCRBY",
6244                b"u2",
6245                b"0",
6246                b"5"
6247            ]),
6248            "*1\r\n$-1\r\n"
6249        );
6250        assert_eq!(
6251            f.run(&[
6252                b"BITFIELD",
6253                b"bf3",
6254                b"OVERFLOW",
6255                b"WRAP",
6256                b"INCRBY",
6257                b"u2",
6258                b"0",
6259                b"5"
6260            ]),
6261            "*1\r\n:1\r\n"
6262        );
6263        assert_eq!(
6264            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
6265            "*1\r\n:4611686018427387904\r\n"
6266        );
6267    }
6268
6269    /// A bad subcommand anywhere in the line stops all of it.
6270    ///
6271    /// Redis checks the whole argument list before it runs any of it, so the
6272    /// `SET` in front of the bad type here never happens and the key it would
6273    /// have created is not there afterwards.
6274    #[test]
6275    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
6276        let mut f = Fixture::new();
6277        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
6278        assert_eq!(
6279            f.run(&[
6280                b"BITFIELD",
6281                b"bad",
6282                b"SET",
6283                b"u8",
6284                b"0",
6285                b"1",
6286                b"GET",
6287                b"u99",
6288                b"0"
6289            ]),
6290            bad_type
6291        );
6292        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
6293        assert_eq!(
6294            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
6295            bad_type
6296        );
6297        assert_eq!(
6298            f.run(&[b"BITFIELD", b"bad", b"GET"]),
6299            "-ERR syntax error\r\n"
6300        );
6301        assert_eq!(
6302            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
6303            "-ERR syntax error\r\n"
6304        );
6305        assert_eq!(
6306            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
6307            "-ERR syntax error\r\n"
6308        );
6309        assert_eq!(
6310            f.run(&[
6311                b"BITFIELD",
6312                b"bad",
6313                b"OVERFLOW",
6314                b"NOPE",
6315                b"GET",
6316                b"u8",
6317                b"0"
6318            ]),
6319            "-ERR Invalid OVERFLOW type specified\r\n"
6320        );
6321        assert_eq!(
6322            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
6323            "-ERR value is not an integer or out of range\r\n"
6324        );
6325        for at in [&b"#-1"[..], b"abc"] {
6326            assert_eq!(
6327                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
6328                "-ERR bit offset is not an integer or out of range\r\n"
6329            );
6330        }
6331    }
6332
6333    /// The read only twin reads, refuses to write, and creates nothing.
6334    #[test]
6335    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
6336        let mut f = Fixture::new();
6337        f.run(&[b"SET", b"n", b"123"]);
6338        assert_eq!(
6339            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
6340            "*1\r\n:49\r\n"
6341        );
6342        // A read does not unpack an int the way a write does.
6343        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
6344
6345        // An OVERFLOW word is allowed even though nothing here can overflow.
6346        assert_eq!(
6347            f.run(&[
6348                b"BITFIELD_RO",
6349                b"n",
6350                b"OVERFLOW",
6351                b"SAT",
6352                b"GET",
6353                b"u8",
6354                b"0"
6355            ]),
6356            "*1\r\n:49\r\n"
6357        );
6358        for sub in [&b"SET"[..], b"INCRBY"] {
6359            assert_eq!(
6360                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
6361                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
6362            );
6363        }
6364
6365        assert_eq!(
6366            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
6367            "*1\r\n:0\r\n"
6368        );
6369        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6370    }
6371
6372    /// The offsets a bitmap command will not take.
6373    #[test]
6374    fn an_offset_off_the_end_of_the_world_is_refused() {
6375        let mut f = Fixture::new();
6376        let bad = "-ERR bit offset is not an integer or out of range\r\n";
6377        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
6378            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
6379            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
6380        }
6381        for arg in [&b"2"[..], b"-1"] {
6382            assert_eq!(
6383                f.run(&[b"BITPOS", b"k", arg]),
6384                "-ERR The bit argument must be 1 or 0.\r\n"
6385            );
6386        }
6387        assert_eq!(
6388            f.run(&[b"BITPOS", b"k", b"abc"]),
6389            "-ERR value is not an integer or out of range\r\n"
6390        );
6391        assert_eq!(
6392            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
6393            "-ERR value is not an integer or out of range\r\n"
6394        );
6395        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
6396        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
6397        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
6398    }
6399
6400    /// Every one of the seven refuses a key that is not a string.
6401    #[test]
6402    fn every_bitmap_command_says_wrongtype() {
6403        let mut f = Fixture::new();
6404        f.run(&[b"LPUSH", b"l", b"x"]);
6405        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6406        let cases: &[&[&[u8]]] = &[
6407            &[b"SETBIT", b"l", b"0", b"1"],
6408            &[b"GETBIT", b"l", b"0"],
6409            &[b"BITCOUNT", b"l"],
6410            &[b"BITPOS", b"l", b"1"],
6411            &[b"BITOP", b"AND", b"d", b"l"],
6412            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
6413            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
6414        ];
6415        for case in cases {
6416            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
6417        }
6418    }
6419
6420    // --------------------------------------------------------- hyperloglogs
6421
6422    #[test]
6423    fn a_sketch_is_added_to_and_counted() {
6424        let mut f = Fixture::new();
6425        // Creating the key counts as a change, even with nothing to add.
6426        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
6427        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
6428        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
6429        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
6430        // And it is a string, which is not an implementation detail: a client
6431        // can `GET` a sketch out of one server and `SET` it into another.
6432        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
6433        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
6434
6435        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
6436        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
6437        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6438    }
6439
6440    #[test]
6441    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
6442        let mut f = Fixture::new();
6443        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6444        // Not text, so it is compared as bytes.
6445        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";
6446        let mut reply = b"$27\r\n".to_vec();
6447        reply.extend_from_slice(want);
6448        reply.extend_from_slice(b"\r\n");
6449        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
6450    }
6451
6452    #[test]
6453    fn counting_several_keys_counts_their_union() {
6454        let mut f = Fixture::new();
6455        f.run(&[b"PFADD", b"a", b"x", b"y"]);
6456        f.run(&[b"PFADD", b"b", b"y", b"z"]);
6457        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
6458        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
6459        // A key that is not there is an empty sketch, not an error and not
6460        // something that gets created by being counted.
6461        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
6462        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
6463        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
6464    }
6465
6466    #[test]
6467    fn a_merge_keeps_what_the_destination_had() {
6468        let mut f = Fixture::new();
6469        f.run(&[b"PFADD", b"a", b"x", b"y"]);
6470        f.run(&[b"PFADD", b"b", b"z"]);
6471        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
6472        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
6473        // The destination is one of the sources, so a second merge adds to it.
6474        f.run(&[b"PFADD", b"c", b"w"]);
6475        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
6476        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
6477        // And with no sources it is a no-op that still answers OK and still
6478        // creates a destination that was not there.
6479        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
6480        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
6481    }
6482
6483    #[test]
6484    fn the_debug_forms_answer_four_different_shapes() {
6485        let mut f = Fixture::new();
6486        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6487        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
6488        assert_eq!(
6489            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6490            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
6491        );
6492        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
6493        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
6494        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
6495        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
6496        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
6497        // A dense sketch has no opcodes left to print.
6498        assert_eq!(
6499            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
6500            "-ERR HLL encoding is not sparse\r\n"
6501        );
6502
6503        // All 16384 registers, of which three are not nought.
6504        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
6505        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
6506        assert_eq!(reply.matches(":0\r\n").count(), 16381);
6507        assert_eq!(reply.matches(":1\r\n").count(), 2);
6508        assert_eq!(reply.matches(":2\r\n").count(), 1);
6509
6510        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
6511    }
6512
6513    #[test]
6514    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
6515        let mut f = Fixture::new();
6516        f.run(&[b"SET", b"plain", b"not a sketch"]);
6517        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
6518        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
6519        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
6520        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
6521        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
6522
6523        // A key that is not a string at all gets the ordinary sentence, and a
6524        // destination that would have been written is not created.
6525        f.run(&[b"RPUSH", b"l", b"x"]);
6526        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6527        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
6528        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
6529        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
6530        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
6531        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
6532    }
6533
6534    #[test]
6535    fn pfdebug_has_its_own_complaints() {
6536        let mut f = Fixture::new();
6537        f.run(&[b"PFADD", b"h", b"a"]);
6538        // The word is quoted exactly as the client spelled it, and this is not
6539        // the "Try X HELP." sentence every other container command uses.
6540        assert_eq!(
6541            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
6542            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
6543        );
6544        // Where all three of the real commands take a missing key as empty.
6545        let gone = "-ERR The specified key does not exist\r\n";
6546        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
6547        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
6548        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
6549        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
6550        assert_eq!(
6551            f.run(&[b"PFDEBUG"]),
6552            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
6553        );
6554        assert_eq!(
6555            f.run(&[b"PFSELFTEST", b"x"]),
6556            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
6557        );
6558    }
6559
6560    #[test]
6561    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
6562        let mut f = Fixture::new();
6563        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
6564        // The sketch with its last byte cut off, which is still a header and a
6565        // magic and is a run length encoding that stops short of register 16384.
6566        let reply = f.raw(&[b"GET", b"h"]);
6567        let short = reply[5..reply.len() - 3].to_vec();
6568        f.run(&[b"SET", b"h", &short]);
6569        assert_eq!(
6570            f.run(&[b"PFCOUNT", b"h"]),
6571            "-INVALIDOBJ Corrupted HLL object detected\r\n"
6572        );
6573    }
6574
6575    #[test]
6576    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
6577        let mut f = Fixture::new();
6578        // One that stays sparse and one that has gone dense, since the payload
6579        // carries the bytes and the two encodings are different lengths.
6580        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
6581        for i in 0..10_000u32 {
6582            let ele = format!("e{i}");
6583            f.run(&[b"PFADD", b"big", ele.as_bytes()]);
6584        }
6585        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
6586        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
6587
6588        for key in [&b"small"[..], b"big"] {
6589            let mut copy = key.to_vec();
6590            copy.push(b'2');
6591            let bytes = payload(&f.raw(&[b"DUMP", key]));
6592            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
6593            // The bytes, the encoding and the estimate all come back, which is
6594            // the whole of what byte compatibility across a round trip means.
6595            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
6596            assert_eq!(
6597                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
6598                f.run(&[b"PFDEBUG", b"ENCODING", key])
6599            );
6600            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
6601        }
6602        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
6603        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
6604    }
6605
6606    /// One RESP2 bulk string. The JSON replies are almost all one of these and
6607    /// the text inside them has quotes in it, so writing the frame out by hand
6608    /// buries the part of the assertion that matters.
6609    fn bulk(s: &str) -> String {
6610        format!("${}\r\n{s}\r\n", s.len())
6611    }
6612
6613    /// A RESP2 array of bulk strings, which is what most of the list replies
6614    /// are and what writing them out by hand in every assertion looks like.
6615    fn bulks(parts: &[&str]) -> String {
6616        let mut s = format!("*{}\r\n", parts.len());
6617        for p in parts {
6618            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
6619        }
6620        s
6621    }
6622
6623    #[test]
6624    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
6625        let mut f = Fixture::new();
6626        // Each element in turn goes at the head, so the last one sent is at the
6627        // front when it is over. That reads like a bug in the client and it is
6628        // what every Redis has always done.
6629        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
6630        assert_eq!(
6631            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6632            bulks(&["c", "b", "a"])
6633        );
6634        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
6635        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
6636        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
6637        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
6638        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
6639        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
6640    }
6641
6642    #[test]
6643    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
6644        let mut f = Fixture::new();
6645        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
6646        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
6647        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6648        f.run(&[b"RPUSH", b"k", b"a"]);
6649        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
6650        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
6651        assert_eq!(
6652            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6653            bulks(&["z", "a", "y"])
6654        );
6655    }
6656
6657    /// The four ways a pop can come back with nothing, which are three
6658    /// different replies and a RESP2 client can tell all of them apart.
6659    #[test]
6660    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
6661        let mut f = Fixture::new();
6662        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
6663        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
6664        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
6665        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
6666        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6667        // A count of zero against a list that is there is an empty array and
6668        // not a null array, which is the fourth answer.
6669        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
6670        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
6671        // More than there is takes what there is and the key goes with it.
6672        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
6673        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6674    }
6675
6676    #[test]
6677    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
6678        let mut f = Fixture::new();
6679        f.run(&[b"RPUSH", b"k", b"a"]);
6680        let range = "-ERR value is out of range, must be positive\r\n";
6681        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
6682        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
6683        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
6684        // Redis calls this an arity error and not a syntax error, which is a
6685        // distinction it does not always make.
6686        assert_eq!(
6687            f.run(&[b"LPOP", b"k", b"1", b"2"]),
6688            "-ERR wrong number of arguments for 'lpop' command\r\n"
6689        );
6690        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6691    }
6692
6693    #[test]
6694    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
6695        let mut f = Fixture::new();
6696        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6697        assert_eq!(
6698            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6699            bulks(&["a", "b", "c"])
6700        );
6701        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
6702        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
6703        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
6704        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
6705        assert_eq!(
6706            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
6707            bulks(&["a", "b", "c"])
6708        );
6709        // A key that is not there is an empty range and not a nil, which is the
6710        // one place a list disagrees with a set.
6711        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
6712        assert_eq!(
6713            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
6714            "-ERR value is not an integer or out of range\r\n"
6715        );
6716    }
6717
6718    #[test]
6719    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
6720        let mut f = Fixture::new();
6721        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6722        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
6723        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
6724        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
6725        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
6726        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
6727        assert_eq!(
6728            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6729            bulks(&["a", "b", "z"])
6730        );
6731        // Both ways of missing are errors here rather than a nil, because a
6732        // list is never empty and there is nothing else the reply could be.
6733        assert_eq!(
6734            f.run(&[b"LSET", b"k", b"99", b"z"]),
6735            "-ERR index out of range\r\n"
6736        );
6737        assert_eq!(
6738            f.run(&[b"LSET", b"nope", b"0", b"z"]),
6739            "-ERR no such key\r\n"
6740        );
6741    }
6742
6743    #[test]
6744    fn linsert_says_three_things_with_one_signed_number() {
6745        let mut f = Fixture::new();
6746        // Zero for a key that is not there, which is not the same as minus one
6747        // for a pivot that is not in a list that is.
6748        assert_eq!(
6749            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
6750            ":0\r\n"
6751        );
6752        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6753        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
6754        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
6755        assert_eq!(
6756            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6757            bulks(&["X", "a", "b", "Y"])
6758        );
6759        assert_eq!(
6760            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
6761            ":-1\r\n"
6762        );
6763        assert_eq!(
6764            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
6765            "-ERR syntax error\r\n"
6766        );
6767    }
6768
6769    #[test]
6770    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
6771        let mut f = Fixture::new();
6772        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
6773        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
6774        assert_eq!(
6775            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
6776            bulks(&["b", "c", "a"])
6777        );
6778        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
6779        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6780        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
6781        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
6782        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6783        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
6784    }
6785
6786    #[test]
6787    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
6788        let mut f = Fixture::new();
6789        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
6790        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
6791        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
6792        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
6793        // leave `EXISTS` answering zero rather than leaving an empty one.
6794        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
6795        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
6796        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
6797    }
6798
6799    #[test]
6800    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
6801        let mut f = Fixture::new();
6802        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
6803        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
6804        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
6805        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
6806        assert_eq!(
6807            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
6808            "*2\r\n:0\r\n:3\r\n"
6809        );
6810        assert_eq!(
6811            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
6812            "*3\r\n:6\r\n:3\r\n:0\r\n"
6813        );
6814        // MAXLEN counts elements looked at and not matches found, so three
6815        // stops after `a b c` and finds the one match in it.
6816        assert_eq!(
6817            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
6818            "*1\r\n:0\r\n"
6819        );
6820        // Nothing found is three different replies depending on how it was
6821        // asked and whether the key is there at all.
6822        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
6823        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
6824        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
6825        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
6826    }
6827
6828    #[test]
6829    fn lpos_words_its_three_mistakes_the_way_redis_does() {
6830        let mut f = Fixture::new();
6831        f.run(&[b"RPUSH", b"p", b"a"]);
6832        // The whole sentence and not a prefix, because the older wording of it
6833        // is still all over the internet and clients match on the text.
6834        assert_eq!(
6835            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
6836            "-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"
6837        );
6838        assert_eq!(
6839            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
6840            "-ERR COUNT can't be negative\r\n"
6841        );
6842        assert_eq!(
6843            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
6844            "-ERR MAXLEN can't be negative\r\n"
6845        );
6846        assert_eq!(
6847            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
6848            "-ERR syntax error\r\n"
6849        );
6850        assert_eq!(
6851            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
6852            "-ERR syntax error\r\n"
6853        );
6854    }
6855
6856    #[test]
6857    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
6858        let mut f = Fixture::new();
6859        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
6860        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
6861        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6862        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
6863        assert_eq!(
6864            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
6865            "$1\r\na\r\n"
6866        );
6867        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
6868        // The same key twice is the documented way to rotate a list and falls
6869        // out of taking the element before deciding where to put it.
6870        f.run(&[b"DEL", b"r"]);
6871        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
6872        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
6873        assert_eq!(
6874            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
6875            bulks(&["3", "1", "2"])
6876        );
6877        assert_eq!(
6878            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
6879            "$-1\r\n"
6880        );
6881        assert_eq!(
6882            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
6883            "-ERR syntax error\r\n"
6884        );
6885    }
6886
6887    #[test]
6888    fn a_move_checks_the_destination_before_it_takes_anything() {
6889        let mut f = Fixture::new();
6890        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6891        f.run(&[b"SET", b"str", b"v"]);
6892        assert_eq!(
6893            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
6894            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6895        );
6896        // The element is still where it was, rather than having gone nowhere.
6897        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6898    }
6899
6900    #[test]
6901    fn a_block_move_orders_the_block_by_the_ends_and_the_ordering_word() {
6902        // OBO is what you get from sending LMOVE that many times, BULK keeps
6903        // the source order. The two only differ when both ends are the same,
6904        // which is the whole reason the word exists.
6905        for (from, to, order, want) in [
6906            ("LEFT", "RIGHT", "OBO", ["a", "b"]),
6907            ("LEFT", "RIGHT", "BULK", ["a", "b"]),
6908            ("LEFT", "LEFT", "OBO", ["b", "a"]),
6909            ("LEFT", "LEFT", "BULK", ["a", "b"]),
6910            ("RIGHT", "LEFT", "OBO", ["d", "e"]),
6911            ("RIGHT", "LEFT", "BULK", ["d", "e"]),
6912            ("RIGHT", "RIGHT", "OBO", ["e", "d"]),
6913            ("RIGHT", "RIGHT", "BULK", ["d", "e"]),
6914        ] {
6915            let mut f = Fixture::new();
6916            f.run(&[b"RPUSH", b"s", b"a", b"b", b"c", b"d", b"e"]);
6917            let how = format!("{from} {to} {order}");
6918            let reply = f.run(&[
6919                b"LMOVEM",
6920                b"s",
6921                b"d",
6922                from.as_bytes(),
6923                to.as_bytes(),
6924                b"COUNT",
6925                b"2",
6926                order.as_bytes(),
6927            ]);
6928            assert_eq!(reply, bulks(&want), "the reply for {how}");
6929            assert_eq!(
6930                f.run(&[b"LRANGE", b"d", b"0", b"-1"]),
6931                bulks(&want),
6932                "the destination for {how}"
6933            );
6934        }
6935    }
6936
6937    #[test]
6938    fn a_block_move_of_one_needs_no_count_at_all() {
6939        let mut f = Fixture::new();
6940        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
6941        assert_eq!(
6942            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT"]),
6943            bulks(&["a"])
6944        );
6945        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["b", "c"]));
6946        // Six and seven arguments are neither of the two forms, so the
6947        // reference calls both of them a syntax error rather than guessing.
6948        assert_eq!(
6949            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT"]),
6950            "-ERR syntax error\r\n"
6951        );
6952        assert_eq!(
6953            f.run(&[b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"2"]),
6954            "-ERR syntax error\r\n"
6955        );
6956    }
6957
6958    #[test]
6959    fn a_block_move_with_exactly_takes_all_of_them_or_none() {
6960        let mut f = Fixture::new();
6961        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
6962        // A null array and not a null bulk string, which `redis-cli` prints as
6963        // `(nil)` either way and only the raw wire tells apart. What it would
6964        // have sent is an array, so its nothing is an array's nothing.
6965        assert_eq!(
6966            f.run(&[
6967                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"EXACTLY", b"99", b"BULK"
6968            ]),
6969            "*-1\r\n"
6970        );
6971        assert_eq!(
6972            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
6973            bulks(&["a", "b", "c"])
6974        );
6975        // COUNT takes what there is, and an emptied source goes away.
6976        assert_eq!(
6977            f.run(&[
6978                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"99", b"BULK"
6979            ]),
6980            bulks(&["a", "b", "c"])
6981        );
6982        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
6983        assert_eq!(
6984            f.run(&[
6985                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
6986            ]),
6987            "*-1\r\n"
6988        );
6989    }
6990
6991    #[test]
6992    fn a_block_move_onto_itself_rotates_by_the_count() {
6993        let mut f = Fixture::new();
6994        f.run(&[b"RPUSH", b"s", b"a", b"b", b"c"]);
6995        assert_eq!(
6996            f.run(&[
6997                b"LMOVEM", b"s", b"s", b"LEFT", b"RIGHT", b"COUNT", b"2", b"BULK"
6998            ]),
6999            bulks(&["a", "b"])
7000        );
7001        assert_eq!(
7002            f.run(&[b"LRANGE", b"s", b"0", b"-1"]),
7003            bulks(&["c", "a", "b"])
7004        );
7005    }
7006
7007    #[test]
7008    fn a_block_move_reads_the_count_before_the_ordering_word() {
7009        let mut f = Fixture::new();
7010        f.run(&[b"RPUSH", b"s", b"a", b"b"]);
7011        f.run(&[b"SET", b"str", b"v"]);
7012        let count = "-ERR count should be greater than 0\r\n";
7013        assert_eq!(
7014            f.run(&[
7015                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"NOPE"
7016            ]),
7017            count
7018        );
7019        assert_eq!(
7020            f.run(&[
7021                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"0", b"BULK"
7022            ]),
7023            count
7024        );
7025        assert_eq!(
7026            f.run(&[
7027                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"COUNT", b"1", b"NOPE"
7028            ]),
7029            "-ERR syntax error\r\n"
7030        );
7031        assert_eq!(
7032            f.run(&[
7033                b"LMOVEM", b"s", b"d", b"LEFT", b"RIGHT", b"NOPE", b"abc", b"BULK"
7034            ]),
7035            "-ERR syntax error\r\n"
7036        );
7037        // Every argument is read before the keys are looked at, so a bad count
7038        // beats a wrong type even when the type is wrong on the source.
7039        assert_eq!(
7040            f.run(&[
7041                b"LMOVEM", b"str", b"d", b"LEFT", b"RIGHT", b"COUNT", b"abc", b"BULK"
7042            ]),
7043            count
7044        );
7045        assert_eq!(
7046            f.run(&[
7047                b"LMOVEM", b"s", b"str", b"LEFT", b"RIGHT", b"COUNT", b"1", b"BULK"
7048            ]),
7049            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
7050        );
7051        assert_eq!(f.run(&[b"LRANGE", b"s", b"0", b"-1"]), bulks(&["a", "b"]));
7052    }
7053
7054    #[test]
7055    fn lmpop_answers_from_the_first_key_that_has_anything() {
7056        let mut f = Fixture::new();
7057        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
7058        // The name of the key that answered comes back with the elements,
7059        // because the client cannot work out which one it was.
7060        assert_eq!(
7061            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
7062            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
7063        );
7064        assert_eq!(
7065            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
7066            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
7067        );
7068        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
7069        // A null array and not a null, even though what it stands in for is an
7070        // array holding a key name and then another array.
7071        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
7072    }
7073
7074    #[test]
7075    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
7076        let mut f = Fixture::new();
7077        f.run(&[b"RPUSH", b"k", b"a"]);
7078        assert_eq!(
7079            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
7080            "-ERR numkeys should be greater than 0\r\n"
7081        );
7082        assert_eq!(
7083            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
7084            "-ERR numkeys should be greater than 0\r\n"
7085        );
7086        assert_eq!(
7087            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
7088            "-ERR count should be greater than 0\r\n"
7089        );
7090        // A key count that eats the direction is a syntax error and not a
7091        // sentence about key counts, because the direction is simply not there.
7092        assert_eq!(
7093            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
7094            "-ERR syntax error\r\n"
7095        );
7096        assert_eq!(
7097            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
7098            "-ERR syntax error\r\n"
7099        );
7100        assert_eq!(
7101            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
7102            "-ERR syntax error\r\n"
7103        );
7104        assert_eq!(
7105            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
7106            "-ERR syntax error\r\n"
7107        );
7108        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
7109    }
7110
7111    #[test]
7112    fn every_list_command_says_wrongtype_and_writes_nothing() {
7113        let mut f = Fixture::new();
7114        f.run(&[b"SET", b"str", b"v"]);
7115        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7116        for cmd in [
7117            &[b"LPUSH".as_slice(), b"str", b"a"][..],
7118            &[b"RPUSH", b"str", b"a"],
7119            &[b"LPUSHX", b"str", b"a"],
7120            &[b"RPUSHX", b"str", b"a"],
7121            &[b"LPOP", b"str"],
7122            &[b"LPOP", b"str", b"2"],
7123            &[b"RPOP", b"str"],
7124            &[b"LLEN", b"str"],
7125            &[b"LRANGE", b"str", b"0", b"-1"],
7126            &[b"LINDEX", b"str", b"0"],
7127            &[b"LSET", b"str", b"0", b"a"],
7128            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
7129            &[b"LREM", b"str", b"0", b"a"],
7130            &[b"LTRIM", b"str", b"0", b"-1"],
7131            &[b"LPOS", b"str", b"a"],
7132            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
7133            &[b"RPOPLPUSH", b"str", b"d"],
7134            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
7135            &[b"LMPOP", b"1", b"str", b"LEFT"],
7136        ] {
7137            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
7138        }
7139        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
7140        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7141    }
7142
7143    /// A timeout is not an integer and it is not an ordinary float either: the
7144    /// three sentences it can answer with are its own, and which one a given
7145    /// argument gets is not what reading the code would suggest.
7146    #[test]
7147    fn a_timeout_has_three_ways_of_being_wrong() {
7148        let mut f = Fixture::new();
7149        let not_float = "-ERR timeout is not a float or out of range\r\n";
7150        let range = "-ERR timeout is out of range\r\n";
7151        for (bad, want) in [
7152            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
7153            (&[b"BLPOP", b"k", b"nan"], not_float),
7154            (&[b"BLPOP", b"k", b""], not_float),
7155            // Whitespace on either side, which `strtold` would take and Redis
7156            // does not.
7157            (&[b"BLPOP", b"k", b" 1"], not_float),
7158            (&[b"BLPOP", b"k", b"1 "], not_float),
7159            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
7160            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
7161            // These three parse, so they are not the not-a-float error, and all
7162            // three are further off than an i64 of milliseconds reaches.
7163            (&[b"BLPOP", b"k", b"1e400"], range),
7164            (&[b"BLPOP", b"k", b"inf"], range),
7165            (&[b"BLPOP", b"k", b"9999999999999999"], range),
7166            (&[b"BRPOP", b"k", b"abc"], not_float),
7167            (
7168                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
7169                not_float,
7170            ),
7171            (
7172                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
7173                "-ERR timeout is negative\r\n",
7174            ),
7175            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
7176        ] {
7177            assert_eq!(f.run(bad), want, "for {bad:?}");
7178        }
7179    }
7180
7181    /// A timeout of exactly zero means no timeout, and there are two ways of
7182    /// writing exactly zero.
7183    #[test]
7184    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
7185        let mut f = Fixture::new();
7186        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
7187            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
7188            assert_eq!(flow, Flow::Block, "for {timeout:?}");
7189            assert!(out.is_empty(), "for {timeout:?}");
7190        }
7191        // Positive, so it is a real deadline, and the deadline is this
7192        // millisecond. Nothing is written here either: the reply comes from the
7193        // sweep, which is the engine's and not this layer's.
7194        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
7195        assert_eq!(flow, Flow::Block);
7196        assert!(out.is_empty());
7197    }
7198
7199    #[test]
7200    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
7201        let mut f = Fixture::new();
7202        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7203
7204        // The one difference from LPOP: the reply names the key that answered,
7205        // which is what makes BLPOP over several keys usable.
7206        assert_eq!(
7207            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
7208            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
7209        );
7210        assert_eq!(
7211            f.run(&[b"BRPOP", b"L", b"0"]),
7212            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
7213        );
7214        assert_eq!(
7215            f.run(&[
7216                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
7217            ]),
7218            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7219        );
7220        assert_eq!(
7221            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7222            "$1\r\nd\r\n"
7223        );
7224        assert_eq!(
7225            f.run(&[b"EXISTS", b"L"]),
7226            ":0\r\n",
7227            "and the key went with it"
7228        );
7229        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
7230        // Onto itself, which is how a list is rotated and is a real thing to ask
7231        // a blocking move for.
7232        f.run(&[b"RPUSH", b"D", b"x"]);
7233        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
7234        assert_eq!(
7235            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7236            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
7237        );
7238    }
7239
7240    #[test]
7241    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
7242        let mut f = Fixture::new();
7243        f.run(&[b"RPUSH", b"k", b"a"]);
7244        for (bad, want) in [
7245            (
7246                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
7247                "-ERR numkeys should be greater than 0\r\n",
7248            ),
7249            (
7250                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
7251                "-ERR numkeys should be greater than 0\r\n",
7252            ),
7253            // Two keys named and one given, so the word that should have been
7254            // the direction is a key and there is no direction left.
7255            (
7256                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
7257                "-ERR syntax error\r\n",
7258            ),
7259            (
7260                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
7261                "-ERR syntax error\r\n",
7262            ),
7263            (
7264                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
7265                "-ERR syntax error\r\n",
7266            ),
7267            (
7268                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
7269                "-ERR syntax error\r\n",
7270            ),
7271            // A count that is not a number at all gets the same sentence a zero
7272            // or a negative one gets, rather than the usual one about integers.
7273            (
7274                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
7275                "-ERR count should be greater than 0\r\n",
7276            ),
7277            (
7278                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
7279                "-ERR count should be greater than 0\r\n",
7280            ),
7281        ] {
7282            assert_eq!(f.run(bad), want, "for {bad:?}");
7283        }
7284        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
7285    }
7286
7287    #[test]
7288    fn a_blocking_move_reads_its_directions_before_its_timeout() {
7289        let mut f = Fixture::new();
7290        // Both are wrong. Redis checks the directions first, so this is the
7291        // syntax error and not a complaint about the timeout.
7292        assert_eq!(
7293            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
7294            "-ERR syntax error\r\n"
7295        );
7296        assert_eq!(
7297            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
7298            "-ERR syntax error\r\n"
7299        );
7300    }
7301
7302    /// `BLMOVEM` answers exactly what `LMOVEM` answers when it does not have to
7303    /// wait, which is the same relationship every other command in this file has
7304    /// with the one it wraps.
7305    #[test]
7306    fn a_blocking_block_move_that_can_be_answered_answers_like_lmovem() {
7307        let mut f = Fixture::new();
7308        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
7309        assert_eq!(
7310            f.flow(&[b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
7311            (Flow::Continue, "*1\r\n$1\r\na\r\n".to_owned())
7312        );
7313        assert_eq!(
7314            f.run(&[
7315                b"BLMOVEM", b"L", b"D", b"RIGHT", b"RIGHT", b"0", b"COUNT", b"2", b"OBO"
7316            ]),
7317            bulks(&["e", "d"])
7318        );
7319        assert_eq!(
7320            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
7321            bulks(&["a", "e", "d"])
7322        );
7323        // `EXACTLY` with enough there does not wait either.
7324        assert_eq!(
7325            f.run(&[
7326                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"2", b"BULK"
7327            ]),
7328            bulks(&["b", "c"])
7329        );
7330        assert_eq!(f.run(&[b"EXISTS", b"L"]), ":0\r\n", "and the key went");
7331    }
7332
7333    /// The one thing `BLMOVEM` decides differently from the other five: `COUNT`
7334    /// is ready as soon as there is anything and `EXACTLY` is not ready until the
7335    /// whole block has arrived.
7336    #[test]
7337    fn a_blocking_block_move_waits_for_the_whole_block_only_under_exactly() {
7338        let mut f = Fixture::new();
7339        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7340        // Two there and three asked for. `COUNT` takes the two.
7341        assert_eq!(
7342            f.flow(&[
7343                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"COUNT", b"3", b"BULK"
7344            ]),
7345            (Flow::Continue, bulks(&["a", "b"]))
7346        );
7347
7348        f.run(&[b"RPUSH", b"L", b"a", b"b"]);
7349        // The same line with `EXACTLY` parks instead, and takes nothing on the
7350        // way past.
7351        assert_eq!(
7352            f.flow(&[
7353                b"BLMOVEM", b"L", b"D", b"LEFT", b"RIGHT", b"0", b"EXACTLY", b"3", b"BULK"
7354            ])
7355            .0,
7356            Flow::Block
7357        );
7358        assert_eq!(f.run(&[b"LRANGE", b"L", b"0", b"-1"]), bulks(&["a", "b"]));
7359    }
7360
7361    #[test]
7362    fn a_blocking_block_move_reads_its_directions_then_its_timeout_then_its_count() {
7363        let mut f = Fixture::new();
7364        let syntax = "-ERR syntax error\r\n";
7365        // All three are wrong and the directions are read first.
7366        assert_eq!(
7367            f.run(&[
7368                b"BLMOVEM", b"a", b"b", b"UP", b"DOWN", b"abc", b"NOPE", b"x", b"y"
7369            ]),
7370            syntax
7371        );
7372        // Directions fine, timeout and count both wrong, so the timeout wins.
7373        assert_eq!(
7374            f.run(&[
7375                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"abc", b"COUNT", b"abc", b"BULK"
7376            ]),
7377            "-ERR timeout is not a float or out of range\r\n"
7378        );
7379        assert_eq!(
7380            f.run(&[
7381                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"-1", b"COUNT", b"1", b"BULK"
7382            ]),
7383            "-ERR timeout is negative\r\n"
7384        );
7385        // And with the timeout fine, the count before the ordering word.
7386        assert_eq!(
7387            f.run(&[
7388                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"abc", b"NOPE"
7389            ]),
7390            "-ERR count should be greater than 0\r\n"
7391        );
7392        assert_eq!(
7393            f.run(&[
7394                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"1", b"NOPE"
7395            ]),
7396            syntax
7397        );
7398        // Seven and eight arguments are neither of the two forms, the same way
7399        // six and seven are for `LMOVEM`.
7400        assert_eq!(
7401            f.run(&[b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT"]),
7402            syntax
7403        );
7404        assert_eq!(
7405            f.run(&[
7406                b"BLMOVEM", b"a", b"b", b"LEFT", b"RIGHT", b"0", b"COUNT", b"2"
7407            ]),
7408            syntax
7409        );
7410    }
7411
7412    /// The four ways a blocking command sees a key of another type, and the one
7413    /// way it does not.
7414    #[test]
7415    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
7416        let mut f = Fixture::new();
7417        f.run(&[b"SET", b"S", b"v"]);
7418        f.run(&[b"RPUSH", b"D", b"x"]);
7419        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7420
7421        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
7422        // Every key is checked even when an earlier one would have blocked, so
7423        // an empty key in front of a string does not hide it.
7424        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
7425        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
7426        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
7427        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
7428        // The destination, which is only reached because the source has
7429        // something in it.
7430        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
7431        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
7432        assert_eq!(
7433            f.run(&[b"BLMOVEM", b"S", b"D", b"LEFT", b"RIGHT", b"0"]),
7434            wrong
7435        );
7436        assert_eq!(
7437            f.run(&[b"BLMOVEM", b"D", b"S", b"LEFT", b"RIGHT", b"0"]),
7438            wrong
7439        );
7440
7441        // And the one that does not: an empty source means the destination is
7442        // never looked at, so this waits rather than erroring, and on a real
7443        // server it times out.
7444        assert_eq!(
7445            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7446                .0,
7447            Flow::Block
7448        );
7449        // `BLMOVEM` has a second way of not being ready, and it hides the
7450        // destination just as well: the source is a list with two elements in it
7451        // and `EXACTLY` wants three, so the string never gets looked at.
7452        assert_eq!(
7453            f.flow(&[b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
7454                .0,
7455            Flow::Block
7456        );
7457        f.run(&[b"RPUSH", b"E", b"1", b"2"]);
7458        assert_eq!(
7459            f.flow(&[
7460                b"BLMOVEM", b"E", b"S", b"LEFT", b"RIGHT", b"0.1", b"EXACTLY", b"3", b"BULK"
7461            ])
7462            .0,
7463            Flow::Block
7464        );
7465    }
7466
7467    /// The same churn the set and the string get, because a list that leaks a
7468    /// chunk per push looks exactly like one that does not until it has run for
7469    /// an afternoon.
7470    #[test]
7471    fn churning_lists_does_not_grow_the_server() {
7472        let mut f = Fixture::new();
7473        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
7474        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
7475            .into_iter()
7476            .chain(vals.iter().map(Vec::as_slice))
7477            .collect();
7478
7479        f.run(&args);
7480        f.run(&[b"DEL", b"k"]);
7481        f.server.compact_step();
7482        let after_first = f.server.memory_bytes();
7483
7484        for _ in 0..200 {
7485            f.run(&args);
7486            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
7487            f.server.compact_step();
7488        }
7489        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7490        assert!(
7491            f.server.memory_bytes() <= after_first * 2,
7492            "held {} after two hundred passes against {after_first} after one",
7493            f.server.memory_bytes()
7494        );
7495    }
7496
7497    // ------------------------------------------------------------ sorted set
7498
7499    #[test]
7500    fn a_sorted_set_takes_scores_and_gives_them_back() {
7501        let mut f = Fixture::new();
7502        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
7503        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
7504        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
7505        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
7506        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
7507        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
7508        assert_eq!(
7509            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
7510            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
7511        );
7512        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
7513        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
7514        // The key goes when the last member does.
7515        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
7516        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7517    }
7518
7519    #[test]
7520    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
7521        let mut f = Fixture::new();
7522        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
7523        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
7524        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
7525        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
7526
7527        f.out = Out::new(Proto::Resp3);
7528        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
7529        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
7530        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
7531        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
7532    }
7533
7534    #[test]
7535    fn the_zadd_options_gate_what_gets_written() {
7536        let mut f = Fixture::new();
7537        f.run(&[b"ZADD", b"z", b"5", b"a"]);
7538        // NX leaves a member that is there alone, XX will not create one.
7539        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
7540        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
7541        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
7542        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
7543        // GT and LT only move a score one way.
7544        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
7545        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
7546        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
7547        // CH counts a moved score and plain ZADD does not.
7548        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
7549        assert_eq!(
7550            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
7551            ":2\r\n"
7552        );
7553    }
7554
7555    #[test]
7556    fn zadd_incr_answers_a_score_or_nothing_at_all() {
7557        let mut f = Fixture::new();
7558        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
7559        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
7560        // A gate that refuses is the string nil, because the reply it stands in
7561        // for is a score.
7562        assert_eq!(
7563            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
7564            "$-1\r\n"
7565        );
7566        assert_eq!(
7567            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
7568            "$-1\r\n"
7569        );
7570        assert_eq!(
7571            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
7572            "$-1\r\n"
7573        );
7574        assert_eq!(
7575            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
7576            "$1\r\n8\r\n"
7577        );
7578        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
7579        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
7580    }
7581
7582    #[test]
7583    fn the_two_infinities_will_not_be_added_together() {
7584        let mut f = Fixture::new();
7585        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
7586        let nan = "-ERR resulting score is not a number (NaN)\r\n";
7587        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
7588        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
7589        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
7590        // And a key made for an increment that then fails does not stay behind.
7591        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
7592    }
7593
7594    #[test]
7595    fn zadd_says_its_mistakes_the_way_redis_says_them() {
7596        let mut f = Fixture::new();
7597        // The pairs are counted before the options are looked at, so this is a
7598        // syntax error about having none and not a complaint about NX and XX.
7599        assert_eq!(
7600            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
7601            "-ERR syntax error\r\n"
7602        );
7603        assert_eq!(
7604            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
7605            "-ERR XX and NX options at the same time are not compatible\r\n"
7606        );
7607        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
7608        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
7609        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
7610        assert_eq!(
7611            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
7612            "-ERR INCR option supports a single increment-element pair\r\n"
7613        );
7614        // An odd number of arguments after the options.
7615        assert_eq!(
7616            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
7617            "-ERR syntax error\r\n"
7618        );
7619        // Every score is read before the first is stored.
7620        assert_eq!(
7621            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
7622            "-ERR value is not a valid float\r\n"
7623        );
7624        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7625    }
7626
7627    #[test]
7628    fn a_rank_says_where_a_member_sits_from_either_end() {
7629        let mut f = Fixture::new();
7630        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7631        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
7632        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
7633        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
7634        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
7635        // WITHSCORE changes both shapes: the answer and the nothing.
7636        assert_eq!(
7637            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
7638            "*2\r\n:1\r\n$1\r\n2\r\n"
7639        );
7640        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
7641        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
7642        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
7643        // A bad option is a syntax error and one argument too many is an arity
7644        // error, which is Redis's split.
7645        assert_eq!(
7646            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
7647            "-ERR syntax error\r\n"
7648        );
7649        assert_eq!(
7650            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
7651            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
7652        );
7653    }
7654
7655    #[test]
7656    fn the_two_counts_read_their_two_kinds_of_bound() {
7657        let mut f = Fixture::new();
7658        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7659        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
7660        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
7661        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
7662        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
7663        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
7664        assert_eq!(
7665            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
7666            "-ERR min or max is not a float\r\n"
7667        );
7668
7669        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
7670        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
7671        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
7672        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
7673        // A bare member is not a bound, because a member can start with any
7674        // byte and there would be no way to say the bracket if it were optional.
7675        assert_eq!(
7676            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
7677            "-ERR min or max not valid string range item\r\n"
7678        );
7679    }
7680
7681    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
7682    ///
7683    /// Every byte in here was read off a real 8.10.1 rather than worked out,
7684    /// because the interesting part of this command is not what it selects, it
7685    /// is which of the two ends the client is expected to name first.
7686    #[test]
7687    fn one_range_command_selects_by_rank_or_score_or_name() {
7688        let mut f = Fixture::new();
7689        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7690        assert_eq!(
7691            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7692            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7693        );
7694        assert_eq!(
7695            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
7696            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7697        );
7698        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
7699        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
7700        // REV over ranks reverses the walk and leaves the two arguments alone,
7701        // because a rank counts from the end the walk starts at.
7702        assert_eq!(
7703            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
7704            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7705        );
7706        assert_eq!(
7707            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
7708            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7709        );
7710        // And REV over scores does swap them, since a bound does not count from
7711        // anywhere. This is the one line of the parse that tells the two apart.
7712        assert_eq!(
7713            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
7714            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7715        );
7716        assert_eq!(
7717            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
7718            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7719        );
7720        assert_eq!(
7721            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
7722            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7723        );
7724    }
7725
7726    /// The older spellings, which are the same six windows with the mode in the
7727    /// name and the high end named first on the three that go backwards.
7728    #[test]
7729    fn the_older_range_spellings_name_their_high_end_first() {
7730        let mut f = Fixture::new();
7731        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7732        assert_eq!(
7733            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
7734            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
7735        );
7736        assert_eq!(
7737            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
7738            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7739        );
7740        assert_eq!(
7741            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
7742            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7743        );
7744        assert_eq!(
7745            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
7746            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
7747        );
7748        // The two arguments the wrong way round is an empty answer and not an
7749        // error, which is what the swap being in the parse rather than in the
7750        // window buys.
7751        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
7752        assert_eq!(
7753            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
7754            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
7755        );
7756        assert_eq!(
7757            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
7758            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
7759        );
7760        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
7761        // way of spelling the mode, they are a syntax error.
7762        for cmd in [
7763            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
7764            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
7765            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
7766        ] {
7767            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
7768        }
7769    }
7770
7771    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
7772    /// only some of them accept.
7773    #[test]
7774    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
7775        let mut f = Fixture::new();
7776        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7777        assert_eq!(
7778            f.run(&[
7779                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
7780            ]),
7781            "*1\r\n$1\r\nb\r\n"
7782        );
7783        // A negative offset skips past everything, a negative count is no bound.
7784        assert_eq!(
7785            f.run(&[
7786                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
7787            ]),
7788            "*0\r\n"
7789        );
7790        assert_eq!(
7791            f.run(&[
7792                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
7793            ]),
7794            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7795        );
7796        // The two options in either order, which falls out of the parse loop.
7797        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";
7798        assert_eq!(
7799            f.run(&[
7800                b"ZRANGEBYSCORE",
7801                b"z",
7802                b"1",
7803                b"3",
7804                b"WITHSCORES",
7805                b"LIMIT",
7806                b"0",
7807                b"2"
7808            ]),
7809            both
7810        );
7811        assert_eq!(
7812            f.run(&[
7813                b"ZRANGEBYSCORE",
7814                b"z",
7815                b"1",
7816                b"3",
7817                b"LIMIT",
7818                b"0",
7819                b"2",
7820                b"WITHSCORES"
7821            ]),
7822            both
7823        );
7824        // LIMIT on a range by rank is refused after the whole option list has
7825        // been read, so this complains about LIMIT and not about WITHSCORES.
7826        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
7827        assert_eq!(
7828            f.run(&[
7829                b"ZREVRANGE",
7830                b"z",
7831                b"0",
7832                b"-1",
7833                b"WITHSCORES",
7834                b"LIMIT",
7835                b"0",
7836                b"1"
7837            ]),
7838            needs_by
7839        );
7840        assert_eq!(
7841            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
7842            needs_by
7843        );
7844        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
7845        assert_eq!(
7846            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
7847            not_bylex
7848        );
7849        assert_eq!(
7850            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
7851            not_bylex
7852        );
7853        // Two modes at once, an option nobody knows, a LIMIT missing its count,
7854        // and the three number errors, which are three different sentences.
7855        for cmd in [
7856            &[
7857                b"ZRANGE".as_slice(),
7858                b"z",
7859                b"0",
7860                b"-1",
7861                b"BYSCORE",
7862                b"BYLEX",
7863            ][..],
7864            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
7865            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
7866        ] {
7867            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
7868        }
7869        assert_eq!(
7870            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
7871            "-ERR min or max is not a float\r\n"
7872        );
7873        assert_eq!(
7874            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
7875            "-ERR min or max not valid string range item\r\n"
7876        );
7877        assert_eq!(
7878            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
7879            "-ERR value is not an integer or out of range\r\n"
7880        );
7881    }
7882
7883    /// `WITHSCORES` is the one place in this group where the two protocols
7884    /// disagree about the shape of the reply and not just the type of a value.
7885    #[test]
7886    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
7887        let mut f = Fixture::new();
7888        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7889        assert_eq!(
7890            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
7891            "*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"
7892        );
7893        f.out = Out::new(Proto::Resp3);
7894        assert_eq!(
7895            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
7896            "*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"
7897        );
7898        assert_eq!(
7899            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7900            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
7901        );
7902    }
7903
7904    /// The store form, which is the same parse with the destination in front.
7905    #[test]
7906    fn a_range_store_writes_the_window_into_another_key() {
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!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
7910        // A window that selects nothing deletes the destination rather than
7911        // leaving an empty sorted set, because an empty one does not exist.
7912        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
7913        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
7914        assert_eq!(
7915            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
7916            ":2\r\n"
7917        );
7918        assert_eq!(
7919            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
7920            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7921        );
7922        // The destination is allowed to be the source, because the result is
7923        // built whole before anything is written over.
7924        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
7925        assert_eq!(
7926            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
7927            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7928        );
7929        // It takes every option ZRANGE takes except WITHSCORES, which is a
7930        // plain syntax error here and not the sentence about BYLEX.
7931        assert_eq!(
7932            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
7933            "-ERR syntax error\r\n"
7934        );
7935    }
7936
7937    /// The three removals, which are the read side's window with the walk
7938    /// turned into a removal and no options at all.
7939    #[test]
7940    fn the_three_removals_share_their_window_with_the_reads() {
7941        let mut f = Fixture::new();
7942        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7943        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
7944        assert_eq!(
7945            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
7946            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
7947        );
7948        assert_eq!(
7949            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
7950            ":1\r\n"
7951        );
7952        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
7953        // The last member going takes the key with it.
7954        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
7955        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7956        assert_eq!(
7957            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
7958            ":0\r\n"
7959        );
7960        assert_eq!(
7961            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
7962            "-ERR value is not an integer or out of range\r\n"
7963        );
7964    }
7965
7966    /// The algebra, which is one gather and three names for it.
7967    #[test]
7968    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
7969        let mut f = Fixture::new();
7970        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7971        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
7972        assert_eq!(
7973            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
7974            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
7975        );
7976        // The scores are added where a member is in both, and the answer comes
7977        // out in the order those combined scores put it in.
7978        assert_eq!(
7979            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
7980            "*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"
7981        );
7982        assert_eq!(
7983            f.run(&[
7984                b"ZUNION",
7985                b"2",
7986                b"z",
7987                b"y",
7988                b"WEIGHTS",
7989                b"2",
7990                b"3",
7991                b"WITHSCORES"
7992            ]),
7993            "*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"
7994        );
7995        assert_eq!(
7996            f.run(&[
7997                b"ZUNION",
7998                b"2",
7999                b"z",
8000                b"y",
8001                b"AGGREGATE",
8002                b"MIN",
8003                b"WITHSCORES"
8004            ]),
8005            "*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"
8006        );
8007        assert_eq!(
8008            f.run(&[
8009                b"ZUNION",
8010                b"2",
8011                b"z",
8012                b"y",
8013                b"AGGREGATE",
8014                b"MAX",
8015                b"WITHSCORES"
8016            ]),
8017            "*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"
8018        );
8019        assert_eq!(
8020            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
8021            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
8022        );
8023        assert_eq!(
8024            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
8025            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
8026        );
8027        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
8028        // A plain set is an input, and it behaves as a sorted set in which
8029        // every member scores one.
8030        f.run(&[b"SADD", b"p", b"a", b"d"]);
8031        assert_eq!(
8032            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
8033            "*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"
8034        );
8035        // A difference never combines two scores, so it has nothing for either
8036        // of the two options to do and refuses both.
8037        for cmd in [
8038            &[
8039                b"ZDIFF".as_slice(),
8040                b"2",
8041                b"z",
8042                b"y",
8043                b"WEIGHTS",
8044                b"1",
8045                b"1",
8046            ][..],
8047            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
8048        ] {
8049            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8050        }
8051    }
8052
8053    /// The count of keys, which is what lets a key be named `WEIGHTS`.
8054    #[test]
8055    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
8056        let mut f = Fixture::new();
8057        f.run(&[b"ZADD", b"z", b"1", b"a"]);
8058        f.run(&[b"ZADD", b"y", b"2", b"b"]);
8059        // Redis names the command in this one, so each spelling says its own.
8060        assert_eq!(
8061            f.run(&[b"ZUNION", b"0", b"z"]),
8062            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8063        );
8064        assert_eq!(
8065            f.run(&[b"ZUNION", b"-1", b"z"]),
8066            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
8067        );
8068        assert_eq!(
8069            f.run(&[b"ZINTERCARD", b"0", b"z"]),
8070            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
8071        );
8072        // A count bigger than the line is a plain syntax error, which reads
8073        // oddly and is what Redis says.
8074        assert_eq!(
8075            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
8076            "-ERR syntax error\r\n"
8077        );
8078        assert_eq!(
8079            f.run(&[b"ZUNION", b"x", b"z"]),
8080            "-ERR value is not an integer or out of range\r\n"
8081        );
8082        // A WEIGHTS list that is not one per key is a syntax error, and a
8083        // weight that is not a number gets a sentence of its own.
8084        assert_eq!(
8085            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
8086            "-ERR syntax error\r\n"
8087        );
8088        assert_eq!(
8089            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
8090            "-ERR weight value is not a float\r\n"
8091        );
8092        assert_eq!(
8093            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
8094            "-ERR syntax error\r\n"
8095        );
8096    }
8097
8098    /// The three store forms, which answer a count and take no WITHSCORES.
8099    #[test]
8100    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
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!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
8105        assert_eq!(
8106            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
8107            "*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"
8108        );
8109        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
8110        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
8111        // An empty result deletes the destination rather than leaving an empty
8112        // sorted set, because an empty one does not exist.
8113        assert_eq!(
8114            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
8115            ":0\r\n"
8116        );
8117        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
8118        // The destination is allowed to name its own source.
8119        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
8120        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
8121        for cmd in [
8122            &[
8123                b"ZUNIONSTORE".as_slice(),
8124                b"d",
8125                b"2",
8126                b"z",
8127                b"y",
8128                b"WITHSCORES",
8129            ][..],
8130            &[
8131                b"ZDIFFSTORE",
8132                b"d",
8133                b"2",
8134                b"z",
8135                b"y",
8136                b"WEIGHTS",
8137                b"1",
8138                b"1",
8139            ],
8140        ] {
8141            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8142        }
8143    }
8144
8145    /// `ZINTERCARD`, which counts without building anything.
8146    #[test]
8147    fn intercard_counts_and_stops_at_its_limit() {
8148        let mut f = Fixture::new();
8149        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8150        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
8151        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
8152        // A limit of zero is no limit, which is Redis's reading of it.
8153        assert_eq!(
8154            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
8155            ":2\r\n"
8156        );
8157        assert_eq!(
8158            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
8159            ":1\r\n"
8160        );
8161        // A negative limit and a limit that is not a number at all get the same
8162        // sentence, which looks like a mistake in Redis and is copied as one.
8163        let bad = "-ERR LIMIT can't be negative\r\n";
8164        assert_eq!(
8165            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
8166            bad
8167        );
8168        assert_eq!(
8169            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
8170            bad
8171        );
8172        for cmd in [
8173            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
8174            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
8175            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
8176        ] {
8177            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
8178        }
8179    }
8180
8181    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
8182    #[test]
8183    fn a_draw_answers_one_member_or_an_array_of_them() {
8184        let mut f = Fixture::new();
8185        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8186        // No count is one member or a nil, a count is an array that may be
8187        // empty, and those are two reply types the client has to tell apart.
8188        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
8189        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
8190        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
8191        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
8192        // A positive count draws without replacement, so a count over the size
8193        // answers the whole set and never a member twice.
8194        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
8195        assert!(all.starts_with("*3\r\n"), "{all}");
8196        for m in ["a", "b", "c"] {
8197            assert!(all.contains(m), "{all}");
8198        }
8199        // A negative one draws with replacement and answers exactly as many as
8200        // it was asked for, whatever the size of the set.
8201        assert!(
8202            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
8203            "five draws with replacement"
8204        );
8205        assert!(
8206            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
8207                .starts_with("*4\r\n"),
8208            "two pairs, flat on RESP2"
8209        );
8210        f.out = Out::new(Proto::Resp3);
8211        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
8212        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
8213        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
8214        f.out = Out::new(Proto::Resp2);
8215        assert_eq!(
8216            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
8217            "-ERR syntax error\r\n"
8218        );
8219        assert_eq!(
8220            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
8221            "-ERR value is not an integer or out of range\r\n"
8222        );
8223    }
8224
8225    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
8226    #[test]
8227    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
8228        let mut f = Fixture::new();
8229        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8230        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";
8231        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8232        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
8233        assert_eq!(
8234            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
8235            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8236        );
8237        assert_eq!(
8238            f.run(&[b"ZSCAN", b"nokey", b"0"]),
8239            "*2\r\n$1\r\n0\r\n*0\r\n"
8240        );
8241        // A score stays a bulk string on RESP3, which is the one place the two
8242        // protocols agree about a score and everywhere else they do not.
8243        f.out = Out::new(Proto::Resp3);
8244        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
8245        f.out = Out::new(Proto::Resp2);
8246        assert_eq!(
8247            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
8248            "-ERR NOVALUES option can only be used in HSCAN\r\n"
8249        );
8250        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
8251        assert_eq!(
8252            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
8253            "-ERR syntax error\r\n"
8254        );
8255    }
8256
8257    /// The count is what decides the shape, and its value is not.
8258    #[test]
8259    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
8260        let mut f = Fixture::new();
8261        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8262        // No count, so one flat pair, and the score is a bulk string on RESP2.
8263        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
8264        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
8265        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8266        // A count, so pairs, and on RESP2 they are flattened into one run.
8267        assert_eq!(
8268            f.run(&[b"ZPOPMIN", b"z", b"2"]),
8269            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
8270        );
8271        // An empty array rather than a null, which is where a sorted set pop and
8272        // a list pop part company, and the same answer a count of zero gives.
8273        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
8274        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
8275        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
8276        // The last member takes the key with it.
8277        assert_eq!(
8278            f.run(&[b"ZPOPMIN", b"z", b"9"]),
8279            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
8280        );
8281        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8282
8283        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
8284        f.out = Out::new(Proto::Resp3);
8285        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
8286        assert_eq!(
8287            f.run(&[b"ZPOPMIN", b"z", b"1"]),
8288            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
8289        );
8290        f.out = Out::new(Proto::Resp2);
8291        // Both of these are the range error rather than the usual sentence about
8292        // integers, which is the odd answer and so the one worth copying.
8293        let bad = "-ERR value is out of range, must be positive\r\n";
8294        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
8295        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
8296        assert_eq!(
8297            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
8298            "-ERR syntax error\r\n"
8299        );
8300    }
8301
8302    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
8303    #[test]
8304    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
8305        let mut f = Fixture::new();
8306        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8307        assert_eq!(
8308            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
8309            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
8310        );
8311        // Nested on RESP2 as well, because the key name is already in front of
8312        // the pairs and there is nothing left to flatten into.
8313        assert_eq!(
8314            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
8315            "*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"
8316        );
8317        // A null array and not a null, the same as LMPOP.
8318        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
8319        f.out = Out::new(Proto::Resp3);
8320        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
8321        f.out = Out::new(Proto::Resp2);
8322        let numkeys = "-ERR numkeys should be greater than 0\r\n";
8323        for bad in [
8324            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
8325            &[b"ZMPOP", b"-1", b"z", b"MIN"],
8326            &[b"ZMPOP", b"x", b"z", b"MIN"],
8327        ] {
8328            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
8329        }
8330        let count = "-ERR count should be greater than 0\r\n";
8331        for bad in [
8332            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
8333            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
8334            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
8335        ] {
8336            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
8337        }
8338        let syntax = "-ERR syntax error\r\n";
8339        for bad in [
8340            // Two keys named and one given, so the word that should have been
8341            // the direction is a key and there is no direction left.
8342            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
8343            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
8344            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
8345            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
8346        ] {
8347            assert_eq!(f.run(bad), syntax, "{bad:?}");
8348        }
8349    }
8350
8351    /// The three that wait, when there is something there and they do not have
8352    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
8353    #[test]
8354    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
8355        let mut f = Fixture::new();
8356        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
8357        assert_eq!(
8358            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
8359            (
8360                Flow::Continue,
8361                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
8362            )
8363        );
8364        assert_eq!(
8365            f.run(&[b"BZPOPMAX", b"z", b"0"]),
8366            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
8367        );
8368        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
8369        assert_eq!(
8370            f.run(&[
8371                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
8372            ]),
8373            "*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"
8374        );
8375        f.out = Out::new(Proto::Resp3);
8376        assert_eq!(
8377            f.run(&[b"BZPOPMIN", b"z", b"0"]),
8378            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
8379        );
8380        f.out = Out::new(Proto::Resp2);
8381        // Nothing to take, so the client is parked and nothing was written.
8382        assert_eq!(
8383            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
8384            (Flow::Block, String::new())
8385        );
8386        assert_eq!(
8387            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
8388            (Flow::Block, String::new())
8389        );
8390        // The timeout is read before the key count, so this complains about the
8391        // timeout and not about the count.
8392        assert_eq!(
8393            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
8394            "-ERR timeout is not a float or out of range\r\n"
8395        );
8396        assert_eq!(
8397            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
8398            "-ERR numkeys should be greater than 0\r\n"
8399        );
8400        assert_eq!(
8401            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
8402            "-ERR timeout is negative\r\n"
8403        );
8404    }
8405
8406    /// A parked sorted set client is served by whatever puts a member under one
8407    /// of its keys, and is not served by something of another type landing
8408    /// there.
8409    #[test]
8410    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
8411        let mut f = Fixture::new();
8412        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
8413        assert_eq!(f.server.waiters().len(), 1);
8414        // A string under the key is not what it asked for, so it stays parked
8415        // rather than being handed a WRONGTYPE on a command that was accepted.
8416        f.run(&[b"SET", b"z", b"v"]);
8417        let mut out = Out::new(Proto::Resp2);
8418        assert!(!f.server.serve_waiter(0, 0, &mut out));
8419        assert!(out.as_slice().is_empty());
8420        f.run(&[b"DEL", b"z"]);
8421        f.run(&[b"ZADD", b"z", b"5", b"m"]);
8422        assert!(f.server.serve_waiter(0, 0, &mut out));
8423        assert_eq!(
8424            core::str::from_utf8(out.as_slice()).expect("ascii"),
8425            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
8426        );
8427        // And the member is gone, which is what makes a queue of workers on a
8428        // sorted set work at all.
8429        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
8430    }
8431
8432    #[test]
8433    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
8434        let mut f = Fixture::new();
8435        f.run(&[b"SET", b"s", b"v"]);
8436        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8437        for cmd in [
8438            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
8439            &[b"ZINCRBY", b"s", b"1", b"a"],
8440            &[b"ZCARD", b"s"],
8441            &[b"ZSCORE", b"s", b"a"],
8442            &[b"ZMSCORE", b"s", b"a"],
8443            &[b"ZREM", b"s", b"a"],
8444            &[b"ZRANK", b"s", b"a"],
8445            &[b"ZREVRANK", b"s", b"a"],
8446            &[b"ZCOUNT", b"s", b"1", b"2"],
8447            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
8448            &[b"ZRANGE", b"s", b"0", b"-1"],
8449            &[b"ZREVRANGE", b"s", b"0", b"-1"],
8450            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
8451            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
8452            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
8453            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
8454            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
8455            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
8456            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
8457            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
8458            &[b"ZUNION", b"1", b"s"],
8459            &[b"ZINTER", b"1", b"s"],
8460            &[b"ZDIFF", b"1", b"s"],
8461            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
8462            &[b"ZINTERSTORE", b"d", b"1", b"s"],
8463            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
8464            &[b"ZINTERCARD", b"1", b"s"],
8465            &[b"ZRANDMEMBER", b"s"],
8466            &[b"ZSCAN", b"s", b"0"],
8467            &[b"ZPOPMIN", b"s"],
8468            &[b"ZPOPMAX", b"s", b"2"],
8469            &[b"ZMPOP", b"1", b"s", b"MIN"],
8470            &[b"BZPOPMIN", b"s", b"0"],
8471            &[b"BZPOPMAX", b"s", b"0"],
8472            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
8473        ] {
8474            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
8475        }
8476        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
8477    }
8478
8479    /// The same churn the set, the string and the list get, because a sorted
8480    /// set that leaks a tree node per add looks exactly like one that does not
8481    /// until it has run for an afternoon.
8482    #[test]
8483    fn churning_sorted_sets_does_not_grow_the_server() {
8484        let mut f = Fixture::new();
8485        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
8486        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
8487        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
8488        for i in 0..200 {
8489            args.push(&scores[i]);
8490            args.push(&members[i]);
8491        }
8492
8493        f.run(&args);
8494        f.run(&[b"DEL", b"z"]);
8495        f.server.compact_step();
8496        let after_first = f.server.memory_bytes();
8497
8498        for _ in 0..200 {
8499            f.run(&args);
8500            f.run(&[b"DEL", b"z"]);
8501            f.server.compact_step();
8502        }
8503        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
8504        assert!(
8505            f.server.memory_bytes() <= after_first * 2,
8506            "held {} after two hundred passes against {after_first} after one",
8507            f.server.memory_bytes()
8508        );
8509    }
8510
8511    // ------------------------------------------------------------------- geo
8512
8513    /// The three places every Redis geo example uses, and one more.
8514    ///
8515    /// Every reply this section asserts on came off a running 8.10.1 with these
8516    /// three loaded, byte for byte, including the number of digits in a
8517    /// coordinate and the four places on a distance.
8518    fn sicily(f: &mut Fixture) {
8519        f.run(&[
8520            b"GEOADD",
8521            b"Sicily",
8522            b"13.361389",
8523            b"38.115556",
8524            b"Palermo",
8525            b"15.087269",
8526            b"37.502669",
8527            b"Catania",
8528        ]);
8529        f.run(&[
8530            b"GEOADD",
8531            b"Sicily",
8532            b"13.583333",
8533            b"37.316667",
8534            b"Agrigento",
8535        ]);
8536    }
8537
8538    #[test]
8539    fn places_go_in_as_scores_and_come_back_as_positions() {
8540        let mut f = Fixture::new();
8541        assert_eq!(
8542            f.run(&[
8543                b"GEOADD",
8544                b"Sicily",
8545                b"13.361389",
8546                b"38.115556",
8547                b"Palermo",
8548                b"15.087269",
8549                b"37.502669",
8550                b"Catania"
8551            ]),
8552            ":2\r\n"
8553        );
8554        // A geo key is a sorted set and says so, which is not an implementation
8555        // detail either: a client removes a place with ZREM and counts them
8556        // with ZCARD, and the score is the number a real server stores.
8557        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
8558        assert_eq!(
8559            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
8560            "$16\r\n3479099956230698\r\n"
8561        );
8562        assert_eq!(
8563            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
8564            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
8565        );
8566        assert_eq!(
8567            f.run(&[
8568                b"GEOHASH",
8569                b"Sicily",
8570                b"Palermo",
8571                b"Catania",
8572                b"NonExisting"
8573            ]),
8574            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
8575        );
8576        // A key that is not there is an empty one, and the two nulls are not
8577        // the same null: GEOPOS answers the array one and GEOHASH the string
8578        // one, which a RESP2 client can tell apart.
8579        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
8580        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
8581    }
8582
8583    #[test]
8584    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
8585        let mut f = Fixture::new();
8586        sicily(&mut f);
8587        assert_eq!(
8588            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
8589            "$11\r\n166274.1516\r\n"
8590        );
8591        assert_eq!(
8592            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
8593            "$8\r\n166.2742\r\n"
8594        );
8595        assert_eq!(
8596            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
8597            "$8\r\n103.3182\r\n"
8598        );
8599        // A member that is not there and a key that is not there are the same
8600        // nil, and the unit is read before the key is looked up, so a bad unit
8601        // on a missing key is still an error.
8602        assert_eq!(
8603            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
8604            "$-1\r\n"
8605        );
8606        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
8607        assert_eq!(
8608            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
8609            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
8610        );
8611        assert_eq!(
8612            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
8613            "-ERR syntax error\r\n"
8614        );
8615    }
8616
8617    #[test]
8618    fn a_search_finds_what_is_inside_it_nearest_first() {
8619        let mut f = Fixture::new();
8620        sicily(&mut f);
8621        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
8622        assert_eq!(
8623            f.run(&[
8624                b"GEOSEARCH",
8625                b"Sicily",
8626                b"FROMLONLAT",
8627                b"15",
8628                b"37",
8629                b"BYRADIUS",
8630                b"200",
8631                b"km",
8632                b"ASC"
8633            ]),
8634            all
8635        );
8636        // The older spelling of the same search, which is the same nine boxes
8637        // and the same order.
8638        assert_eq!(
8639            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
8640            all
8641        );
8642        assert_eq!(
8643            f.run(&[
8644                b"GEORADIUS_RO",
8645                b"Sicily",
8646                b"15",
8647                b"37",
8648                b"200",
8649                b"km",
8650                b"ASC"
8651            ]),
8652            all
8653        );
8654        // A count with no ordering means the nearest ones, so DESC has to be
8655        // asked for to get the far end.
8656        assert_eq!(
8657            f.run(&[
8658                b"GEORADIUS",
8659                b"Sicily",
8660                b"15",
8661                b"37",
8662                b"200",
8663                b"km",
8664                b"DESC",
8665                b"COUNT",
8666                b"1"
8667            ]),
8668            "*1\r\n$7\r\nPalermo\r\n"
8669        );
8670        assert_eq!(
8671            f.run(&[
8672                b"GEORADIUS",
8673                b"Sicily",
8674                b"15",
8675                b"37",
8676                b"200",
8677                b"km",
8678                b"COUNT",
8679                b"1"
8680            ]),
8681            "*1\r\n$7\r\nCatania\r\n"
8682        );
8683        // Nothing inside a kilometre of that point, and nothing in a key that
8684        // is not there, and both are the empty array rather than an error.
8685        let empty = "*0\r\n";
8686        assert_eq!(
8687            f.run(&[
8688                b"GEOSEARCH",
8689                b"Sicily",
8690                b"FROMLONLAT",
8691                b"15",
8692                b"37",
8693                b"BYRADIUS",
8694                b"1",
8695                b"km"
8696            ]),
8697            empty
8698        );
8699        assert_eq!(
8700            f.run(&[
8701                b"GEOSEARCH",
8702                b"nokey",
8703                b"FROMLONLAT",
8704                b"15",
8705                b"37",
8706                b"BYRADIUS",
8707                b"1",
8708                b"km"
8709            ]),
8710            empty
8711        );
8712        assert_eq!(
8713            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
8714            empty
8715        );
8716    }
8717
8718    #[test]
8719    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
8720        let mut f = Fixture::new();
8721        sicily(&mut f);
8722        assert_eq!(
8723            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
8724            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8725        );
8726        // The member itself is nothing away from itself, which is where the
8727        // fixed point writer's zero shows up on the wire.
8728        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";
8729        assert_eq!(
8730            f.run(&[
8731                b"GEORADIUSBYMEMBER_RO",
8732                b"Sicily",
8733                b"Agrigento",
8734                b"100",
8735                b"km",
8736                b"WITHDIST"
8737            ]),
8738            with_dist
8739        );
8740        assert_eq!(
8741            f.run(&[
8742                b"GEOSEARCH",
8743                b"Sicily",
8744                b"FROMMEMBER",
8745                b"Agrigento",
8746                b"BYRADIUS",
8747                b"100",
8748                b"km",
8749                b"ASC",
8750                b"WITHDIST"
8751            ]),
8752            with_dist
8753        );
8754        assert_eq!(
8755            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
8756            "-ERR could not decode requested zset member\r\n"
8757        );
8758    }
8759
8760    #[test]
8761    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
8762        let mut f = Fixture::new();
8763        sicily(&mut f);
8764        // Three options asked for, so each result is a four element array of
8765        // the member, the distance, the hash and a pair. The order of the three
8766        // is Redis's and not the order they were written in the command.
8767        assert_eq!(
8768            f.run(&[
8769                b"GEOSEARCH",
8770                b"Sicily",
8771                b"FROMLONLAT",
8772                b"15",
8773                b"37",
8774                b"BYBOX",
8775                b"400",
8776                b"400",
8777                b"km",
8778                b"ASC",
8779                b"WITHCOORD",
8780                b"WITHDIST",
8781                b"WITHHASH"
8782            ]),
8783            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
8784             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
8785             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
8786             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
8787             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
8788             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
8789        );
8790    }
8791
8792    #[test]
8793    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
8794        let mut f = Fixture::new();
8795        sicily(&mut f);
8796        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
8797                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
8798                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
8799        assert_eq!(
8800            f.run(&[
8801                b"GEOSEARCHSTORE",
8802                b"dst",
8803                b"Sicily",
8804                b"FROMLONLAT",
8805                b"15",
8806                b"37",
8807                b"BYRADIUS",
8808                b"200",
8809                b"km",
8810                b"ASC"
8811            ]),
8812            ":3\r\n"
8813        );
8814        assert_eq!(
8815            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
8816            hashes
8817        );
8818        // The same again through the older spelling, which stores the same
8819        // scores, so a key written by either is a geo key.
8820        assert_eq!(
8821            f.run(&[
8822                b"GEORADIUS",
8823                b"Sicily",
8824                b"15",
8825                b"37",
8826                b"200",
8827                b"km",
8828                b"STORE",
8829                b"dst3"
8830            ]),
8831            ":3\r\n"
8832        );
8833        assert_eq!(
8834            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
8835            hashes
8836        );
8837        // STOREDIST stores the distance in the search unit instead, and those
8838        // are full doubles rather than the four places WITHDIST writes. The
8839        // numbers on the right are what 8.10.1 stored for this search, and they
8840        // are compared with a tolerance rather than byte for byte because the
8841        // last bit of a haversine is the platform's sin, cos and asin: this
8842        // machine and that one disagree in the sixteenth digit, and so do two
8843        // Redis builds. Everything a client actually reads back is four places
8844        // and is asserted exactly above.
8845        assert_eq!(
8846            f.run(&[
8847                b"GEOSEARCHSTORE",
8848                b"dst2",
8849                b"Sicily",
8850                b"FROMLONLAT",
8851                b"15",
8852                b"37",
8853                b"BYRADIUS",
8854                b"200",
8855                b"km",
8856                b"ASC",
8857                b"STOREDIST"
8858            ]),
8859            ":3\r\n"
8860        );
8861        for (member, want) in [
8862            ("Catania", 56.441_257_870_158_19),
8863            ("Agrigento", 130.423_487_067_147_14),
8864            ("Palermo", 190.442_429_847_757_92),
8865        ] {
8866            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
8867            let got: f64 = reply
8868                .trim_start_matches(|c: char| c != '\n')
8869                .trim()
8870                .parse()
8871                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
8872            assert!(
8873                (got - want).abs() < 1e-9,
8874                "{member} scored {got} not {want}"
8875            );
8876        }
8877        // The order they went in is the order the scores put them in, which is
8878        // the point of storing the distance rather than the hash.
8879        assert_eq!(
8880            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
8881            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
8882        );
8883        // A search that finds nothing takes the destination with it rather than
8884        // leaving what was there, and a source key that is not there is a
8885        // search that finds nothing.
8886        assert_eq!(
8887            f.run(&[
8888                b"GEOSEARCHSTORE",
8889                b"dst",
8890                b"nokey",
8891                b"FROMLONLAT",
8892                b"15",
8893                b"37",
8894                b"BYRADIUS",
8895                b"200",
8896                b"km"
8897            ]),
8898            ":0\r\n"
8899        );
8900        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
8901    }
8902
8903    #[test]
8904    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
8905        let mut f = Fixture::new();
8906        sicily(&mut f);
8907        // XX on a member that is already where it is changes nothing, and NX on
8908        // one that is there refuses to move it.
8909        assert_eq!(
8910            f.run(&[
8911                b"GEOADD",
8912                b"Sicily",
8913                b"XX",
8914                b"CH",
8915                b"13.361389",
8916                b"38.115556",
8917                b"Palermo"
8918            ]),
8919            ":0\r\n"
8920        );
8921        assert_eq!(
8922            f.run(&[
8923                b"GEOADD",
8924                b"Sicily",
8925                b"NX",
8926                b"13.361389",
8927                b"38.9",
8928                b"Palermo"
8929            ]),
8930            ":0\r\n"
8931        );
8932        assert_eq!(
8933            f.run(&[
8934                b"GEOADD",
8935                b"Sicily",
8936                b"CH",
8937                b"13.361389",
8938                b"38.9",
8939                b"Palermo"
8940            ]),
8941            ":1\r\n"
8942        );
8943        // Out of range, and nothing is stored: the whole call is refused rather
8944        // than the good pairs going in and the bad one stopping it.
8945        assert_eq!(
8946            f.run(&[
8947                b"GEOADD",
8948                b"new",
8949                b"13.361389",
8950                b"38.115556",
8951                b"here",
8952                b"181",
8953                b"38",
8954                b"there"
8955            ]),
8956            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
8957        );
8958        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
8959        assert_eq!(
8960            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
8961            "-ERR value is not a valid float\r\n"
8962        );
8963        // The count of triples is checked before the two gates are, and a call
8964        // with no triples at all reaches the same sentence.
8965        assert_eq!(
8966            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
8967            "-ERR syntax error\r\n"
8968        );
8969        assert_eq!(
8970            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
8971            "-ERR syntax error\r\n"
8972        );
8973        assert_eq!(
8974            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
8975            "-ERR syntax error\r\n"
8976        );
8977        assert_eq!(
8978            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
8979            "-ERR wrong number of arguments for 'geoadd' command\r\n"
8980        );
8981    }
8982
8983    /// The sentences a search answers, which are its contract as much as the
8984    /// results are.
8985    #[test]
8986    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
8987        let mut f = Fixture::new();
8988        sicily(&mut f);
8989        let cases: &[(&[&[u8]], &str)] = &[
8990            (
8991                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
8992                "-ERR need numeric radius\r\n",
8993            ),
8994            (
8995                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
8996                "-ERR radius cannot be negative\r\n",
8997            ),
8998            (
8999                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
9000                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
9001            ),
9002            (
9003                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
9004                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
9005            ),
9006            (
9007                &[
9008                    b"GEOSEARCH",
9009                    b"Sicily",
9010                    b"FROMLONLAT",
9011                    b"15",
9012                    b"37",
9013                    b"BYBOX",
9014                    b"x",
9015                    b"1",
9016                    b"km",
9017                ],
9018                "-ERR need numeric width\r\n",
9019            ),
9020            (
9021                &[
9022                    b"GEOSEARCH",
9023                    b"Sicily",
9024                    b"FROMLONLAT",
9025                    b"15",
9026                    b"37",
9027                    b"BYBOX",
9028                    b"1",
9029                    b"y",
9030                    b"km",
9031                ],
9032                "-ERR need numeric height\r\n",
9033            ),
9034            (
9035                &[
9036                    b"GEOSEARCH",
9037                    b"Sicily",
9038                    b"FROMLONLAT",
9039                    b"15",
9040                    b"37",
9041                    b"BYBOX",
9042                    b"-1",
9043                    b"1",
9044                    b"km",
9045                ],
9046                "-ERR height or width cannot be negative\r\n",
9047            ),
9048            (
9049                &[
9050                    b"GEOSEARCH",
9051                    b"Sicily",
9052                    b"FROMLONLAT",
9053                    b"15",
9054                    b"37",
9055                    b"BYRADIUS",
9056                    b"1",
9057                    b"km",
9058                    b"ANY",
9059                ],
9060                "-ERR the ANY argument requires COUNT argument\r\n",
9061            ),
9062            (
9063                &[
9064                    b"GEOSEARCH",
9065                    b"Sicily",
9066                    b"FROMLONLAT",
9067                    b"15",
9068                    b"37",
9069                    b"BYRADIUS",
9070                    b"1",
9071                    b"km",
9072                    b"COUNT",
9073                    b"0",
9074                ],
9075                "-ERR COUNT must be > 0\r\n",
9076            ),
9077            (
9078                &[
9079                    b"GEOSEARCH",
9080                    b"Sicily",
9081                    b"BYRADIUS",
9082                    b"1",
9083                    b"km",
9084                    b"BYBOX",
9085                    b"1",
9086                    b"1",
9087                    b"km",
9088                ],
9089                "-ERR syntax error\r\n",
9090            ),
9091            (
9092                &[
9093                    b"GEOSEARCH",
9094                    b"Sicily",
9095                    b"FROMMEMBER",
9096                    b"Palermo",
9097                    b"FROMLONLAT",
9098                    b"1",
9099                    b"2",
9100                    b"BYRADIUS",
9101                    b"1",
9102                    b"km",
9103                ],
9104                "-ERR syntax error\r\n",
9105            ),
9106            // The two options a GEOSEARCH cannot leave out, each with its own
9107            // sentence, and the command quoted the way the client spelled it.
9108            (
9109                &[
9110                    b"geosearch",
9111                    b"Sicily",
9112                    b"BYRADIUS",
9113                    b"1",
9114                    b"km",
9115                    b"ASC",
9116                    b"WITHDIST",
9117                ],
9118                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
9119            ),
9120            (
9121                &[
9122                    b"GEOSEARCH",
9123                    b"Sicily",
9124                    b"FROMLONLAT",
9125                    b"15",
9126                    b"37",
9127                    b"ASC",
9128                    b"WITHDIST",
9129                ],
9130                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
9131            ),
9132            // A store cannot also be asked for the distance, and the two
9133            // families name themselves differently in the same sentence.
9134            (
9135                &[
9136                    b"GEOSEARCHSTORE",
9137                    b"d",
9138                    b"Sicily",
9139                    b"FROMLONLAT",
9140                    b"15",
9141                    b"37",
9142                    b"BYRADIUS",
9143                    b"1",
9144                    b"km",
9145                    b"WITHCOORD",
9146                ],
9147                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9148            ),
9149            (
9150                &[
9151                    b"GEORADIUS",
9152                    b"Sicily",
9153                    b"15",
9154                    b"37",
9155                    b"1",
9156                    b"km",
9157                    b"WITHDIST",
9158                    b"STORE",
9159                    b"d",
9160                ],
9161                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
9162            ),
9163            // The read only forms have no store at all, so the word is a stray
9164            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
9165            (
9166                &[
9167                    b"GEORADIUS_RO",
9168                    b"Sicily",
9169                    b"15",
9170                    b"37",
9171                    b"1",
9172                    b"km",
9173                    b"STORE",
9174                    b"d",
9175                ],
9176                "-ERR syntax error\r\n",
9177            ),
9178            (
9179                &[
9180                    b"GEOSEARCH",
9181                    b"Sicily",
9182                    b"FROMLONLAT",
9183                    b"15",
9184                    b"37",
9185                    b"BYRADIUS",
9186                    b"1",
9187                    b"km",
9188                    b"STOREDIST",
9189                ],
9190                "-ERR syntax error\r\n",
9191            ),
9192        ];
9193        for (parts, want) in cases {
9194            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
9195        }
9196    }
9197
9198    /// A wrong type wins over a bad argument, because the key is looked up
9199    /// first, and every one of the ten says the same thing about it.
9200    #[test]
9201    fn every_geo_command_says_wrongtype() {
9202        let mut f = Fixture::new();
9203        f.run(&[b"SET", b"s", b"v"]);
9204        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9205        let cases: &[&[&[u8]]] = &[
9206            &[b"GEOADD", b"s", b"13", b"38", b"m"],
9207            &[b"GEOPOS", b"s", b"m"],
9208            &[b"GEOHASH", b"s", b"m"],
9209            &[b"GEODIST", b"s", b"a", b"b"],
9210            &[
9211                b"GEOSEARCH",
9212                b"s",
9213                b"FROMLONLAT",
9214                b"15",
9215                b"37",
9216                b"BYRADIUS",
9217                b"1",
9218                b"km",
9219            ],
9220            &[
9221                b"GEOSEARCHSTORE",
9222                b"d",
9223                b"s",
9224                b"FROMLONLAT",
9225                b"15",
9226                b"37",
9227                b"BYRADIUS",
9228                b"1",
9229                b"km",
9230            ],
9231            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
9232            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
9233            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
9234            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
9235        ];
9236        for case in cases {
9237            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
9238        }
9239        // And it wins over an argument that will not parse, which is the whole
9240        // reason the lookup comes first.
9241        assert_eq!(
9242            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
9243            wrong
9244        );
9245    }
9246
9247    // ----------------------------------------------------------------- array
9248
9249    #[test]
9250    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
9251        let mut f = Fixture::new();
9252        // Three consecutive positions from a high index, and the reply is how
9253        // many of them were empty before rather than how many were written.
9254        assert_eq!(
9255            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
9256            ":3\r\n"
9257        );
9258        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
9259        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
9260        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
9261        // A hole and a key that is not there are the same answer.
9262        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
9263        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
9264        assert_eq!(
9265            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
9266            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
9267        );
9268        // Scattered pairs in one command, last write wins within it.
9269        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
9270        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
9271    }
9272
9273    /// The two numbers an array reports are not the same number, and one of
9274    /// them does not fit a signed integer.
9275    #[test]
9276    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
9277        let mut f = Fixture::new();
9278        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
9279        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
9280        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
9281        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9282        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9283        // Deleting in the middle leaves the high water mark where it was.
9284        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
9285        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
9286        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
9287
9288        // The top of the space is addressable, and its length is a number with
9289        // bit sixty three set, so the reply has to be unsigned or it comes back
9290        // negative.
9291        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
9292        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
9293        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
9294        // And one past it does not exist, so a write that would reach it fails
9295        // before any of it lands.
9296        assert_eq!(
9297            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
9298            "-ERR array index overflow\r\n"
9299        );
9300        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
9301    }
9302
9303    /// One reply per position and not one per element, which is the whole
9304    /// reason the range is capped.
9305    #[test]
9306    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
9307        let mut f = Fixture::new();
9308        f.run(&[b"ARSET", b"a", b"1", b"x"]);
9309        assert_eq!(
9310            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
9311            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
9312        );
9313        // The two ends may come in either order, and the answer is reversed
9314        // rather than empty.
9315        assert_eq!(
9316            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
9317            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
9318        );
9319        // A key that is not there reads like an array of nothing but holes.
9320        assert_eq!(
9321            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
9322            "*2\r\n$-1\r\n$-1\r\n"
9323        );
9324        // A range wider than a million positions is refused and not trimmed,
9325        // because against a missing key it is a request for as many nulls as
9326        // the range is wide.
9327        assert_eq!(
9328            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
9329            "-ERR range exceeds maximum of 1000000 items\r\n"
9330        );
9331    }
9332
9333    /// Every index in the argument list is read before the key is touched, so
9334    /// a bad one at the end leaves nothing half written.
9335    #[test]
9336    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
9337        let mut f = Fixture::new();
9338        assert_eq!(
9339            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
9340            "-ERR invalid array index\r\n"
9341        );
9342        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9343        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
9344        assert_eq!(
9345            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
9346            "-ERR invalid array index\r\n"
9347        );
9348        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
9349        // An index is unsigned here, so the numbers a list would take are not
9350        // the last element, they are errors.
9351        assert_eq!(
9352            f.run(&[b"ARGET", b"a", b"-1"]),
9353            "-ERR invalid array index\r\n"
9354        );
9355        // And a pair list with an odd tail is an arity error rather than a
9356        // syntax one.
9357        assert_eq!(
9358            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
9359            "-ERR wrong number of arguments for 'armset' command\r\n"
9360        );
9361        assert_eq!(
9362            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
9363            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
9364        );
9365    }
9366
9367    #[test]
9368    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
9369        let mut f = Fixture::new();
9370        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
9371        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
9372        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
9373        // Two ranges in one command, and the second one covers the whole space
9374        // without walking it.
9375        assert_eq!(
9376            f.run(&[
9377                b"ARDELRANGE",
9378                b"a",
9379                b"100",
9380                b"200",
9381                b"0",
9382                b"18446744073709551614"
9383            ]),
9384            ":2\r\n"
9385        );
9386        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
9387        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
9388        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
9389    }
9390
9391    /// A value goes out as the bytes it came in as, whichever of the three ways
9392    /// the array found to store it.
9393    #[test]
9394    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
9395        let mut f = Fixture::new();
9396        let long = vec![b'v'; 200];
9397        f.run(&[
9398            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
9399            b"short", b"5", &long, b"6", b"-0",
9400        ]);
9401        // 42 is an integer, 007 is not one because it does not print back the
9402        // same, 3.5 survives a double and 3.14 does not, and the last two are a
9403        // word packed string and a blob.
9404        assert_eq!(
9405            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
9406            format!(
9407                "*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",
9408                String::from_utf8_lossy(&long)
9409            )
9410        );
9411    }
9412
9413    #[test]
9414    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
9415        let mut f = Fixture::new();
9416        f.run(&[b"ARSET", b"a", b"0", b"x"]);
9417        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
9418        assert_eq!(
9419            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
9420            "$12\r\nsliced-array\r\n"
9421        );
9422        // And it is a body like any other, so the key commands work on it.
9423        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
9424        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
9425        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
9426        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
9427        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
9428        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
9429    }
9430
9431    #[test]
9432    fn every_array_command_refuses_a_key_holding_something_else() {
9433        let mut f = Fixture::new();
9434        f.run(&[b"SET", b"s", b"v"]);
9435        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9436        for cmd in [
9437            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
9438            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
9439            &[b"ARGET".as_ref(), b"s", b"0"][..],
9440            &[b"ARMGET".as_ref(), b"s", b"0"][..],
9441            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
9442            &[b"ARLEN".as_ref(), b"s"][..],
9443            &[b"ARCOUNT".as_ref(), b"s"][..],
9444            &[b"ARDEL".as_ref(), b"s", b"0"][..],
9445            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
9446            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
9447            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
9448            &[b"ARNEXT".as_ref(), b"s"][..],
9449            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
9450            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
9451            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
9452            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
9453            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
9454            &[b"ARINFO".as_ref(), b"s"][..],
9455        ] {
9456            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
9457        }
9458    }
9459
9460    /// Two of the array commands look the key up before they read the index and
9461    /// the rest read the index first, so the same broken argument gets two
9462    /// different errors depending on which command it went to.
9463    #[test]
9464    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
9465        let mut f = Fixture::new();
9466        f.run(&[b"SET", b"s", b"v"]);
9467        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9468        let bad = "-ERR invalid array index\r\n";
9469        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
9470        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
9471        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
9472        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
9473        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
9474        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
9475        // And on a key that is an array the index is just an index.
9476        f.run(&[b"ARSET", b"a", b"0", b"x"]);
9477        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
9478        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
9479    }
9480
9481    #[test]
9482    fn an_append_follows_a_cursor_the_client_can_move() {
9483        let mut f = Fixture::new();
9484        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
9485        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
9486        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
9487        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
9488        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
9489
9490        // A seek says where the next one goes, and a missing key has no cursor
9491        // to move and is not created by the asking.
9492        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
9493        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
9494        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
9495        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
9496        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
9497        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
9498        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
9499
9500        // The top of the space is the one index only ARSEEK will take, and it
9501        // leaves the cursor with nowhere to go.
9502        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
9503        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
9504        assert_eq!(
9505            f.run(&[b"ARINSERT", b"a", b"x"]),
9506            "-ERR insert index overflow\r\n"
9507        );
9508        assert_eq!(
9509            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
9510            "-ERR invalid array index\r\n"
9511        );
9512    }
9513
9514    #[test]
9515    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
9516        let mut f = Fixture::new();
9517        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
9518        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
9519        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
9520        assert_eq!(
9521            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
9522            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
9523        );
9524        // Growing it after it has wrapped puts the survivors back in the order
9525        // they arrived, which is the whole point of paying for the rebuild.
9526        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
9527        assert_eq!(
9528            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
9529            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
9530        );
9531        // The size is read before the key, so a bad one is a bad size wherever
9532        // it is sent.
9533        assert_eq!(
9534            f.run(&[b"ARRING", b"r", b"0", b"x"]),
9535            "-ERR size must be positive\r\n"
9536        );
9537        assert_eq!(
9538            f.run(&[b"ARRING", b"r", b"big", b"x"]),
9539            "-ERR invalid size\r\n"
9540        );
9541    }
9542
9543    #[test]
9544    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
9545        let mut f = Fixture::new();
9546        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
9547        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
9548        assert_eq!(
9549            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
9550            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
9551        );
9552        assert_eq!(
9553            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
9554            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
9555        );
9556        assert_eq!(
9557            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
9558            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
9559            "more than there is gets what there is"
9560        );
9561        // Nothing asked for is an empty reply, and Redis answers that before it
9562        // has read the option or looked at the key.
9563        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
9564        assert_eq!(
9565            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
9566            "-ERR syntax error\r\n"
9567        );
9568        assert_eq!(
9569            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
9570            "-ERR invalid COUNT\r\n"
9571        );
9572
9573        // With no cursor the tail of the array is the anchor, and a hole inside
9574        // the window is reported as one.
9575        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
9576        assert_eq!(
9577            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
9578            "*2\r\n$-1\r\n$1\r\nz\r\n"
9579        );
9580    }
9581
9582    #[test]
9583    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
9584        let mut f = Fixture::new();
9585        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
9586        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
9587        // The whole index space, which ARGETRANGE refuses and this one answers
9588        // in three visits because holes cost nothing.
9589        assert_eq!(
9590            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
9591            "*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"
9592        );
9593        assert_eq!(
9594            f.run(&[
9595                b"ARSCAN",
9596                b"a",
9597                b"18446744073709551614",
9598                b"0",
9599                b"LIMIT",
9600                b"1"
9601            ]),
9602            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
9603        );
9604        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
9605        assert_eq!(
9606            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
9607            "-ERR LIMIT must be positive\r\n"
9608        );
9609        assert_eq!(
9610            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
9611            "-ERR syntax error\r\n"
9612        );
9613        assert_eq!(
9614            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
9615            "-ERR wrong number of arguments for 'arscan' command\r\n"
9616        );
9617    }
9618
9619    #[test]
9620    fn a_grep_answers_the_indexes_whose_elements_match() {
9621        let mut f = Fixture::new();
9622        assert_eq!(
9623            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
9624            "*0\r\n"
9625        );
9626        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
9627
9628        // The two bounds take the ends of the array as well as an index, and a
9629        // reversed range is walked backwards the way ARSCAN walks one.
9630        assert_eq!(
9631            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
9632            "*3\r\n:0\r\n:1\r\n:2\r\n"
9633        );
9634        assert_eq!(
9635            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
9636            "*3\r\n:2\r\n:1\r\n:0\r\n"
9637        );
9638        assert_eq!(
9639            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
9640            "*2\r\n:1\r\n:2\r\n"
9641        );
9642
9643        // One test each. NOCASE reaches all four of them and it may be written
9644        // after the pattern it applies to.
9645        assert_eq!(
9646            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
9647            "*1\r\n:0\r\n"
9648        );
9649        assert_eq!(
9650            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
9651            "*2\r\n:0\r\n:3\r\n"
9652        );
9653        assert_eq!(
9654            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
9655            "*1\r\n:2\r\n"
9656        );
9657        assert_eq!(
9658            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
9659            "*2\r\n:1\r\n:2\r\n"
9660        );
9661
9662        // OR is the default and AND has to be asked for, and either way the
9663        // last of a repeated option wins.
9664        let both: &[&[u8]] = &[
9665            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
9666        ];
9667        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
9668        assert_eq!(
9669            f.run(&[
9670                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
9671            ]),
9672            "*0\r\n"
9673        );
9674        assert_eq!(
9675            f.run(&[
9676                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
9677            ]),
9678            "*2\r\n:0\r\n:1\r\n"
9679        );
9680
9681        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
9682        // not the positions it had to look at.
9683        assert_eq!(
9684            f.run(&[
9685                b"ARGREP",
9686                b"a",
9687                b"-",
9688                b"+",
9689                b"MATCH",
9690                b"a",
9691                b"WITHVALUES",
9692                b"LIMIT",
9693                b"2"
9694            ]),
9695            "*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"
9696        );
9697        assert_eq!(
9698            f.run(&[
9699                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
9700            ]),
9701            "*1\r\n:3\r\n"
9702        );
9703    }
9704
9705    /// Everything ARGREP refuses, in the order it refuses it.
9706    #[test]
9707    fn a_grep_reports_a_broken_command_the_way_redis_does() {
9708        let mut f = Fixture::new();
9709        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
9710        let syntax = "-ERR syntax error\r\n";
9711
9712        // The bounds are read before the plan, so a bad index beats a bad
9713        // predicate whichever way round the two are written.
9714        assert_eq!(
9715            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
9716            "-ERR invalid array index\r\n"
9717        );
9718        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
9719        // A keyword with nothing after it, and a command that asks for nothing.
9720        assert_eq!(
9721            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
9722            syntax
9723        );
9724        assert_eq!(
9725            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
9726            syntax
9727        );
9728        assert_eq!(
9729            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
9730            syntax,
9731            "a command with no predicate in it at all"
9732        );
9733        assert_eq!(
9734            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
9735            "-ERR LIMIT must be positive\r\n"
9736        );
9737        assert_eq!(
9738            f.run(&[
9739                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
9740            ]),
9741            "-ERR value is not an integer or out of range\r\n"
9742        );
9743        assert_eq!(
9744            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
9745            "-ERR regular expression is empty\r\n"
9746        );
9747        assert_eq!(
9748            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
9749            "-ERR invalid regular expression: Missing ')'\r\n"
9750        );
9751        assert_eq!(
9752            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
9753            "-ERR regular expression backreferences are not supported\r\n"
9754        );
9755        // The arity is minus six, so a predicate keyword with no pattern after
9756        // it is short by one and never reaches the parser.
9757        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
9758        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
9759        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
9760    }
9761
9762    #[test]
9763    fn an_op_reduces_a_range_to_one_number() {
9764        let mut f = Fixture::new();
9765        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
9766        assert_eq!(
9767            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
9768            "$4\r\n-0.5\r\n"
9769        );
9770        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
9771        assert_eq!(
9772            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
9773            "$3\r\n2.5\r\n"
9774        );
9775        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
9776        assert_eq!(
9777            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
9778            ":1\r\n"
9779        );
9780        // An aggregate is written with seventeen significant digits, which is
9781        // Redis's own choice and not what a score comes back as.
9782        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
9783        assert_eq!(
9784            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
9785            "$19\r\n0.30000000000000004\r\n"
9786        );
9787        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
9788        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
9789
9790        // Nothing to work with is a null, and a missing key is a null for the
9791        // aggregates and a zero for the two that count.
9792        f.run(&[b"ARSET", b"w", b"0", b"word"]);
9793        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
9794        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
9795        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
9796
9797        assert_eq!(
9798            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
9799            "-ERR unknown operation\r\n"
9800        );
9801        assert_eq!(
9802            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
9803            "-ERR MATCH requires a value argument\r\n"
9804        );
9805        assert_eq!(
9806            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
9807            "-ERR wrong number of arguments for 'arop' command\r\n"
9808        );
9809    }
9810
9811    #[test]
9812    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
9813        let mut f = Fixture::new();
9814        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
9815        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
9816        let short = f.run(&[b"ARINFO", b"a"]);
9817        assert!(
9818            short.starts_with("*14\r\n"),
9819            "seven pairs on RESP2: {short}"
9820        );
9821        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
9822        assert!(
9823            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
9824            "{short}"
9825        );
9826        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
9827        let full = f.run(&[b"ARINFO", b"a", b"full"]);
9828        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
9829        // Two values one apart are held sparsely, so the dense count is zero and
9830        // the two dense averages have nothing to average.
9831        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
9832        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
9833        assert!(
9834            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
9835            "{full}"
9836        );
9837        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
9838
9839        // On RESP3 the same reply is a map and the averages are doubles.
9840        let mut g = Fixture::new();
9841        g.run(&[b"HELLO", b"3"]);
9842        g.run(&[b"ARINSERT", b"a", b"x"]);
9843        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
9844        assert!(map.starts_with("%12\r\n"), "{map}");
9845        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
9846        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
9847    }
9848
9849    #[test]
9850    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
9851        let mut f = Fixture::new();
9852        // Whole numbers up to two to the sixty second come back as integers,
9853        // and past that the digit generator takes over and uses an exponent.
9854        for (score, want) in [
9855            ("3", "3"),
9856            ("3.5", "3.5"),
9857            ("0.3", "0.3"),
9858            ("1e30", "1e+30"),
9859            ("1e19", "1e+19"),
9860            ("1e-7", "1e-7"),
9861            ("0.000001", "0.000001"),
9862            ("4611686018427387904", "4611686018427387904"),
9863            ("-0", "-0"),
9864        ] {
9865            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
9866            assert_eq!(
9867                f.run(&[b"ZSCORE", b"z", b"m"]),
9868                format!("${}\r\n{want}\r\n", want.len()),
9869                "score {score}"
9870            );
9871        }
9872
9873        // The same bytes on RESP3, where the reply is a double rather than a
9874        // bulk string.
9875        let mut g = Fixture::new();
9876        g.run(&[b"HELLO", b"3"]);
9877        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
9878        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
9879        // The two float increments are not this printer. They go through
9880        // ld2string in its human mode, which is a fixed point conversion with
9881        // the trailing zeros taken off, so they never write an exponent, and
9882        // they reply with a bulk string on both protocols.
9883        assert_eq!(
9884            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
9885            "$31\r\n1000000000000000000000000000000\r\n"
9886        );
9887        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
9888        assert_eq!(
9889            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
9890            "$20\r\n10000000000000000000\r\n"
9891        );
9892    }
9893
9894    // ----------------------------------------------------------------- graph
9895
9896    #[test]
9897    fn a_node_comes_back_with_the_fields_it_went_in_with() {
9898        let mut f = Fixture::new();
9899        assert_eq!(
9900            f.run(&[
9901                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
9902            ]),
9903            ":1\r\n"
9904        );
9905        // The year comes back as the four bytes that were sent and not as a
9906        // number, because every property is text and there is nothing on the
9907        // wire that says which of `1815` and `"1815"` the client meant. The
9908        // fields are in the document's order, which is sorted by name, because
9909        // that is what makes a field lookup a binary search.
9910        assert_eq!(
9911            f.run(&[b"G.NGET", b"social", b"ada"]),
9912            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
9913        );
9914        // A second write to the same id replaces the document and says so with
9915        // a zero, so an ingest can count what it created.
9916        assert_eq!(
9917            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
9918            ":0\r\n"
9919        );
9920        assert_eq!(
9921            f.run(&[b"G.NGET", b"social", b"ada"]),
9922            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
9923        );
9924        // A node with no properties is an empty map and not a null, which is
9925        // how a client tells an isolated node from one that is not there.
9926        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
9927        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
9928        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
9929        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
9930
9931        // A field with no value creates nothing, because the pairs are checked
9932        // before the key is touched.
9933        assert_eq!(
9934            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
9935            "-ERR syntax error\r\n"
9936        );
9937        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
9938
9939        // On RESP3 the same reply is a map.
9940        let mut g = Fixture::new();
9941        g.run(&[b"HELLO", b"3"]);
9942        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
9943        assert_eq!(
9944            g.run(&[b"G.NGET", b"social", b"ada"]),
9945            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
9946        );
9947    }
9948
9949    #[test]
9950    fn an_edge_creates_the_ends_it_needs() {
9951        let mut f = Fixture::new();
9952        assert_eq!(
9953            f.run(&[
9954                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
9955            ]),
9956            ":1\r\n"
9957        );
9958        // Neither end was written first and both are there, as empty nodes.
9959        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
9960        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
9961        assert_eq!(
9962            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
9963            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
9964        );
9965        assert_eq!(
9966            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
9967            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
9968        );
9969        // The same pair under the same label again updates the edge rather than
9970        // making a second one.
9971        assert_eq!(
9972            f.run(&[
9973                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
9974            ]),
9975            ":0\r\n"
9976        );
9977        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
9978        // A different label between the same pair is a different edge.
9979        assert_eq!(
9980            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
9981            ":1\r\n"
9982        );
9983        assert_eq!(
9984            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
9985            ":1\r\n"
9986        );
9987
9988        assert_eq!(
9989            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
9990            ":1\r\n"
9991        );
9992        assert_eq!(
9993            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
9994            ":0\r\n"
9995        );
9996        // A label nothing has used, an end that is not there, and a key that is
9997        // not there are all a zero rather than an error.
9998        assert_eq!(
9999            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
10000            ":0\r\n"
10001        );
10002        assert_eq!(
10003            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
10004            ":0\r\n"
10005        );
10006        assert_eq!(
10007            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
10008            ":0\r\n"
10009        );
10010    }
10011
10012    /// A run is paged the way `SCAN` is paged, so a client that can walk one
10013    /// can walk the other.
10014    #[test]
10015    fn a_hop_answers_a_cursor_and_a_page() {
10016        let mut f = Fixture::new();
10017        for i in 0..25u32 {
10018            let dst = format!("n{i}");
10019            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
10020        }
10021        // Ten without being asked, and the cursor is where to carry on from.
10022        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
10023        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
10024
10025        let mut seen = 0;
10026        let mut cursor = String::from("0");
10027        loop {
10028            let page = f.run(&[
10029                b"G.OUT",
10030                b"social",
10031                b"hub",
10032                b"FOLLOWS",
10033                b"COUNT",
10034                b"7",
10035                b"CURSOR",
10036                cursor.as_bytes(),
10037            ]);
10038            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
10039            cursor = head
10040                .rsplit("\r\n")
10041                .next()
10042                .expect("the cursor line")
10043                .to_string();
10044            seen += rest
10045                .split_once("\r\n")
10046                .expect("the page length")
10047                .0
10048                .parse::<usize>()
10049                .expect("a length");
10050            if cursor == "0" {
10051                break;
10052            }
10053        }
10054        assert_eq!(seen, 25, "every neighbour once across the pages");
10055
10056        // A cursor past the end is an empty page and not an error, and so is a
10057        // key or a label that is not there.
10058        assert_eq!(
10059            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
10060            "*2\r\n$1\r\n0\r\n*0\r\n"
10061        );
10062        assert_eq!(
10063            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
10064            "*2\r\n$1\r\n0\r\n*0\r\n"
10065        );
10066        assert_eq!(
10067            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
10068            "*2\r\n$1\r\n0\r\n*0\r\n"
10069        );
10070        assert_eq!(
10071            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
10072            "-ERR COUNT must be a positive integer\r\n"
10073        );
10074        assert_eq!(
10075            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
10076            "-ERR syntax error\r\n"
10077        );
10078    }
10079
10080    #[test]
10081    fn a_degree_counts_one_way_or_both() {
10082        let mut f = Fixture::new();
10083        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
10084        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
10085        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
10086        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
10087        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
10088        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
10089        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
10090        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
10091        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
10092        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
10093        assert_eq!(
10094            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
10095            "-ERR syntax error\r\n"
10096        );
10097    }
10098
10099    /// A walk answers which nodes it can reach and not by how many routes, so a
10100    /// node two ways out is in the frontier once.
10101    #[test]
10102    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
10103        let mut f = Fixture::new();
10104        for (src, dst) in [
10105            ("ada", "grace"),
10106            ("ada", "alan"),
10107            ("grace", "edsger"),
10108            ("alan", "edsger"),
10109            ("edsger", "barbara"),
10110        ] {
10111            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
10112        }
10113        // Two hops without being asked, the start left out, and edsger once
10114        // even though both of the first hop's nodes point at it.
10115        assert_eq!(
10116            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
10117            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
10118        );
10119        assert_eq!(
10120            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
10121            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
10122        );
10123        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
10124        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
10125        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
10126        // COUNT stops the walk rather than trimming what it found.
10127        assert_eq!(
10128            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
10129            "*1\r\n$5\r\ngrace\r\n"
10130        );
10131        // A node nothing leaves is an empty array and not an error.
10132        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
10133        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
10134        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
10135        assert_eq!(
10136            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
10137            "-ERR DEPTH must be a positive integer\r\n"
10138        );
10139        assert_eq!(
10140            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
10141            "-ERR syntax error\r\n"
10142        );
10143    }
10144
10145    /// The two sided search, which is the whole reason `G.PATH` is a command
10146    /// and not something a client builds out of `G.OUT`.
10147    #[test]
10148    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
10149        let mut f = Fixture::new();
10150        // A chain of six, and a shortcut that makes a shorter way round under a
10151        // second label so the search has to take either kind of hop.
10152        for i in 0..6u32 {
10153            let src = format!("n{i}");
10154            let dst = format!("n{}", i + 1);
10155            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
10156        }
10157        assert_eq!(
10158            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10159            "*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"
10160        );
10161        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
10162        assert_eq!(
10163            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
10164            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
10165        );
10166        // A node to itself is a path of one, and a depth too short to reach is
10167        // no path at all.
10168        assert_eq!(
10169            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
10170            "*1\r\n$2\r\nn2\r\n"
10171        );
10172        assert_eq!(
10173            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
10174            "*0\r\n"
10175        );
10176        // Direction counts: the chain only goes one way.
10177        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
10178        // An unreachable node, a node that is not there, and a key that is not
10179        // there are the same empty answer.
10180        f.run(&[b"G.NADD", b"road", b"island"]);
10181        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
10182        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
10183        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
10184        assert_eq!(
10185            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
10186            "-ERR syntax error\r\n"
10187        );
10188    }
10189
10190    /// The point of the escape in the record tag: the keyspace owns a graph key
10191    /// the way it owns every other key, and none of these commands know a graph
10192    /// exists.
10193    #[test]
10194    fn the_keyspace_sees_a_graph_key_like_any_other() {
10195        let mut f = Fixture::new();
10196        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
10197        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
10198        assert_eq!(
10199            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
10200            "$9\r\nadjacency\r\n"
10201        );
10202        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10203        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
10204        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
10205        // A graph is counted against the server the way every other body is,
10206        // which is what `maxmemory` will read when this key is a million nodes.
10207        // There is no `MEMORY USAGE` command yet, so this asks the server.
10208        let held = f.server.memory_bytes();
10209        for i in 0..200u32 {
10210            let dst = format!("n{i}");
10211            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
10212        }
10213        assert!(
10214            f.server.memory_bytes() > held,
10215            "two hundred edges cost something: {held} then {}",
10216            f.server.memory_bytes()
10217        );
10218        f.run(&[b"DEL", b"big"]);
10219
10220        // An expiry, then a rename, then a move to another database, all of
10221        // which are the keyspace moving a record it cannot look inside.
10222        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
10223        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
10224        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
10225        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
10226        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
10227        f.run(&[b"SELECT", b"1"]);
10228        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
10229
10230        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
10231        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10232        f.run(&[b"G.NADD", b"g", b"n"]);
10233        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
10234        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10235    }
10236
10237    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
10238    /// rather than answering the way they answer for a key that is not there.
10239    #[test]
10240    fn a_graph_cannot_be_copied_or_dumped() {
10241        let mut f = Fixture::new();
10242        f.run(&[b"G.NADD", b"social", b"ada"]);
10243        assert_eq!(
10244            f.run(&[b"COPY", b"social", b"other"]),
10245            "-ERR COPY is not supported for a graph\r\n"
10246        );
10247        assert_eq!(
10248            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
10249            "-ERR COPY is not supported for a graph\r\n"
10250        );
10251        assert_eq!(
10252            f.run(&[b"DUMP", b"social"]),
10253            "-ERR DUMP is not supported for a graph\r\n"
10254        );
10255        // A refused copy leaves both keys exactly as they were.
10256        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
10257    }
10258
10259    /// A graph key is a key, so the commands for the other types refuse it and
10260    /// the graph commands refuse theirs.
10261    #[test]
10262    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
10263        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
10264        let mut f = Fixture::new();
10265        f.run(&[b"G.NADD", b"social", b"ada"]);
10266        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
10267        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
10268        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
10269
10270        f.run(&[b"SET", b"str", b"v"]);
10271        for cmd in [
10272            vec![b"G.NADD".as_ref(), b"str", b"n"],
10273            vec![b"G.NGET".as_ref(), b"str", b"n"],
10274            vec![b"G.NDEL".as_ref(), b"str", b"n"],
10275            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
10276            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
10277            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
10278            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
10279            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
10280            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
10281            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
10282        ] {
10283            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
10284        }
10285    }
10286
10287    /// Every other collection here takes its key with it when its last member
10288    /// goes, and a graph is no different.
10289    #[test]
10290    fn a_graph_goes_when_its_last_node_does() {
10291        let mut f = Fixture::new();
10292        f.run(&[
10293            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
10294        ]);
10295        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
10296        // The node and the edges that hung off it are both gone.
10297        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
10298        assert_eq!(
10299            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
10300            ":0\r\n"
10301        );
10302        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
10303        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
10304
10305        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
10306        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
10307        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
10308        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
10309
10310        // The id the removed node had is not handed out again, so a client
10311        // holding an id from an earlier reply cannot have it mean another node.
10312        f.run(&[b"G.NADD", b"social", b"first"]);
10313        f.run(&[b"G.NADD", b"social", b"second"]);
10314        f.run(&[b"G.NDEL", b"social", b"first"]);
10315        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
10316        assert_eq!(
10317            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
10318            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
10319        );
10320    }
10321
10322    // ------------------------------------------------------------------ json
10323
10324    /// The two path syntaxes answer different shapes, which is the thing a
10325    /// client is most likely to be broken by and so the thing to pin first.
10326    #[test]
10327    fn a_json_path_answers_a_set_and_a_legacy_path_answers_a_value() {
10328        let mut f = Fixture::new();
10329        let doc = br#"{"a":1,"b":{"c":true}}"#;
10330        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$", doc]), "+OK\r\n");
10331        // No path at all is the legacy root and not `$`, so the document comes
10332        // back as itself rather than wrapped.
10333        assert_eq!(
10334            f.run(&[b"JSON.GET", b"doc"]),
10335            bulk(r#"{"a":1,"b":{"c":true}}"#)
10336        );
10337        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.a"]), bulk("[1]"));
10338        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("1"));
10339        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$..c"]), bulk("[true]"));
10340        // A path that matched nothing is an empty set on one syntax and an
10341        // error on the other, and the error does not quote the path.
10342        assert_eq!(f.run(&[b"JSON.GET", b"doc", b"$.nope"]), bulk("[]"));
10343        assert_eq!(
10344            f.run(&[b"JSON.GET", b"doc", b".nope"]),
10345            "-ERR Path does not exist\r\n"
10346        );
10347        assert_eq!(f.run(&[b"JSON.GET", b"nokey"]), "$-1\r\n");
10348        // The key is a document to the rest of the keyspace, under the name
10349        // RedisJSON registers, and every generic command works on it.
10350        assert_eq!(f.run(&[b"TYPE", b"doc"]), "+ReJSON-RL\r\n");
10351        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":1\r\n");
10352        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"doc"]), bulk("raw"));
10353        assert_eq!(f.run(&[b"DEL", b"doc"]), ":1\r\n");
10354        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10355    }
10356
10357    /// The two error lines RedisJSON sends without a prefix in front of them.
10358    ///
10359    /// Every other error this server writes starts `ERR` or `WRONGTYPE`. These
10360    /// two do not, on a real server, and a differential harness compares the
10361    /// whole line.
10362    #[test]
10363    fn the_two_json_errors_that_carry_no_prefix() {
10364        let mut f = Fixture::new();
10365        f.run(&[b"SET", b"plain", b"x"]);
10366        let wrong = "-Existing key has wrong Redis type\r\n";
10367        assert_eq!(f.run(&[b"JSON.GET", b"plain"]), wrong);
10368        assert_eq!(f.run(&[b"JSON.SET", b"plain", b"$", b"1"]), wrong);
10369        assert_eq!(f.run(&[b"JSON.DEL", b"plain"]), wrong);
10370        assert_eq!(f.run(&[b"JSON.TYPE", b"plain"]), wrong);
10371        assert_eq!(f.run(&[b"JSON.CLEAR", b"plain"]), wrong);
10372
10373        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"z":1},"b":{"z":2}}"#]);
10374        // A wildcard that matched something writes to all of it. A wildcard
10375        // that matched nothing would have to invent a place, and that is the
10376        // other unprefixed line.
10377        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.*.z", b"9"]), "+OK\r\n");
10378        assert_eq!(
10379            f.run(&[b"JSON.GET", b"doc"]),
10380            bulk(r#"{"a":{"z":9},"b":{"z":9}}"#)
10381        );
10382        assert_eq!(
10383            f.run(&[b"JSON.SET", b"doc", b"$.*.y", b"9"]),
10384            "-Err wrong static path\r\n"
10385        );
10386    }
10387
10388    /// What `JSON.SET` does with a path that named nowhere.
10389    #[test]
10390    fn json_set_creates_one_field_and_refuses_to_invent_the_rest() {
10391        let mut f = Fixture::new();
10392        // A key that is not there can only be written whole.
10393        assert_eq!(
10394            f.run(&[b"JSON.SET", b"new", b".a", b"1"]),
10395            "-ERR new objects must be created at the root\r\n"
10396        );
10397        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
10398        // The root check comes before NX and XX, which is the order a real
10399        // server checks them in.
10400        assert_eq!(
10401            f.run(&[b"JSON.SET", b"new", b".a", b"1", b"NX"]),
10402            "-ERR new objects must be created at the root\r\n"
10403        );
10404        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"XX"]), "$-1\r\n");
10405        assert_eq!(f.run(&[b"JSON.SET", b"new", b"$", b"1", b"NX"]), "+OK\r\n");
10406
10407        f.run(&[
10408            b"JSON.SET",
10409            b"doc",
10410            b"$",
10411            br#"{"o":{},"arr":[1,2],"s":"x"}"#,
10412        ]);
10413        // One step past a container that is there is a place to write.
10414        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"1"]), "+OK\r\n");
10415        // One step past something that is not, or past something that is not an
10416        // object, is not an error and is not a write either.
10417        assert_eq!(
10418            f.run(&[b"JSON.SET", b"doc", b"$.nope.made", b"1"]),
10419            "$-1\r\n"
10420        );
10421        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.s.made", b"1"]), "$-1\r\n");
10422        // An index past the end does not append. JSON.ARRAPPEND appends.
10423        assert_eq!(
10424            f.run(&[b"JSON.SET", b"doc", b"$.arr[5]", b"9"]),
10425            "-ERR array index out of range\r\n"
10426        );
10427        assert_eq!(
10428            f.run(&[b"JSON.SET", b"doc", b"$.arr[2]", b"9"]),
10429            "-ERR array index out of range\r\n"
10430        );
10431        assert_eq!(f.run(&[b"JSON.SET", b"doc", b"$.arr[1]", b"9"]), "+OK\r\n");
10432        // NX on a path that is there and XX on a path that is not are both a
10433        // nil and neither changes anything.
10434        assert_eq!(
10435            f.run(&[b"JSON.SET", b"doc", b"$.o.made", b"2", b"NX"]),
10436            "$-1\r\n"
10437        );
10438        assert_eq!(
10439            f.run(&[b"JSON.SET", b"doc", b"$.gone", b"2", b"XX"]),
10440            "$-1\r\n"
10441        );
10442        assert_eq!(
10443            f.run(&[b"JSON.GET", b"doc"]),
10444            bulk(r#"{"o":{"made":1},"s":"x","arr":[1,9]}"#)
10445        );
10446        // Text that is not JSON is refused before the key is touched. The
10447        // line has no `ERR` in front of it, which is this command's and not
10448        // every command's, and is in D-37.
10449        assert!(
10450            f.run(&[b"JSON.SET", b"doc", b"$.s", b"nope"])
10451                .starts_with("-this is not the start of a value")
10452        );
10453        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".s"]), bulk("\"x\""));
10454    }
10455
10456    /// `JSON.DEL`, `JSON.TYPE`, `JSON.TOGGLE` and `JSON.CLEAR`, each of which
10457    /// answers a count or a word rather than text.
10458    #[test]
10459    fn the_json_commands_that_do_not_answer_text() {
10460        let mut f = Fixture::new();
10461        let doc = br#"{"a":1,"t":true,"o":{"x":1},"arr":[1,2],"f":1.5,"s":"x","n":null}"#;
10462        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10463
10464        assert_eq!(f.run(&[b"JSON.TYPE", b"doc"]), bulk("object"));
10465        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".a"]), bulk("integer"));
10466        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".f"]), bulk("number"));
10467        assert_eq!(
10468            f.run(&[b"JSON.TYPE", b"doc", b"$.a"]),
10469            format!("*1\r\n{}", bulk("integer"))
10470        );
10471        // The one place a legacy path that matched nothing is a nil rather than
10472        // an error, which lines up with a key that is not there.
10473        assert_eq!(f.run(&[b"JSON.TYPE", b"doc", b".nope"]), "$-1\r\n");
10474        assert_eq!(f.run(&[b"JSON.TYPE", b"nokey"]), "$-1\r\n");
10475
10476        // A boolean flips and answers the value it now has, as an integer on
10477        // one syntax and as the word on the other.
10478        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.t"]), "*1\r\n:0\r\n");
10479        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b".t"]), bulk("true"));
10480        // Something that is not a boolean is a hole on one syntax and one
10481        // sentence covering both cases on the other.
10482        assert_eq!(f.run(&[b"JSON.TOGGLE", b"doc", b"$.a"]), "*1\r\n$-1\r\n");
10483        assert_eq!(
10484            f.run(&[b"JSON.TOGGLE", b"doc", b".a"]),
10485            "-ERR Path does not exist or not a bool\r\n"
10486        );
10487        assert_eq!(
10488            f.run(&[b"JSON.TOGGLE", b"doc", b".nope"]),
10489            "-ERR Path does not exist or not a bool\r\n"
10490        );
10491        assert_eq!(
10492            f.run(&[b"JSON.TOGGLE", b"nokey", b"$.a"]),
10493            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10494        );
10495
10496        // Clearing empties containers and zeroes numbers and leaves everything
10497        // else alone, and counts only what it changed.
10498        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.s"]), ":0\r\n");
10499        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":4\r\n");
10500        assert_eq!(f.run(&[b"JSON.CLEAR", b"doc", b"$.*"]), ":0\r\n");
10501        assert_eq!(
10502            f.run(&[b"JSON.GET", b"doc"]),
10503            bulk(r#"{"a":0,"f":0,"n":null,"o":{},"s":"x","t":true,"arr":[]}"#)
10504        );
10505
10506        // Deleting counts what it removed, and deleting the root is deleting
10507        // the key.
10508        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.nope"]), ":0\r\n");
10509        assert_eq!(f.run(&[b"JSON.DEL", b"doc", b"$.a"]), ":1\r\n");
10510        // Deleting the last member of the root container deletes the key, the
10511        // same way popping the last element off a list does. It is a rule about
10512        // deleting and not about shape: a document written as an empty object
10513        // by JSON.SET stays, because nothing was removed from it.
10514        assert_eq!(f.run(&[b"JSON.FORGET", b"doc", b"$.*"]), ":6\r\n");
10515        assert_eq!(f.run(&[b"EXISTS", b"doc"]), ":0\r\n");
10516        assert_eq!(f.run(&[b"JSON.GET", b"doc"]), "$-1\r\n");
10517        assert_eq!(f.run(&[b"JSON.DEL", b"doc"]), ":0\r\n");
10518        assert_eq!(f.run(&[b"JSON.SET", b"empty", b"$", b"{}"]), "+OK\r\n");
10519        assert_eq!(f.run(&[b"EXISTS", b"empty"]), ":1\r\n");
10520        assert_eq!(f.run(&[b"JSON.GET", b"empty"]), bulk("{}"));
10521        assert_eq!(f.run(&[b"JSON.DEL", b"nokey"]), ":0\r\n");
10522    }
10523
10524    /// `JSON.GET` with more than one path, and with a layout.
10525    ///
10526    /// The wrapper the reply is built in is laid out too, so what a path
10527    /// matched starts one level in for a single JSONPath and two for one of
10528    /// several, and getting that wrong is the kind of thing only a byte for
10529    /// byte comparison catches.
10530    #[test]
10531    fn json_get_lays_out_the_wrapper_it_builds() {
10532        let mut f = Fixture::new();
10533        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[1,{"c":2}]}"#]);
10534
10535        assert_eq!(
10536            f.run(&[b"JSON.GET", b"doc", b"$.a", b"$.b"]),
10537            bulk(r#"{"$.a":[1],"$.b":[[1,{"c":2}]]}"#)
10538        );
10539        // Legacy paths are not wrapped, even when there are several of them.
10540        assert_eq!(
10541            f.run(&[b"JSON.GET", b"doc", b".a", b".b"]),
10542            bulk(r#"{".a":1,".b":[1,{"c":2}]}"#)
10543        );
10544        let fmt: &[&[u8]] = &[b"INDENT", b"  ", b"NEWLINE", b"\n", b"SPACE", b" "];
10545        let mut one = vec![b"JSON.GET".as_slice(), b"doc"];
10546        one.extend_from_slice(fmt);
10547        one.push(b"$.b");
10548        assert_eq!(
10549            f.run(&one),
10550            bulk("[\n  [\n    1,\n    {\n      \"c\": 2\n    }\n  ]\n]")
10551        );
10552        let mut two = vec![b"JSON.GET".as_slice(), b"doc"];
10553        two.extend_from_slice(fmt);
10554        two.push(b"$.a");
10555        two.push(b"$.nope");
10556        assert_eq!(
10557            f.run(&two),
10558            bulk("{\n  \"$.a\": [\n    1\n  ],\n  \"$.nope\": []\n}")
10559        );
10560        // The options are read before the paths and in any order, and a
10561        // document with nothing to lay out is the same either way.
10562        let mut root = vec![b"JSON.GET".as_slice(), b"doc", b"SPACE", b" "];
10563        root.push(b".a");
10564        assert_eq!(f.run(&root), bulk("1"));
10565    }
10566
10567    /// `JSON.MGET`, which is the only command here that reads more than one key
10568    /// and so the only one whose answer has holes in it.
10569    #[test]
10570    fn json_mget_answers_once_per_key_whatever_is_under_them() {
10571        let mut f = Fixture::new();
10572        f.run(&[b"JSON.SET", b"one", b"$", br#"{"a":1}"#]);
10573        f.run(&[b"JSON.SET", b"two", b"$", br#"{"a":2}"#]);
10574        f.run(&[b"SET", b"plain", b"x"]);
10575        assert_eq!(
10576            f.run(&[b"JSON.MGET", b"one", b"two", b"$.a"]),
10577            format!("*2\r\n{}{}", bulk("[1]"), bulk("[2]"))
10578        );
10579        // A key that is not there and a key holding something else are both a
10580        // hole rather than an error, the way MGET treats a hash.
10581        assert_eq!(
10582            f.run(&[b"JSON.MGET", b"one", b"nokey", b"plain", b".a"]),
10583            format!("*3\r\n{}$-1\r\n$-1\r\n", bulk("1"))
10584        );
10585        // A legacy path that matched nothing is a hole too, because one bad
10586        // answer should not lose the others.
10587        assert_eq!(f.run(&[b"JSON.MGET", b"one", b".nope"]), "*1\r\n$-1\r\n");
10588    }
10589
10590    /// The four commands that ask how big something is, and the four different
10591    /// sets of answers they give for the same three failures.
10592    ///
10593    /// There is no pattern in this and there is no reading it off the
10594    /// documentation either. It was read off a running RedisJSON one line at a
10595    /// time, and it is written down here because the error text is what a client
10596    /// library branches on.
10597    #[test]
10598    fn the_json_commands_that_answer_a_size_disagree_about_every_failure() {
10599        let mut f = Fixture::new();
10600        let doc = br#"{"a":[1,2,3],"o":{"x":1,"y":2},"s":"hello","n":7}"#;
10601        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10602
10603        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b".a"]), ":3\r\n");
10604        assert_eq!(f.run(&[b"JSON.ARRLEN", b"doc", b"$.a"]), "*1\r\n:3\r\n");
10605        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".o"]), ":2\r\n");
10606        assert_eq!(f.run(&[b"JSON.STRLEN", b"doc", b".s"]), ":5\r\n");
10607        assert_eq!(
10608            f.run(&[b"JSON.OBJKEYS", b"doc", b".o"]),
10609            format!("*2\r\n{}{}", bulk("x"), bulk("y"))
10610        );
10611        // A JSONPath answers one entry per match and a hole for a match of the
10612        // wrong kind, which is the one shape all four agree on.
10613        assert_eq!(
10614            f.run(&[b"JSON.ARRLEN", b"doc", b"$.*"]),
10615            "*4\r\n:3\r\n$-1\r\n$-1\r\n$-1\r\n"
10616        );
10617
10618        // A legacy path that matched nothing. Two of them are an error and two
10619        // of them are a nil, and the two errors do not use the same sentence.
10620        assert_eq!(
10621            f.run(&[b"JSON.ARRLEN", b"doc", b".nope"]),
10622            "-ERR Path does not exist\r\n"
10623        );
10624        assert_eq!(
10625            f.run(&[b"JSON.STRLEN", b"doc", b".nope"]),
10626            "-ERR Path does not exist\r\n"
10627        );
10628        assert_eq!(f.run(&[b"JSON.OBJLEN", b"doc", b".nope"]), "$-1\r\n");
10629        // A nil bulk and not an empty array, even though the answer would have
10630        // been an array, which is what RedisJSON sends here too.
10631        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b".nope"]), "$-1\r\n");
10632        // The JSONPath spelling of the same question is an empty array, since
10633        // no match is not a failure on that syntax.
10634        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"doc", b"$.nope"]), "*0\r\n");
10635
10636        // A legacy path that matched the wrong kind of value. Now two of them
10637        // are an ERR and two of them are a WRONGTYPE, and it is not the same
10638        // two.
10639        assert_eq!(
10640            f.run(&[b"JSON.ARRLEN", b"doc", b".n"]),
10641            "-ERR Path does not exist or not an array\r\n"
10642        );
10643        assert_eq!(
10644            f.run(&[b"JSON.OBJKEYS", b"doc", b".n"]),
10645            "-ERR Path does not exist or not an object\r\n"
10646        );
10647        assert_eq!(
10648            f.run(&[b"JSON.OBJLEN", b"doc", b".n"]),
10649            "-WRONGTYPE wrong type of path value - expected object\r\n"
10650        );
10651        assert_eq!(
10652            f.run(&[b"JSON.STRLEN", b"doc", b".n"]),
10653            "-WRONGTYPE wrong type of path value - expected string\r\n"
10654        );
10655
10656        // A key that is not there, where the two syntaxes swap over: the legacy
10657        // path is the quiet answer and the JSONPath is the error.
10658        assert_eq!(f.run(&[b"JSON.ARRLEN", b"nokey", b".a"]), "$-1\r\n");
10659        assert_eq!(f.run(&[b"JSON.OBJLEN", b"nokey", b".a"]), "$-1\r\n");
10660        assert_eq!(f.run(&[b"JSON.STRLEN", b"nokey", b".a"]), "$-1\r\n");
10661        assert_eq!(f.run(&[b"JSON.OBJKEYS", b"nokey", b".a"]), "$-1\r\n");
10662        assert_eq!(
10663            f.run(&[b"JSON.ARRLEN", b"nokey", b"$.a"]),
10664            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10665        );
10666        // Except this one, which answers about the path instead.
10667        assert_eq!(
10668            f.run(&[b"JSON.OBJLEN", b"nokey", b"$.a"]),
10669            "-ERR Path does not exist or not an object\r\n"
10670        );
10671    }
10672
10673    /// `JSON.ARRAPPEND`, `JSON.ARRINSERT`, `JSON.ARRTRIM` and `JSON.ARRPOP`.
10674    ///
10675    /// The four of them share one error line for a path that named something
10676    /// that is not an array, and they disagree about what an index outside the
10677    /// array means: insert refuses it and the other two clamp.
10678    #[test]
10679    fn the_json_array_writes_agree_on_the_errors_and_not_on_the_indexes() {
10680        let mut f = Fixture::new();
10681        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3],"n":7}"#]);
10682
10683        assert_eq!(f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"4"]), ":4\r\n");
10684        assert_eq!(
10685            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$.a", b"5", b"6"]),
10686            "*1\r\n:6\r\n"
10687        );
10688        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[1,2,3,4,5,6]"));
10689
10690        // A negative index counts back from the end, and the end itself is a
10691        // place to insert at, so an insert at the length is an append.
10692        assert_eq!(
10693            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-1", b"0"]),
10694            ":7\r\n"
10695        );
10696        assert_eq!(
10697            f.run(&[b"JSON.GET", b"doc", b".a"]),
10698            bulk("[1,2,3,4,5,0,6]")
10699        );
10700        assert_eq!(
10701            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"7", b"9"]),
10702            ":8\r\n"
10703        );
10704        // One past the end is not, and neither is one before the front.
10705        assert_eq!(
10706            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"9", b"9"]),
10707            "-ERR index out of bounds\r\n"
10708        );
10709        assert_eq!(
10710            f.run(&[b"JSON.ARRINSERT", b"doc", b".a", b"-9", b"9"]),
10711            "-ERR index out of bounds\r\n"
10712        );
10713
10714        // Trim takes both ends inclusive and clamps both of them, so a start
10715        // past the end leaves an empty array rather than an error.
10716        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3,4,5]"]);
10717        assert_eq!(
10718            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"1", b"3"]),
10719            ":3\r\n"
10720        );
10721        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[2,3,4]"));
10722        assert_eq!(
10723            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"-2", b"99"]),
10724            ":2\r\n"
10725        );
10726        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[3,4]"));
10727        assert_eq!(
10728            f.run(&[b"JSON.ARRTRIM", b"doc", b".a", b"9", b"9"]),
10729            ":0\r\n"
10730        );
10731        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10732
10733        // Pop clamps as well, its default is the last element, and an empty
10734        // array pops a nil rather than failing.
10735        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[1,2,3]"]);
10736        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), bulk("3"));
10737        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"0"]), bulk("1"));
10738        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a", b"99"]), bulk("2"));
10739        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a"]), "$-1\r\n");
10740
10741        // One sentence covers a path that matched nothing and a path that
10742        // matched the wrong kind of value, for all four of them.
10743        for call in [
10744            &[&b"JSON.ARRAPPEND"[..], b"doc", b"PATH", b"1"][..],
10745            &[&b"JSON.ARRTRIM"[..], b"doc", b"PATH", b"1", b"1"][..],
10746            &[&b"JSON.ARRPOP"[..], b"doc", b"PATH", b"1"][..],
10747            &[&b"JSON.ARRINSERT"[..], b"doc", b"PATH", b"0", b"1"][..],
10748        ] {
10749            for path in [&b".n"[..], &b".nope"[..]] {
10750                let args: Vec<&[u8]> = call
10751                    .iter()
10752                    .map(|a| if *a == b"PATH" { path } else { *a })
10753                    .collect();
10754                assert_eq!(
10755                    f.run(&args),
10756                    "-ERR Path does not exist or not an array\r\n",
10757                    "{} {}",
10758                    String::from_utf8_lossy(call[0]),
10759                    String::from_utf8_lossy(path)
10760                );
10761            }
10762        }
10763
10764        // A key that is not there is the same sentence for all four, on either
10765        // syntax, and it is about the key and not about the path.
10766        assert_eq!(
10767            f.run(&[b"JSON.ARRAPPEND", b"nokey", b".a", b"1"]),
10768            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10769        );
10770        assert_eq!(
10771            f.run(&[b"JSON.ARRPOP", b"nokey", b"$.a"]),
10772            "-ERR could not perform this operation on a key that doesn't exist\r\n"
10773        );
10774
10775        // The values are parsed before the key is touched, so text that is not
10776        // JSON leaves the document alone.
10777        // Text that is not JSON is refused before the key is touched, and
10778        // the line has no `ERR` in front of it, which is D-37.
10779        assert!(
10780            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a", b"nope"])
10781                .starts_with("-this is not the start of a value")
10782        );
10783        assert_eq!(f.run(&[b"JSON.GET", b"doc", b".a"]), bulk("[]"));
10784    }
10785
10786    /// `JSON.ARRINSERT` refuses the whole command when any one of the arrays a
10787    /// path matched cannot take the index, which is D-36.
10788    ///
10789    /// RedisJSON walks the matches, inserts into each one it can, and returns
10790    /// the error on the first one it cannot, leaving the earlier inserts in the
10791    /// document. A write here is one list of edits applied together, so either
10792    /// all of them happen or none of them do.
10793    #[test]
10794    fn json_arrinsert_is_all_or_nothing_across_the_matches() {
10795        let mut f = Fixture::new();
10796        let doc = br#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#;
10797        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10798        assert_eq!(
10799            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"-2", b"0"]),
10800            "-ERR index out of bounds\r\n"
10801        );
10802        assert_eq!(
10803            f.run(&[b"JSON.GET", b"doc"]),
10804            bulk(r#"{"a":[1,2,3],"n":{"a":[9,8],"in":{"a":[1]}}}"#)
10805        );
10806        // Every match can take the index, so every match gets it.
10807        assert_eq!(
10808            f.run(&[b"JSON.ARRINSERT", b"doc", b"$..a", b"0", b"0"]),
10809            "*3\r\n:4\r\n:3\r\n:2\r\n"
10810        );
10811        assert_eq!(
10812            f.run(&[b"JSON.GET", b"doc"]),
10813            bulk(r#"{"a":[0,1,2,3],"n":{"a":[0,9,8],"in":{"a":[0,1]}}}"#)
10814        );
10815    }
10816
10817    /// `JSON.ARRINDEX`, whose stop is exclusive and whose start clamps to the
10818    /// last element rather than to one past it.
10819    ///
10820    /// Both of those read like mistakes and both are what RedisJSON does. The
10821    /// start is the one that bites: a start of five into an array of four still
10822    /// looks at the fourth, so a search that should have run out of array comes
10823    /// back with an answer.
10824    #[test]
10825    fn json_arrindex_has_an_exclusive_stop_and_a_start_that_cannot_run_off_the_end() {
10826        let mut f = Fixture::new();
10827        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3,1],"n":7}"#]);
10828
10829        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"2"]), ":1\r\n");
10830        assert_eq!(f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"9"]), ":-1\r\n");
10831        assert_eq!(
10832            f.run(&[b"JSON.ARRINDEX", b"doc", b"$.a", b"2"]),
10833            "*1\r\n:1\r\n"
10834        );
10835
10836        // Zero as the stop means the end rather than the front, so leaving it
10837        // off and passing it are the same thing.
10838        assert_eq!(
10839            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"0"]),
10840            ":3\r\n"
10841        );
10842        // The stop is exclusive, so a stop of three does not look at index
10843        // three.
10844        assert_eq!(
10845            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1", b"3"]),
10846            ":-1\r\n"
10847        );
10848
10849        // The start clamps to the last element in both directions, which is why
10850        // a start of four, five or minus one all find the 1 at index three.
10851        for start in [&b"4"[..], &b"5"[..], &b"-1"[..]] {
10852            assert_eq!(
10853                f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", start]),
10854                ":3\r\n",
10855                "{}",
10856                String::from_utf8_lossy(start)
10857            );
10858        }
10859        assert_eq!(
10860            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"-100"]),
10861            ":0\r\n"
10862        );
10863        // An empty array is the one case that comes back with nothing, since
10864        // the stop is zero and the loop never starts.
10865        f.run(&[b"JSON.SET", b"doc", b"$.a", b"[]"]);
10866        assert_eq!(
10867            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"1", b"1"]),
10868            ":-1\r\n"
10869        );
10870
10871        // The comparison is structural rather than one of the encoded bytes,
10872        // because an object in a stored document holds its keys as intern table
10873        // ids where one parsed off the wire holds them as bytes.
10874        f.run(&[b"JSON.SET", b"doc", b"$.a", br#"[{"k":1},[1,2],"s"]"#]);
10875        assert_eq!(
10876            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", br#"{"k":1}"#]),
10877            ":0\r\n"
10878        );
10879        assert_eq!(
10880            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[1,2]"]),
10881            ":1\r\n"
10882        );
10883        assert_eq!(
10884            f.run(&[b"JSON.ARRINDEX", b"doc", b".a", b"[2,1]"]),
10885            ":-1\r\n"
10886        );
10887
10888        // Its errors are a third set again: a missing legacy path is the short
10889        // sentence, the wrong kind of value is a WRONGTYPE, and a key that is
10890        // not there is about the path on either syntax.
10891        assert_eq!(
10892            f.run(&[b"JSON.ARRINDEX", b"doc", b".nope", b"1"]),
10893            "-ERR Path does not exist\r\n"
10894        );
10895        assert_eq!(
10896            f.run(&[b"JSON.ARRINDEX", b"doc", b".n", b"1"]),
10897            "-WRONGTYPE wrong type of path value - expected array\r\n"
10898        );
10899        assert_eq!(
10900            f.run(&[b"JSON.ARRINDEX", b"nokey", b".a", b"1"]),
10901            "-ERR Path does not exist\r\n"
10902        );
10903        assert_eq!(
10904            f.run(&[b"JSON.ARRINDEX", b"nokey", b"$.a", b"1"]),
10905            "-ERR Path does not exist\r\n"
10906        );
10907    }
10908
10909    /// The number family answers text and keeps an integer an integer until
10910    /// something in the sum is not one.
10911    #[test]
10912    fn the_json_number_family_answers_json_text_and_keeps_its_integers() {
10913        let mut f = Fixture::new();
10914        let doc = br#"{"i":7,"f":1.5,"neg":-2,"s":"ab"}"#;
10915        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
10916
10917        // A legacy path answers the new value as JSON text in a bulk string,
10918        // not as a number, which is the shape all three of them use.
10919        assert_eq!(
10920            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2"]),
10921            bulk("9").as_str()
10922        );
10923        // A JSONPath answers a bulk string holding a JSON array.
10924        assert_eq!(
10925            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.i", b"2"]),
10926            bulk("[11]").as_str()
10927        );
10928        // Two integers stay an integer and a double anywhere in it makes the
10929        // answer a double, which the document then holds.
10930        assert_eq!(
10931            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"2.0"]),
10932            bulk("13.0").as_str()
10933        );
10934        assert_eq!(
10935            f.run(&[b"JSON.TYPE", b"doc", b".i"]),
10936            bulk("number").as_str()
10937        );
10938        assert_eq!(
10939            f.run(&[b"JSON.NUMMULTBY", b"doc", b".f", b"2"]),
10940            bulk("3.0").as_str()
10941        );
10942        assert_eq!(
10943            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"3"]),
10944            bulk("-8").as_str()
10945        );
10946        // A power of a half is a square root, and the square root of a negative
10947        // number is the error that says the answer is not a number.
10948        f.run(&[b"JSON.SET", b"doc", b"$.f", b"1.5"]);
10949        assert_eq!(
10950            f.run(&[b"JSON.NUMPOWBY", b"doc", b".f", b"0.5"]),
10951            bulk("1.224744871391589").as_str()
10952        );
10953        assert_eq!(
10954            f.run(&[b"JSON.NUMPOWBY", b"doc", b".neg", b"0.5"]),
10955            "-ERR result is not a number\r\n"
10956        );
10957        // An integer answer that does not fit is refused rather than promoted,
10958        // and a negative exponent lands in the same error because there is no
10959        // integer answer to two to the minus one.
10960        f.run(&[b"JSON.SET", b"doc", b"$.big", b"9223372036854775807"]);
10961        assert_eq!(
10962            f.run(&[b"JSON.NUMINCRBY", b"doc", b".big", b"1"]),
10963            "-ERR numeric overflow\r\n"
10964        );
10965        f.run(&[b"JSON.SET", b"doc", b"$.p", b"2"]);
10966        assert_eq!(
10967            f.run(&[b"JSON.NUMPOWBY", b"doc", b".p", b"-1"]),
10968            "-ERR numeric overflow\r\n"
10969        );
10970        // A double that leaves the finite numbers is the other error.
10971        f.run(&[b"JSON.SET", b"doc", b"$.huge", b"1e308"]);
10972        assert_eq!(
10973            f.run(&[b"JSON.NUMMULTBY", b"doc", b".huge", b"1e10"]),
10974            "-ERR result is not a number\r\n"
10975        );
10976
10977        // A match that is not a number is a null inside the array on a
10978        // JSONPath, and a legacy path that found no number at all is the error
10979        // with the module's own typo in it.
10980        assert_eq!(
10981            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"1"]),
10982            bulk("[null]").as_str()
10983        );
10984        assert_eq!(
10985            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.nope", b"1"]),
10986            bulk("[]").as_str()
10987        );
10988        assert_eq!(
10989            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", b"1"]),
10990            "-ERR Path does not exist or does not contains a number\r\n"
10991        );
10992        assert_eq!(
10993            f.run(&[b"JSON.NUMINCRBY", b"doc", b".nope", b"1"]),
10994            "-ERR Path does not exist or does not contains a number\r\n"
10995        );
10996        // The operand is JSON and has to be a number. Valid JSON that is not
10997        // one is a line of its own, and it goes out without a prefix.
10998        assert_eq!(
10999            f.run(&[b"JSON.NUMINCRBY", b"doc", b".i", b"true"]),
11000            "-bad input number\r\n"
11001        );
11002        assert_eq!(
11003            f.run(&[b"JSON.NUMINCRBY", b"nokey", b".i", b"1"]),
11004            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11005        );
11006        assert_eq!(
11007            f.run(&[b"JSON.NUMINCRBY", b"nokey", b"$.i", b"1"]),
11008            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11009        );
11010    }
11011
11012    /// `JSON.STRAPPEND` puts its path in the middle and makes it optional,
11013    /// which nothing else in the group does.
11014    #[test]
11015    fn json_strappend_reads_its_shape_off_the_argument_count() {
11016        let mut f = Fixture::new();
11017        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"s":"ab","n":1}"#]);
11018
11019        assert_eq!(
11020            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""c""#]),
11021            ":3\r\n"
11022        );
11023        assert_eq!(
11024            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", br#""d""#]),
11025            "*1\r\n:4\r\n"
11026        );
11027        // The length is in bytes and not in characters, so one two byte letter
11028        // takes it up by two.
11029        assert_eq!(
11030            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", br#""\u00e9""#]),
11031            ":6\r\n"
11032        );
11033        // Three arguments means the value is the last one and the path is the
11034        // root, so this appends to a document that is a string on its own.
11035        f.run(&[b"JSON.SET", b"str", b"$", br#""ab""#]);
11036        assert_eq!(f.run(&[b"JSON.STRAPPEND", b"str", br#""c""#]), ":3\r\n");
11037        assert_eq!(f.run(&[b"JSON.GET", b"str"]), bulk("\"abc\"").as_str());
11038
11039        // The value is JSON and has to be a JSON string. A number is a
11040        // WRONGTYPE about a path value even though it was the value that was
11041        // wrong, which is the module's wording and not a slip here.
11042        assert_eq!(
11043            f.run(&[b"JSON.STRAPPEND", b"doc", b".s", b"5"]),
11044            "-WRONGTYPE wrong type of path value - expected string\r\n"
11045        );
11046        assert_eq!(
11047            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", br#""c""#]),
11048            "*1\r\n$-1\r\n"
11049        );
11050        assert_eq!(
11051            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", br#""c""#]),
11052            "-ERR Path does not exist or not a string\r\n"
11053        );
11054        assert_eq!(
11055            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.nope", br#""c""#]),
11056            "*0\r\n"
11057        );
11058        assert_eq!(
11059            f.run(&[b"JSON.STRAPPEND", b"nokey", br#""c""#]),
11060            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11061        );
11062    }
11063
11064    /// A legacy path can match more than one value, and which of them the one
11065    /// answer comes from is not the same choice twice.
11066    #[test]
11067    fn a_legacy_wildcard_write_touches_every_match_and_answers_only_one() {
11068        let mut f = Fixture::new();
11069        // Three arrays of one, two and three elements, which tells the first
11070        // match and the last match apart in a single command.
11071        let three = br#"{"a":[[7],[7,7],[7,7,7]]}"#;
11072
11073        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11074        assert_eq!(
11075            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11076            ":4\r\n"
11077        );
11078        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11079        assert_eq!(
11080            f.run(&[b"JSON.ARRINSERT", b"doc", b".a[*]", b"0", b"9"]),
11081            ":2\r\n"
11082        );
11083        f.run(&[b"JSON.SET", b"doc", b"$", three]);
11084        assert_eq!(
11085            f.run(&[b"JSON.ARRTRIM", b"doc", b".a[*]", b"0", b"1"]),
11086            ":1\r\n"
11087        );
11088        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[1,2,3],[4,5,6]]}"#]);
11089        assert_eq!(
11090            f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]", b"0"]),
11091            bulk("1").as_str()
11092        );
11093        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2,3]}"#]);
11094        assert_eq!(
11095            f.run(&[b"JSON.NUMINCRBY", b"doc", b".a[*]", b"10"]),
11096            bulk("13").as_str()
11097        );
11098        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["p","qq","rrr"]}"#]);
11099        assert_eq!(
11100            f.run(&[b"JSON.STRAPPEND", b"doc", b".a[*]", br#""z""#]),
11101            ":4\r\n"
11102        );
11103        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[true,false,true]}"#]);
11104        assert_eq!(
11105            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11106            bulk("false").as_str()
11107        );
11108        // Every one of them wrote to all three matches, whichever one it chose
11109        // to answer about.
11110        assert_eq!(
11111            f.run(&[b"JSON.GET", b"doc", b".a"]),
11112            bulk("[false,true,false]").as_str()
11113        );
11114
11115        // A match of the wrong kind is skipped rather than being the answer, so
11116        // a path that found a string and then two arrays still answers.
11117        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x",[1],[1,2]]}"#]);
11118        assert_eq!(
11119            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11120            ":3\r\n"
11121        );
11122        assert_eq!(
11123            f.run(&[b"JSON.GET", b"doc", b".a"]),
11124            bulk(r#"["x",[1,9],[1,2,9]]"#).as_str()
11125        );
11126        // Nothing of the right kind anywhere is the error, and that is the only
11127        // case that is.
11128        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":["x","y"]}"#]);
11129        assert_eq!(
11130            f.run(&[b"JSON.ARRAPPEND", b"doc", b".a[*]", b"9"]),
11131            "-ERR Path does not exist or not an array\r\n"
11132        );
11133        assert_eq!(
11134            f.run(&[b"JSON.TOGGLE", b"doc", b".a[*]"]),
11135            "-ERR Path does not exist or not a bool\r\n"
11136        );
11137        // The one array that was there and had nothing in it is an answer and
11138        // not a skip, so the pop answers about it rather than about the array
11139        // after it.
11140        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[[],[2,3]]}"#]);
11141        assert_eq!(f.run(&[b"JSON.ARRPOP", b"doc", b".a[*]"]), "$-1\r\n");
11142        assert_eq!(
11143            f.run(&[b"JSON.GET", b"doc", b".a"]),
11144            bulk("[[],[2]]").as_str()
11145        );
11146    }
11147
11148    /// A path that matched a value and something inside that value writes to
11149    /// both, which is what `$..` and a nested wildcard are for.
11150    #[test]
11151    fn a_write_reaches_a_match_that_sits_inside_another_match() {
11152        let mut f = Fixture::new();
11153        let nested = br#"{"a":[{"a":[7]},{"a":[7,7]}]}"#;
11154
11155        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11156        assert_eq!(
11157            f.run(&[b"JSON.ARRAPPEND", b"doc", b"$..a", b"9"]),
11158            "*3\r\n:3\r\n:2\r\n:3\r\n"
11159        );
11160        assert_eq!(
11161            f.run(&[b"JSON.GET", b"doc", b"$"]),
11162            bulk(r#"[{"a":[{"a":[7,9]},{"a":[7,7,9]},9]}]"#).as_str()
11163        );
11164
11165        // The same for a trim, where the outer array keeps the two elements the
11166        // inner writes landed in.
11167        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11168        assert_eq!(
11169            f.run(&[b"JSON.ARRTRIM", b"doc", b"$..a", b"0", b"0"]),
11170            "*3\r\n:1\r\n:1\r\n:1\r\n"
11171        );
11172        assert_eq!(
11173            f.run(&[b"JSON.GET", b"doc", b"$"]),
11174            bulk(r#"[{"a":[{"a":[7]}]}]"#).as_str()
11175        );
11176
11177        // And for a number, where the first match is the object the outer array
11178        // holds and only the two inside it are numbers.
11179        f.run(&[b"JSON.SET", b"doc", b"$", nested]);
11180        assert_eq!(
11181            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$..a[0]", b"1"]),
11182            bulk("[null,8,8]").as_str()
11183        );
11184    }
11185
11186    /// The value a write is given is looked at only once the path has found
11187    /// something of the right kind to use it on.
11188    #[test]
11189    fn a_bad_operand_is_not_the_answer_when_the_path_found_nothing_to_use_it_on() {
11190        let mut f = Fixture::new();
11191        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"n":7,"s":"t"}"#]);
11192
11193        // A string is not a number, so the path answers first and the `"x"` is
11194        // never looked at. Same for the value that is not JSON at all.
11195        assert_eq!(
11196            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", br#""x""#]),
11197            bulk("[null]").as_str()
11198        );
11199        assert_eq!(
11200            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.s", b"notjson"]),
11201            bulk("[null]").as_str()
11202        );
11203        assert_eq!(
11204            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.missing", b"notjson"]),
11205            bulk("[]").as_str()
11206        );
11207        assert_eq!(
11208            f.run(&[b"JSON.NUMINCRBY", b"doc", b".s", br#""x""#]),
11209            "-ERR Path does not exist or does not contains a number\r\n"
11210        );
11211        // A number match anywhere and the value is looked at after all.
11212        assert_eq!(
11213            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.n", br#""x""#]),
11214            "-bad input number\r\n"
11215        );
11216
11217        // JSON.STRAPPEND follows the same order with its own two answers.
11218        assert_eq!(
11219            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.n", b"1"]),
11220            "*1\r\n$-1\r\n"
11221        );
11222        assert_eq!(
11223            f.run(&[b"JSON.STRAPPEND", b"doc", b".n", b"1"]),
11224            "-ERR Path does not exist or not a string\r\n"
11225        );
11226        assert_eq!(
11227            f.run(&[b"JSON.STRAPPEND", b"doc", b"$.s", b"1"]),
11228            "-WRONGTYPE wrong type of path value - expected string\r\n"
11229        );
11230
11231        // A key that is not there still comes before either of them.
11232        assert_eq!(
11233            f.run(&[b"JSON.NUMINCRBY", b"nope", b"$.a", br#""x""#]),
11234            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11235        );
11236        assert_eq!(
11237            f.run(&[b"JSON.STRAPPEND", b"nope", b"$.a", b"1"]),
11238            "-ERR could not perform this operation on a key that doesn't exist\r\n"
11239        );
11240    }
11241
11242    /// RFC 7386 in one test: a null deletes, everything else merges, and a
11243    /// patch that is not an object replaces what it lands on.
11244    #[test]
11245    fn a_merge_patch_adds_replaces_and_deletes_in_one_write() {
11246        let mut f = Fixture::new();
11247
11248        // A key that is not there is created at the root, nulls and all,
11249        // because a deletion with nothing to delete is still what the client
11250        // sent.
11251        assert_eq!(
11252            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"x":null,"y":1}"#]),
11253            "+OK\r\n"
11254        );
11255        assert_eq!(
11256            f.run(&[b"JSON.GET", b"doc", b"$"]),
11257            bulk(r#"[{"x":null,"y":1}]"#).as_str()
11258        );
11259
11260        // Onto something that is there, a null deletes the member of that name
11261        // and the rest is merged one level at a time.
11262        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1,"c":2},"d":3}"#]);
11263        assert_eq!(
11264            f.run(&[b"JSON.MERGE", b"doc", b"$", br#"{"a":{"b":null,"e":4}}"#]),
11265            "+OK\r\n"
11266        );
11267        assert_eq!(
11268            f.run(&[b"JSON.GET", b"doc", b"$"]),
11269            bulk(r#"[{"a":{"c":2,"e":4},"d":3}]"#).as_str()
11270        );
11271
11272        // A patch that is not an object replaces what it is merged onto.
11273        assert_eq!(f.run(&[b"JSON.MERGE", b"doc", b"$.a", b"[1,2]"]), "+OK\r\n");
11274        assert_eq!(
11275            f.run(&[b"JSON.GET", b"doc", b"$"]),
11276            bulk(r#"[{"a":[1,2],"d":3}]"#).as_str()
11277        );
11278
11279        // A patch object onto a value that is not an object starts from an
11280        // empty object, so this time the null has nothing to delete and is
11281        // dropped rather than stored.
11282        assert_eq!(
11283            f.run(&[b"JSON.MERGE", b"doc", b"$.d", br#"{"p":null,"q":9}"#]),
11284            "+OK\r\n"
11285        );
11286        assert_eq!(
11287            f.run(&[b"JSON.GET", b"doc", b"$"]),
11288            bulk(r#"[{"a":[1,2],"d":{"q":9}}]"#).as_str()
11289        );
11290
11291        // A member one level past the end of the document is created and keeps
11292        // its nulls, two levels past it is a write that did not happen, and a
11293        // path that would have to invent where it goes is the unprefixed line.
11294        assert_eq!(
11295            f.run(&[b"JSON.MERGE", b"doc", b"$.new", br#"{"z":null}"#]),
11296            "+OK\r\n"
11297        );
11298        assert_eq!(
11299            f.run(&[b"JSON.GET", b"doc", b"$.new"]),
11300            bulk(r#"[{"z":null}]"#).as_str()
11301        );
11302        assert_eq!(
11303            f.run(&[b"JSON.MERGE", b"doc", b"$.no.deep", b"1"]),
11304            "$-1\r\n"
11305        );
11306        assert_eq!(
11307            f.run(&[b"JSON.MERGE", b"doc", b"$.no.*", b"1"]),
11308            "-Err wrong static path\r\n"
11309        );
11310
11311        // A wildcard merges every match.
11312        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"n":1},"b":{"n":2}}"#]);
11313        assert_eq!(
11314            f.run(&[b"JSON.MERGE", b"doc", b"$.*", br#"{"m":0}"#]),
11315            "+OK\r\n"
11316        );
11317        assert_eq!(
11318            f.run(&[b"JSON.GET", b"doc", b"$"]),
11319            bulk(r#"[{"a":{"m":0,"n":1},"b":{"m":0,"n":2}}]"#).as_str()
11320        );
11321
11322        // The three ways to get it wrong.
11323        assert_eq!(
11324            f.run(&[b"JSON.MERGE", b"doc", b"$", b"{}", b"more"]),
11325            "-ERR syntax error\r\n"
11326        );
11327        assert_eq!(
11328            f.run(&[b"JSON.MERGE", b"gone", b"$.a", b"1"]),
11329            "-ERR new objects must be created at the root\r\n"
11330        );
11331        f.run(&[b"SET", b"str", b"x"]);
11332        assert_eq!(
11333            f.run(&[b"JSON.MERGE", b"str", b"$", b"1"]),
11334            "-Existing key has wrong Redis type\r\n"
11335        );
11336    }
11337
11338    /// A descent is the one path that matches a value and something inside that
11339    /// same value, and the inner merge has to survive the outer one.
11340    #[test]
11341    fn a_merge_down_a_descent_keeps_what_the_inner_match_did() {
11342        let mut f = Fixture::new();
11343        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11344        assert_eq!(
11345            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"m":1}"#]),
11346            "+OK\r\n"
11347        );
11348        // `a`, `a.b`, `c` and `c[0]` all match. `a.b` is merged first and `a` is
11349        // merged onto the result, so the `{"m":1}` written into `a.b` is still
11350        // there. Doing it the other way round would leave `{"a":{"b":1,"m":1}}`.
11351        assert_eq!(
11352            f.run(&[b"JSON.GET", b"doc", b"$"]),
11353            bulk(r#"[{"a":{"b":{"m":1},"m":1},"c":{"m":1}}]"#).as_str()
11354        );
11355
11356        // A deletion down the same path, which is the case where the inner
11357        // merge empties the object the outer one then copies.
11358        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":{"b":1},"c":[2]}"#]);
11359        assert_eq!(
11360            f.run(&[b"JSON.MERGE", b"doc", b"$..*", br#"{"a":null}"#]),
11361            "+OK\r\n"
11362        );
11363        assert_eq!(
11364            f.run(&[b"JSON.GET", b"doc", b"$"]),
11365            bulk(r#"[{"a":{"b":{}},"c":{}}]"#).as_str()
11366        );
11367    }
11368
11369    /// A filter is a selector like any other, so every command that takes a path
11370    /// takes one, reads and writes alike.
11371    #[test]
11372    fn a_filter_path_reads_and_writes_the_members_it_keeps() {
11373        let mut f = Fixture::new();
11374        let doc = br#"{"book":[{"t":"a","p":8},{"t":"b","p":13},{"t":"c","p":9}],"cap":10}"#;
11375        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11376
11377        assert_eq!(
11378            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < 10)].t"]),
11379            bulk(r#"["a","c"]"#).as_str()
11380        );
11381        // `$` inside the expression is the document, so a member can be measured
11382        // against something that is not inside it.
11383        assert_eq!(
11384            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p < $.cap)].t"]),
11385            bulk(r#"["a","c"]"#).as_str()
11386        );
11387        // The legacy syntax takes one too, and answers the first match.
11388        assert_eq!(
11389            f.run(&[b"JSON.GET", b"doc", b"book[?(@.p < 10)].t"]),
11390            bulk(r#""a""#).as_str()
11391        );
11392        assert_eq!(
11393            f.run(&[b"JSON.TYPE", b"doc", b"$.book[?(@.p > 10)]"]),
11394            "*1\r\n$6\r\nobject\r\n"
11395        );
11396
11397        // A write goes through it as far as a value that is already there. A
11398        // field that is not there yet has nowhere definite to go, which is the
11399        // same refusal a wildcard gets.
11400        assert_eq!(
11401            f.run(&[b"JSON.NUMINCRBY", b"doc", b"$.book[?(@.p < 10)].p", b"1"]),
11402            bulk("[9,10]").as_str()
11403        );
11404        assert_eq!(
11405            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].t", br#""B""#]),
11406            "+OK\r\n"
11407        );
11408        assert_eq!(
11409            f.run(&[b"JSON.SET", b"doc", b"$.book[?(@.p == 13)].n", b"1"]),
11410            "-Err wrong static path\r\n"
11411        );
11412        assert_eq!(
11413            f.run(&[b"JSON.DEL", b"doc", b"$.book[?(@.p > 9)]"]),
11414            ":2\r\n"
11415        );
11416        assert_eq!(
11417            f.run(&[b"JSON.GET", b"doc", b"$"]),
11418            bulk(r#"[{"cap":10,"book":[{"p":9,"t":"a"}]}]"#).as_str()
11419        );
11420
11421        // A path that does not parse is refused before the document is read, so
11422        // a key that is not there answers the same way.
11423        assert!(
11424            f.run(&[b"JSON.GET", b"doc", b"$.book[?(@.p <)]"])
11425                .starts_with("-ERR")
11426        );
11427        assert!(
11428            f.run(&[b"JSON.GET", b"nokey", b"$.book[?(@.p <)]"])
11429                .starts_with("-ERR")
11430        );
11431    }
11432
11433    /// The operators past the comparisons, over the wire rather than in the
11434    /// parser's own tests, so that a client can reach all of them.
11435    #[test]
11436    fn a_filter_takes_the_membership_operators_and_the_methods_too() {
11437        let mut f = Fixture::new();
11438        let doc = br#"{"box":[{"t":"a","n":[1,2],"g":"x"},{"t":"b","n":[9],"g":"y"}]}"#;
11439        f.run(&[b"JSON.SET", b"doc", b"$", doc]);
11440
11441        for (path, want) in [
11442            (&b"$.box[?(@.g in [\"x\"])].t"[..], r#"["a"]"#),
11443            (b"$.box[?(@.g nin [\"x\"])].t", r#"["b"]"#),
11444            (b"$.box[?(@.n anyof [2,3])].t", r#"["a"]"#),
11445            (b"$.box[?(@.n subsetof [1,2,3])].t", r#"["a"]"#),
11446            (b"$.box[?(@.n size 2)].t", r#"["a"]"#),
11447            (b"$.box[?(@.n empty false)].t", r#"["a","b"]"#),
11448            (b"$.box[?(@.n.length() == 1)].t", r#"["b"]"#),
11449            (b"$.box[?(@.n.sum() > 5)].t", r#"["b"]"#),
11450            (b"$.box[?(@.n[0] + 1 == 2)].t", r#"["a"]"#),
11451            (b"$.box[?(@~ size 3)].t", r#"["a","b"]"#),
11452            (b"$.box[?(@.n~)].t", "[]"),
11453            (b"$.box[?(@.n sizeof 2)].t", r#"["a"]"#),
11454            (b"$.box[?(-@.n[0] == -9)].t", r#"["b"]"#),
11455            (b"$.box[?(1 in @.n)].t", r#"["a"]"#),
11456            (b"$.box[?(\"g\" in @~)].t", r#"["a","b"]"#),
11457        ] {
11458            assert_eq!(f.run(&[b"JSON.GET", b"doc", path]), bulk(want).as_str());
11459        }
11460
11461        // A write goes through one of these the same way it goes through a
11462        // comparison.
11463        assert_eq!(
11464            f.run(&[b"JSON.SET", b"doc", b"$.box[?(@.n size 1)].g", br#""z""#]),
11465            "+OK\r\n"
11466        );
11467        assert_eq!(
11468            f.run(&[b"JSON.GET", b"doc", b"$.box[?(@.g == \"z\")].t"]),
11469            bulk(r#"["b"]"#).as_str()
11470        );
11471    }
11472
11473    /// D-41. RedisJSON refuses this one, and which document it refuses is
11474    /// decided by how it happens to hold an array of numbers.
11475    #[test]
11476    fn a_merge_onto_a_number_inside_an_array_is_a_merge_and_not_an_error() {
11477        let mut f = Fixture::new();
11478        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2]}"#]);
11479        assert_eq!(
11480            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11481            "+OK\r\n"
11482        );
11483        assert_eq!(
11484            f.run(&[b"JSON.GET", b"doc", b"$"]),
11485            bulk(r#"[{"a":[{"x":1},2]}]"#).as_str()
11486        );
11487        // The same document with one element that is not an integer is the one
11488        // RedisJSON is happy with, and it goes the same way here.
11489        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,"s"]}"#]);
11490        assert_eq!(
11491            f.run(&[b"JSON.MERGE", b"doc", b"$.a[0]", br#"{"x":1}"#]),
11492            "+OK\r\n"
11493        );
11494        assert_eq!(
11495            f.run(&[b"JSON.GET", b"doc", b"$"]),
11496            bulk(r#"[{"a":[{"x":1},"s"]}]"#).as_str()
11497        );
11498    }
11499
11500    /// `JSON.MSET` checks what it can before it writes anything and skips the
11501    /// one thing it cannot, which is a path with nowhere to put its value.
11502    #[test]
11503    fn an_mset_writes_every_triple_it_can_and_checks_the_rest_up_front() {
11504        let mut f = Fixture::new();
11505        assert_eq!(
11506            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b", b"$", b"2"]),
11507            "+OK\r\n"
11508        );
11509        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[1]").as_str());
11510        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[2]").as_str());
11511
11512        // A repeated key takes the last write.
11513        assert_eq!(
11514            f.run(&[b"JSON.MSET", b"a", b"$", b"3", b"a", b"$", b"4"]),
11515            "+OK\r\n"
11516        );
11517        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$"]), bulk("[4]").as_str());
11518
11519        // A triple whose path names nowhere is skipped, the others are still
11520        // written and the reply turns into a nil. Both ways round, because a
11521        // loop that gave up at the first skip would agree with this on one
11522        // order and not on the other.
11523        f.run(&[b"JSON.SET", b"a", b"$", br#"{"n":1}"#]);
11524        assert_eq!(
11525            f.run(&[b"JSON.MSET", b"a", b"$.no.deep", b"9", b"b", b"$", b"5"]),
11526            "$-1\r\n"
11527        );
11528        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[5]").as_str());
11529        assert_eq!(
11530            f.run(&[b"JSON.MSET", b"b", b"$", b"6", b"a", b"$.no.deep", b"9"]),
11531            "$-1\r\n"
11532        );
11533        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11534
11535        // A value that is not JSON, a key holding something else and a path
11536        // that would have to create a document below its own root are all
11537        // checked before anything is written, so the good triple next to them
11538        // does not happen either.
11539        f.run(&[b"SET", b"str", b"x"]);
11540        assert_eq!(
11541            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"b", b"$", b"notjson"]),
11542            "-this is not the start of a value, at byte 0 of the JSON text\r\n"
11543        );
11544        assert_eq!(
11545            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"str", b"$", b"1"]),
11546            "-Existing key has wrong Redis type\r\n"
11547        );
11548        assert_eq!(
11549            f.run(&[b"JSON.MSET", b"a", b"$.n", b"7", b"gone", b"$.x", b"1"]),
11550            "-ERR new objects must be created at the root\r\n"
11551        );
11552        assert_eq!(f.run(&[b"JSON.GET", b"a", b"$.n"]), bulk("[1]").as_str());
11553
11554        // The two errors a path can be are checked up front as well, so the
11555        // triple before them is not written either. A wildcard that matched
11556        // nothing has nowhere to invent, and an index that is not in the array
11557        // is out of range, and both of them stop the whole command.
11558        assert_eq!(
11559            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$.no.*", b"9"]),
11560            "-Err wrong static path\r\n"
11561        );
11562        assert_eq!(
11563            f.run(&[b"JSON.MSET", b"b", b"$", b"8", b"a", b"$[0]", b"9"]),
11564            "-ERR array index out of range\r\n"
11565        );
11566        assert_eq!(f.run(&[b"JSON.GET", b"b", b"$"]), bulk("[6]").as_str());
11567
11568        // Every triple is worked out against the keyspace as the command found
11569        // it, so a second triple on the same key does not see the first one and
11570        // the last write is the one that stays.
11571        f.run(&[b"JSON.SET", b"c", b"$", br#"{"n":1}"#]);
11572        assert_eq!(
11573            f.run(&[b"JSON.MSET", b"c", b"$", br#"{"n":2}"#, b"c", b"$.n", b"3"]),
11574            "+OK\r\n"
11575        );
11576        assert_eq!(
11577            f.run(&[b"JSON.GET", b"c", b"$"]),
11578            bulk(r#"[{"n":3}]"#).as_str()
11579        );
11580
11581        // An argument count that is not a run of key, path and value is the
11582        // arity error rather than a syntax one.
11583        assert_eq!(
11584            f.run(&[b"JSON.MSET", b"a", b"$", b"1", b"b"]),
11585            "-ERR wrong number of arguments for 'json.mset' command\r\n"
11586        );
11587    }
11588
11589    /// `JSON.RESP` hands back RESP types, and the marker element is what tells
11590    /// an empty array and an empty object apart.
11591    #[test]
11592    fn json_resp_answers_the_document_as_resp_types() {
11593        let mut f = Fixture::new();
11594        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":1,"b":[2,"c"]}"#]);
11595        assert_eq!(
11596            f.run(&[b"JSON.RESP", b"doc"]),
11597            "*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"
11598        );
11599        // A JSONPath wraps the same answer in one more array.
11600        assert_eq!(
11601            f.run(&[b"JSON.RESP", b"doc", b"$.b"]),
11602            "*1\r\n*3\r\n+[\r\n:2\r\n$1\r\nc\r\n"
11603        );
11604
11605        f.run(&[
11606            b"JSON.SET",
11607            b"doc",
11608            b"$",
11609            br#"{"f":2.5,"t":true,"z":null,"e":[],"o":{}}"#,
11610        ]);
11611        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".e"]), "*1\r\n+[\r\n");
11612        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".o"]), "*1\r\n+{\r\n");
11613        // A double goes out as its text, so a client reads the same digits
11614        // `JSON.GET` would have given it.
11615        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".f"]), bulk("2.5").as_str());
11616        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".t"]), "+true\r\n");
11617        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b".z"]), "$-1\r\n");
11618
11619        // A missing legacy path is an error, a missing JSONPath is an empty
11620        // array, and a key that is not there is a nil on either.
11621        assert_eq!(
11622            f.run(&[b"JSON.RESP", b"doc", b".nope"]),
11623            "-ERR Path does not exist\r\n"
11624        );
11625        assert_eq!(f.run(&[b"JSON.RESP", b"doc", b"$.nope"]), "*0\r\n");
11626        assert_eq!(f.run(&[b"JSON.RESP", b"gone"]), "$-1\r\n");
11627        assert_eq!(f.run(&[b"JSON.RESP", b"gone", b"$"]), "$-1\r\n");
11628    }
11629
11630    /// `JSON.DEBUG` answers a byte count that is this encoding's, so the test
11631    /// pins the shapes and that the two syntaxes agree rather than a number
11632    /// read off another server. That is D-42.
11633    #[test]
11634    fn json_debug_answers_a_byte_count_and_its_own_help() {
11635        let mut f = Fixture::new();
11636        f.run(&[b"JSON.SET", b"doc", b"$", br#"{"a":[1,2],"s":"hello"}"#]);
11637        let one = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".s"]);
11638        assert!(one.starts_with(':'), "{one}");
11639        assert_eq!(
11640            f.run(&[b"JSON.DEBUG", b"memory", b"doc", b"$.s"]),
11641            format!("*1\r\n{one}")
11642        );
11643        let whole = f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc"]);
11644        assert!(whole.starts_with(':') && whole.len() > one.len(), "{whole}");
11645
11646        // A key that is not there is a zero on a legacy path and an empty set
11647        // on a JSONPath, which is the one reader here that does not answer nil
11648        // for it.
11649        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone"]), ":0\r\n");
11650        assert_eq!(f.run(&[b"JSON.DEBUG", b"MEMORY", b"gone", b"$"]), "*0\r\n");
11651        assert_eq!(
11652            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b".nope"]),
11653            "-ERR Path does not exist\r\n"
11654        );
11655        assert_eq!(
11656            f.run(&[b"JSON.DEBUG", b"MEMORY", b"doc", b"$.nope"]),
11657            "*0\r\n"
11658        );
11659
11660        assert_eq!(
11661            f.run(&[b"JSON.DEBUG", b"HELP"]),
11662            "*2\r\n$42\r\nMEMORY <key> [path] - reports memory usage\r\n\
11663             $34\r\nHELP                - this message\r\n"
11664        );
11665        assert_eq!(
11666            f.run(&[b"JSON.DEBUG", b"NOPE"]),
11667            "-ERR unknown subcommand - try `JSON.DEBUG HELP`\r\n"
11668        );
11669        assert_eq!(
11670            f.run(&[b"JSON.DEBUG", b"MEMORY"]),
11671            "-ERR wrong number of arguments for 'json.debug' command\r\n"
11672        );
11673    }
11674
11675    // ---------------------------------------------------------------- vector
11676
11677    /// The first `VADD` fixes the dimension and every one after it has to
11678    /// agree, because there is no create command to say it earlier.
11679    #[test]
11680    fn the_first_vadd_decides_how_wide_the_set_is() {
11681        let mut f = Fixture::new();
11682        assert_eq!(
11683            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]),
11684            ":1\r\n"
11685        );
11686        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
11687        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11688        // A second vector under the same name replaces it and says so with a
11689        // zero, so an ingest can count what it created.
11690        assert_eq!(
11691            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"east"]),
11692            ":0\r\n"
11693        );
11694        assert_eq!(f.run(&[b"VCARD", b"v"]), ":1\r\n");
11695        // Three dimensions into a two dimensional set names both numbers, since
11696        // a client that gets this wrong needs to know which end is which.
11697        assert_eq!(
11698            f.run(&[b"VADD", b"v", b"VALUES", b"3", b"1", b"0", b"0", b"up"]),
11699            "-ERR Vector dimension mismatch - got 3 but set has 2\r\n"
11700        );
11701        // A vector of zeros has no direction, so a cosine set has nowhere to
11702        // put it.
11703        assert_eq!(
11704            f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"0", b"nowhere"]),
11705            "-ERR a cosine collection compares directions and a vector of length zero has none\r\n"
11706        );
11707        // Nothing above created a key, and a set that never took a vector has
11708        // no dimension to report.
11709        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
11710        assert_eq!(f.run(&[b"VDIM", b"fresh"]), "-ERR key does not exist\r\n");
11711        assert_eq!(f.run(&[b"VCARD", b"fresh"]), ":0\r\n");
11712    }
11713
11714    /// What a client sent comes back out, and what a client asked for is a
11715    /// similarity and not the distance underneath it.
11716    #[test]
11717    fn vemb_gives_back_the_vector_and_vsim_gives_back_a_similarity() {
11718        let mut f = Fixture::new();
11719        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"4", b"a"]);
11720        // The set stored the unit vector and the length is multiplied back on
11721        // the way out, so `3 4` and not `0.6 0.8`.
11722        assert_eq!(
11723            f.run(&[b"VEMB", b"v", b"a"]),
11724            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
11725        );
11726        assert_eq!(f.run(&[b"VEMB", b"v", b"nobody"]), "*-1\r\n");
11727        assert_eq!(f.run(&[b"VEMB", b"nokey", b"a"]), "*-1\r\n");
11728
11729        // On the axes, where the unit vector is exact and so is the dot
11730        // product, both ends of the scale come out exact: the same direction is
11731        // 1 and the opposite one is 0, with a right angle at a half.
11732        let mut f = Fixture::new();
11733        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"3", b"0", b"a"]);
11734        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"-1", b"0", b"opposite"]);
11735        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"7", b"across"]);
11736        assert_eq!(
11737            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"2", b"0", b"WITHSCORES"]),
11738            "*6\r\n$1\r\na\r\n$1\r\n1\r\n$6\r\nacross\r\n$3\r\n0.5\r\n\
11739             $8\r\nopposite\r\n$1\r\n0\r\n"
11740        );
11741        // A search from an element leaves that element out, since it is always
11742        // its own nearest neighbour.
11743        assert_eq!(
11744            f.run(&[b"VSIM", b"v", b"ELE", b"a"]),
11745            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11746        );
11747        // An element that is not there is an empty answer and not an error,
11748        // which is what a missing key gives too.
11749        assert_eq!(f.run(&[b"VSIM", b"v", b"ELE", b"nobody"]), "*0\r\n");
11750        assert_eq!(f.run(&[b"VSIM", b"nokey", b"ELE", b"a"]), "*0\r\n");
11751        // COUNT bounds it and TRUTH reads every vector rather than the codes,
11752        // which has to agree with the index on a set this small.
11753        assert_eq!(
11754            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1"]),
11755            "*1\r\n$6\r\nacross\r\n"
11756        );
11757        assert_eq!(
11758            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"TRUTH"]),
11759            "*2\r\n$6\r\nacross\r\n$8\r\nopposite\r\n"
11760        );
11761        // EF widens how much of the index is read and does not change how many
11762        // answers come back, so a wide search still returns what COUNT asked
11763        // for.
11764        assert_eq!(
11765            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"COUNT", b"1", b"EF", b"500"]),
11766            "*1\r\n$6\r\nacross\r\n"
11767        );
11768
11769        // On RESP3 a scored search is a map, which is what the vector set
11770        // module replies and is not what ZRANGE does here.
11771        let mut g = Fixture::new();
11772        g.run(&[b"HELLO", b"3"]);
11773        g.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11774        assert_eq!(
11775            g.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHSCORES"]),
11776            "%1\r\n$4\r\neast\r\n,1\r\n"
11777        );
11778    }
11779
11780    /// The attribute pair, and the one reply that means two things.
11781    #[test]
11782    fn an_attribute_is_bytes_and_an_empty_one_takes_it_off() {
11783        let mut f = Fixture::new();
11784        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11785        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11786        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]), ":1\r\n");
11787        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$7\r\n{\"k\":1}\r\n");
11788        // Not parsed as JSON, because nothing reads into it yet and refusing a
11789        // write for a rule nothing enforces would be the wrong trade.
11790        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b"not json"]), ":1\r\n");
11791        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$8\r\nnot json\r\n");
11792        // An empty string clears it, which is Redis's spelling of the removal.
11793        assert_eq!(f.run(&[b"VSETATTR", b"v", b"east", b""]), ":1\r\n");
11794        assert_eq!(f.run(&[b"VGETATTR", b"v", b"east"]), "$-1\r\n");
11795        // An element that is not there answers zero rather than being created,
11796        // since an attribute with no vector under it is not a thing this holds.
11797        assert_eq!(f.run(&[b"VSETATTR", b"v", b"nobody", b"{}"]), ":0\r\n");
11798        assert_eq!(f.run(&[b"VSETATTR", b"nokey", b"east", b"{}"]), ":0\r\n");
11799        assert_eq!(f.run(&[b"EXISTS", b"nokey"]), ":0\r\n");
11800        // A null for an element with no attribute and a null for one that is
11801        // not there. VISMEMBER is how a client tells the two apart.
11802        assert_eq!(f.run(&[b"VGETATTR", b"v", b"nobody"]), "$-1\r\n");
11803        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"east"]), ":1\r\n");
11804        assert_eq!(f.run(&[b"VISMEMBER", b"v", b"nobody"]), ":0\r\n");
11805        assert_eq!(f.run(&[b"VISMEMBER", b"nokey", b"east"]), ":0\r\n");
11806
11807        // WITHATTRIBS carries it alongside the answers.
11808        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11809        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11810        assert_eq!(
11811            f.run(&[b"VSIM", b"v", b"VALUES", b"2", b"1", b"0", b"WITHATTRIBS"]),
11812            "*4\r\n$4\r\neast\r\n$7\r\n{\"k\":1}\r\n$5\r\nnorth\r\n$-1\r\n"
11813        );
11814    }
11815
11816    /// The slot a removed element had is reused, and nothing that was beside it
11817    /// comes back with the next element to get it.
11818    #[test]
11819    fn vrem_takes_the_attribute_with_it() {
11820        let mut f = Fixture::new();
11821        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11822        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11823        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":1\r\n");
11824        assert_eq!(f.run(&[b"VREM", b"v", b"east"]), ":0\r\n");
11825        assert_eq!(f.run(&[b"VREM", b"nokey", b"east"]), ":0\r\n");
11826        // The key went with the last element, the way every other collection
11827        // here works.
11828        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11829
11830        // The next element is given the slot the removed one had, and it comes
11831        // with no attribute on it.
11832        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
11833        f.run(&[b"VSETATTR", b"v", b"east", b"{\"k\":1}"]);
11834        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
11835        f.run(&[b"VREM", b"v", b"east"]);
11836        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"1", b"between"]);
11837        assert_eq!(f.run(&[b"VGETATTR", b"v", b"between"]), "$-1\r\n");
11838    }
11839
11840    /// `VINFO` says what the index is before it says anything a client could
11841    /// mistake for a graph.
11842    #[test]
11843    fn vinfo_says_partition_first() {
11844        let mut f = Fixture::new();
11845        f.run(&[
11846            b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east", b"M", b"32",
11847        ]);
11848        f.run(&[b"VSETATTR", b"v", b"east", b"{}"]);
11849        let info = f.run(&[b"VINFO", b"v"]);
11850        assert!(info.starts_with("*24\r\n$10\r\nindex-type\r\n$9\r\npartition\r\n"));
11851        // What the client asked for and not what happened to the tuning, which
11852        // is `10` section 7: M is recorded and changes nothing.
11853        assert!(info.contains("$6\r\nhnsw-m\r\n:32\r\n"), "{info}");
11854        assert!(info.contains("$10\r\nvector-dim\r\n:2\r\n"), "{info}");
11855        assert!(info.contains("$16\r\nattributes-count\r\n:1\r\n"), "{info}");
11856        // The quantisation is recorded and not applied, which is D-32, so it
11857        // reports back what was sent.
11858        assert!(
11859            info.contains("$10\r\nquant-type\r\n$3\r\nf32\r\n"),
11860            "{info}"
11861        );
11862        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north", b"BIN"]);
11863        assert!(
11864            f.run(&[b"VINFO", b"v"])
11865                .contains("$10\r\nquant-type\r\n$3\r\nbin\r\n")
11866        );
11867        assert_eq!(f.run(&[b"VINFO", b"nokey"]), "$-1\r\n");
11868    }
11869
11870    /// The option that asks for something this index does not have says so
11871    /// rather than doing something else quietly.
11872    #[test]
11873    fn reduce_is_refused_and_not_ignored() {
11874        let mut f = Fixture::new();
11875        let reduce = f.run(&[
11876            b"VADD", b"v", b"REDUCE", b"1", b"VALUES", b"2", b"1", b"0", b"east",
11877        ]);
11878        assert!(
11879            reduce.starts_with("-ERR REDUCE is not supported."),
11880            "{reduce}"
11881        );
11882        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
11883    }
11884
11885    /// A filtered search answers with the nearest elements that match, and an
11886    /// expression that is not one is an error before the key is looked at.
11887    #[test]
11888    fn vsim_filter_reads_the_attributes() {
11889        let mut f = Fixture::new();
11890        for (name, x, y, attr) in [
11891            ("a", "1", "0", r#"{"lang":"en","year":1999}"#),
11892            ("b", "9", "1", r#"{"lang":"fr","year":2005}"#),
11893            ("c", "8", "2", r#"{"lang":"en","year":1970}"#),
11894            ("d", "7", "3", r#"{"lang":"en","year":2020}"#),
11895        ] {
11896            f.run(&[
11897                b"VADD",
11898                b"v",
11899                b"VALUES",
11900                b"2",
11901                x.as_bytes(),
11902                y.as_bytes(),
11903                name.as_bytes(),
11904                b"SETATTR",
11905                attr.as_bytes(),
11906            ]);
11907        }
11908        // `b` is the nearest to the query and is the one the filter drops, so
11909        // this is the answer a filter applied afterwards would have got wrong.
11910        assert_eq!(
11911            f.run(&[
11912                b"VSIM",
11913                b"v",
11914                b"VALUES",
11915                b"2",
11916                b"9",
11917                b"1",
11918                b"COUNT",
11919                b"2",
11920                b"FILTER",
11921                b".lang == \"en\"",
11922            ]),
11923            "*2\r\n$1\r\na\r\n$1\r\nc\r\n"
11924        );
11925        // A number is compared as a number, and the two halves of an `and` both
11926        // have to hold.
11927        assert_eq!(
11928            f.run(&[
11929                b"VSIM",
11930                b"v",
11931                b"VALUES",
11932                b"2",
11933                b"9",
11934                b"1",
11935                b"FILTER",
11936                b".lang == 'en' and .year > 1980",
11937            ]),
11938            "*2\r\n$1\r\na\r\n$1\r\nd\r\n"
11939        );
11940        // A list, and a field an element does not have.
11941        assert_eq!(
11942            f.run(&[
11943                b"VSIM",
11944                b"v",
11945                b"VALUES",
11946                b"2",
11947                b"9",
11948                b"1",
11949                b"FILTER",
11950                b".lang in ['fr', 'de']",
11951            ]),
11952            "*1\r\n$1\r\nb\r\n"
11953        );
11954        assert_eq!(
11955            f.run(&[
11956                b"VSIM",
11957                b"v",
11958                b"VALUES",
11959                b"2",
11960                b"9",
11961                b"1",
11962                b"FILTER",
11963                b".rating > 3"
11964            ]),
11965            "*0\r\n"
11966        );
11967        // TRUTH measures every vector, and the filter still decides which ones
11968        // are measured.
11969        assert_eq!(
11970            f.run(&[
11971                b"VSIM",
11972                b"v",
11973                b"VALUES",
11974                b"2",
11975                b"9",
11976                b"1",
11977                b"TRUTH",
11978                b"FILTER",
11979                b".year < 1980",
11980            ]),
11981            "*1\r\n$1\r\nc\r\n"
11982        );
11983        // VSETATTR moves an element in and out of a filter, which means the tag
11984        // beside its code was rewritten and not just the string.
11985        f.run(&[b"VSETATTR", b"v", b"b", r#"{"lang":"en"}"#.as_bytes()]);
11986        assert_eq!(
11987            f.run(&[
11988                b"VSIM",
11989                b"v",
11990                b"VALUES",
11991                b"2",
11992                b"9",
11993                b"1",
11994                b"COUNT",
11995                b"1",
11996                b"FILTER",
11997                b".lang == \"en\"",
11998            ]),
11999            "*1\r\n$1\r\nb\r\n"
12000        );
12001        // And a VADD that replaces the vector keeps the attribute and the tag,
12002        // which is the same rewrite from the other end.
12003        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"9", b"2", b"b"]);
12004        assert_eq!(
12005            f.run(&[
12006                b"VSIM",
12007                b"v",
12008                b"VALUES",
12009                b"2",
12010                b"9",
12011                b"1",
12012                b"COUNT",
12013                b"1",
12014                b"FILTER",
12015                b".lang == \"en\"",
12016            ]),
12017            "*1\r\n$1\r\nb\r\n"
12018        );
12019
12020        // The expression is parsed before the key is read, so a bad one is an
12021        // error whether or not the key is there.
12022        let bad = f.run(&[b"VSIM", b"nokey", b"ELE", b"e", b"FILTER", b".k =="]);
12023        assert_eq!(bad, "-ERR invalid FILTER expression\r\n");
12024        assert_eq!(
12025            f.run(&[b"VSIM", b"v", b"ELE", b"a", b"FILTER", b"junk"]),
12026            "-ERR invalid FILTER expression\r\n"
12027        );
12028        // FILTER-EF raises the effort rather than capping it, and zero is
12029        // Redis's word for no limit, so neither is an error.
12030        assert_eq!(
12031            f.run(&[
12032                b"VSIM",
12033                b"v",
12034                b"VALUES",
12035                b"2",
12036                b"9",
12037                b"1",
12038                b"COUNT",
12039                b"1",
12040                b"FILTER-EF",
12041                b"500",
12042                b"FILTER",
12043                b".lang == 'en'",
12044            ]),
12045            "*1\r\n$1\r\nb\r\n"
12046        );
12047        assert_eq!(
12048            f.run(&[
12049                b"VSIM",
12050                b"v",
12051                b"VALUES",
12052                b"2",
12053                b"9",
12054                b"1",
12055                b"COUNT",
12056                b"1",
12057                b"FILTER-EF",
12058                b"0"
12059            ]),
12060            "*1\r\n$1\r\nb\r\n"
12061        );
12062        assert_eq!(
12063            f.run(&[
12064                b"VSIM",
12065                b"v",
12066                b"VALUES",
12067                b"2",
12068                b"9",
12069                b"1",
12070                b"FILTER-EF",
12071                b"lots"
12072            ]),
12073            "-ERR EF must be a positive integer\r\n"
12074        );
12075    }
12076
12077    /// A vector set key is a key, so the keyspace owns it the way it owns every
12078    /// other one and none of those commands know what is inside it.
12079    #[test]
12080    fn the_keyspace_sees_a_vector_set_key_like_any_other() {
12081        let mut f = Fixture::new();
12082        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12083        assert_eq!(f.run(&[b"TYPE", b"v"]), "+vectorset\r\n");
12084        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":1\r\n");
12085        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"v"]), "$6\r\nrabitq\r\n");
12086        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$1\r\nv\r\n");
12087        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
12088        assert_eq!(f.run(&[b"EXPIRE", b"v", b"100"]), ":1\r\n");
12089        assert_eq!(f.run(&[b"TTL", b"v"]), ":100\r\n");
12090        assert_eq!(f.run(&[b"PERSIST", b"v"]), ":1\r\n");
12091        assert_eq!(f.run(&[b"DEL", b"v"]), ":1\r\n");
12092        assert_eq!(f.run(&[b"EXISTS", b"v"]), ":0\r\n");
12093
12094        // And the wrong type is the wrong type in both directions.
12095        f.run(&[b"SET", b"s", b"1"]);
12096        assert_eq!(
12097            f.run(&[b"VADD", b"s", b"VALUES", b"2", b"1", b"0", b"east"]),
12098            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12099        );
12100        assert_eq!(
12101            f.run(&[b"VCARD", b"s"]),
12102            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12103        );
12104        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12105        assert_eq!(
12106            f.run(&[b"GET", b"v"]),
12107            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12108        );
12109        // A graph and a vector set share the escape in the record tag and are
12110        // still two different types, which is the case the tag alone cannot
12111        // decide.
12112        f.run(&[b"G.NADD", b"social", b"ada"]);
12113        assert_eq!(
12114            f.run(&[b"VCARD", b"social"]),
12115            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12116        );
12117        assert_eq!(
12118            f.run(&[b"G.NGET", b"v", b"ada"]),
12119            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
12120        );
12121    }
12122
12123    /// `VRANDMEMBER` is `SRANDMEMBER` over the element names, in both of its
12124    /// shapes, off the database's own generator.
12125    #[test]
12126    fn vrandmember_has_the_two_shapes_srandmember_has() {
12127        let mut f = Fixture::new();
12128        for (i, name) in [&b"a"[..], b"b", b"c"].iter().enumerate() {
12129            let x = (i + 1).to_string();
12130            f.run(&[b"VADD", b"v", b"VALUES", b"2", x.as_bytes(), b"1", name]);
12131        }
12132        // One element is a bulk string and not an array of one.
12133        let one = f.run(&[b"VRANDMEMBER", b"v"]);
12134        assert!(one.starts_with("$1\r\n"), "{one}");
12135        // A positive count is distinct and stops at the size of the set.
12136        let mut all = f.run(&[b"VRANDMEMBER", b"v", b"9"]);
12137        assert!(all.starts_with("*3\r\n"), "{all}");
12138        for name in ["a", "b", "c"] {
12139            assert!(all.contains(name), "{all} is missing {name}");
12140        }
12141        all = f.run(&[b"VRANDMEMBER", b"v", b"2"]);
12142        assert!(all.starts_with("*2\r\n"), "{all}");
12143        // A negative one draws that many and allows repeats.
12144        let many = f.run(&[b"VRANDMEMBER", b"v", b"-5"]);
12145        assert!(many.starts_with("*5\r\n"), "{many}");
12146        // A key that is not there answers the shape that was asked for.
12147        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey"]), "$-1\r\n");
12148        assert_eq!(f.run(&[b"VRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
12149    }
12150
12151    /// `VLINKS` answers about the index that is here rather than the graph that
12152    /// is not, which is D-2.
12153    #[test]
12154    fn vlinks_reports_one_layer_of_partition_neighbours() {
12155        let mut f = Fixture::new();
12156        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"1", b"0", b"east"]);
12157        f.run(&[b"VADD", b"v", b"VALUES", b"2", b"0", b"1", b"north"]);
12158        // One layer deep, because the index is one layer deep, so a client
12159        // walking layers gets a short list and not a shape it cannot parse.
12160        assert_eq!(
12161            f.run(&[b"VLINKS", b"v", b"east"]),
12162            "*1\r\n*1\r\n$5\r\nnorth\r\n"
12163        );
12164        assert_eq!(
12165            f.run(&[b"VLINKS", b"v", b"east", b"WITHSCORES"]),
12166            "*1\r\n*2\r\n$5\r\nnorth\r\n$3\r\n0.5\r\n"
12167        );
12168        assert_eq!(f.run(&[b"VLINKS", b"v", b"nobody"]), "*-1\r\n");
12169        assert_eq!(f.run(&[b"VLINKS", b"nokey", b"east"]), "*-1\r\n");
12170    }
12171
12172    /// A vector arrives either as digits or as bytes, and the two have to mean
12173    /// the same thing.
12174    #[test]
12175    fn fp32_and_values_are_the_same_vector() {
12176        let mut f = Fixture::new();
12177        let mut blob = Vec::new();
12178        for x in [3.0f32, 4.0] {
12179            blob.extend_from_slice(&x.to_le_bytes());
12180        }
12181        assert_eq!(f.run(&[b"VADD", b"v", b"FP32", &blob, b"a"]), ":1\r\n");
12182        assert_eq!(f.run(&[b"VDIM", b"v"]), ":2\r\n");
12183        assert_eq!(
12184            f.run(&[b"VEMB", b"v", b"a"]),
12185            "*2\r\n$1\r\n3\r\n$1\r\n4\r\n"
12186        );
12187        // RAW is the stored form and the number that turns it back into the
12188        // client's, which is the unit vector and the length it arrived with.
12189        let raw = f.run(&[b"VEMB", b"v", b"a", b"RAW"]);
12190        assert!(raw.starts_with("*3\r\n$3\r\nf32\r\n$8\r\n"), "{raw}");
12191        assert!(raw.ends_with("$1\r\n5\r\n"), "{raw}");
12192        // A blob that is not a whole number of floats is not a vector.
12193        assert_eq!(
12194            f.run(&[b"VADD", b"w", b"FP32", b"abc", b"a"]),
12195            "-ERR invalid vector specification\r\n"
12196        );
12197        // Neither is a count that promises more than arrived.
12198        assert_eq!(
12199            f.run(&[b"VADD", b"w", b"VALUES", b"4", b"1", b"0", b"a"]),
12200            "-ERR syntax error\r\n"
12201        );
12202        assert_eq!(f.run(&[b"EXISTS", b"w"]), ":0\r\n");
12203    }
12204
12205    // ----------------------------------------------------------------- bloom
12206
12207    /// The filter a client gets when it does not describe one, and the two
12208    /// answers an add can give.
12209    #[test]
12210    fn bf_add_makes_the_filter_and_says_whether_it_was_new() {
12211        let mut f = Fixture::new();
12212        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":1\r\n");
12213        assert_eq!(f.run(&[b"BF.ADD", b"b", b"hello"]), ":0\r\n");
12214        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"hello"]), ":1\r\n");
12215        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"never"]), ":0\r\n");
12216        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":1\r\n");
12217        // The defaults are the module's configs and not anything the command
12218        // said, which is 100 entries at a hundredth and a growth of 2.
12219        assert_eq!(
12220            f.run(&[b"BF.INFO", b"b"]),
12221            "*10\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12222             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
12223             +Expansion rate\r\n:2\r\n"
12224        );
12225        assert_eq!(f.run(&[b"TYPE", b"b"]), "+MBbloom--\r\n");
12226        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"b"]), "$3\r\nraw\r\n");
12227        // A key that is not there has no filter to report on, and answers two
12228        // different ways about it depending on which command asked.
12229        assert_eq!(f.run(&[b"BF.CARD", b"gone"]), ":0\r\n");
12230        assert_eq!(f.run(&[b"BF.INFO", b"gone"]), "-ERR not found\r\n");
12231    }
12232
12233    /// `BF.EXISTS` on a key holding something else answers a miss, and
12234    /// everything else in the family answers `WRONGTYPE`.
12235    ///
12236    /// The two halves of a check and set disagree about what that key is, which
12237    /// is the module's behaviour and not a decision taken here.
12238    #[test]
12239    fn a_wrong_type_is_a_miss_to_the_two_that_only_read_bits() {
12240        let mut f = Fixture::new();
12241        f.run(&[b"SET", b"s", b"text"]);
12242        assert_eq!(f.run(&[b"BF.EXISTS", b"s", b"x"]), ":0\r\n");
12243        assert_eq!(f.run(&[b"BF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12244        for cmd in [
12245            vec![&b"BF.ADD"[..], b"s", b"x"],
12246            vec![&b"BF.MADD"[..], b"s", b"x"],
12247            vec![&b"BF.CARD"[..], b"s"],
12248            vec![&b"BF.INFO"[..], b"s"],
12249            vec![&b"BF.DEBUG"[..], b"s"],
12250            vec![&b"BF.SCANDUMP"[..], b"s", b"0"],
12251        ] {
12252            let name = String::from_utf8_lossy(cmd[0]).into_owned();
12253            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12254        }
12255        // The arguments are read before the key is, so a reserve with a bad
12256        // error rate complains about the rate and never learns about the string.
12257        assert_eq!(
12258            f.run(&[b"BF.RESERVE", b"s", b"abc", b"10"]),
12259            "-ERR bad error rate\r\n"
12260        );
12261        assert!(
12262            f.run(&[b"BF.RESERVE", b"s", b"0.01", b"10"])
12263                .starts_with("-WRONGTYPE")
12264        );
12265    }
12266
12267    /// A chain grows by its expansion factor and each link is half as wrong as
12268    /// the one before, which is what makes the whole filter hold its rate.
12269    #[test]
12270    fn a_full_filter_grows_a_link_and_a_fixed_one_says_no() {
12271        let mut f = Fixture::new();
12272        assert_eq!(f.run(&[b"BF.RESERVE", b"g", b"0.01", b"10"]), "+OK\r\n");
12273        for i in 0..10u32 {
12274            assert_eq!(
12275                f.run(&[b"BF.ADD", b"g", i.to_string().as_bytes()]),
12276                ":1\r\n"
12277            );
12278        }
12279        assert_eq!(f.run(&[b"BF.INFO", b"g", b"FILTERS"]), "*1\r\n:1\r\n");
12280        assert_eq!(f.run(&[b"BF.ADD", b"g", b"11"]), ":1\r\n");
12281        assert_eq!(f.run(&[b"BF.INFO", b"g", b"filters"]), "*1\r\n:2\r\n");
12282        // Capacity is the sum of every link and not the number that was asked
12283        // for, so it is 10 and then 10 plus 20.
12284        assert_eq!(f.run(&[b"BF.INFO", b"g", b"CAPACITY"]), "*1\r\n:30\r\n");
12285        assert_eq!(
12286            f.run(&[b"BF.DEBUG", b"g"]),
12287            "*3\r\n$7\r\nsize:11\r\n\
12288             $71\r\nbytes:16 bits:128 hashes:8 hashwidth:64 capacity:10 size:10 ratio:0.005\r\n\
12289             $71\r\nbytes:32 bits:256 hashes:9 hashwidth:64 capacity:20 size:1 ratio:0.0025\r\n"
12290        );
12291
12292        // The same filter told not to grow fills instead.
12293        assert_eq!(
12294            f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]),
12295            "+OK\r\n"
12296        );
12297        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":1\r\n");
12298        assert_eq!(f.run(&[b"BF.ADD", b"n", b"b"]), ":1\r\n");
12299        assert_eq!(
12300            f.run(&[b"BF.ADD", b"n", b"c"]),
12301            "-ERR non scaling filter is full\r\n"
12302        );
12303        // And an item that is already in it still answers, because membership
12304        // is checked before fullness.
12305        assert_eq!(f.run(&[b"BF.ADD", b"n", b"a"]), ":0\r\n");
12306        // A filter that will not grow has no expansion rate to report, in
12307        // either of the two spellings that make one.
12308        assert_eq!(f.run(&[b"BF.INFO", b"n", b"EXPANSION"]), "*1\r\n$-1\r\n");
12309        f.run(&[b"BF.RESERVE", b"z", b"0.01", b"2", b"EXPANSION", b"0"]);
12310        assert_eq!(f.run(&[b"BF.INFO", b"z", b"EXPANSION"]), "*1\r\n$-1\r\n");
12311        // Asking for both at once is refused, which is one of the module's
12312        // errors that carries no prefix at all.
12313        assert_eq!(
12314            f.run(&[
12315                b"BF.RESERVE",
12316                b"q",
12317                b"0.01",
12318                b"2",
12319                b"NONSCALING",
12320                b"EXPANSION",
12321                b"2"
12322            ]),
12323            "-Nonscaling filters cannot expand\r\n"
12324        );
12325    }
12326
12327    /// A multi add stops where the filter did, so the reply can be shorter than
12328    /// the argument list.
12329    #[test]
12330    fn madd_truncates_its_reply_at_the_item_that_did_not_fit() {
12331        let mut f = Fixture::new();
12332        f.run(&[b"BF.RESERVE", b"n", b"0.01", b"2", b"NONSCALING"]);
12333        assert_eq!(
12334            f.run(&[b"BF.MADD", b"n", b"a", b"b", b"c", b"d"]),
12335            "*3\r\n:1\r\n:1\r\n-ERR non scaling filter is full\r\n"
12336        );
12337        assert_eq!(
12338            f.run(&[b"BF.MEXISTS", b"n", b"a", b"c"]),
12339            "*2\r\n:1\r\n:0\r\n"
12340        );
12341    }
12342
12343    /// `BF.INSERT` describes a filter and fills it in one command, with its own
12344    /// spelling of every complaint.
12345    #[test]
12346    fn insert_is_a_reserve_and_a_madd_with_different_errors() {
12347        let mut f = Fixture::new();
12348        assert_eq!(
12349            f.run(&[
12350                b"BF.INSERT",
12351                b"i",
12352                b"CAPACITY",
12353                b"50",
12354                b"ERROR",
12355                b"0.001",
12356                b"ITEMS",
12357                b"a",
12358                b"b"
12359            ]),
12360            "*2\r\n:1\r\n:1\r\n"
12361        );
12362        assert_eq!(f.run(&[b"BF.INFO", b"i", b"CAPACITY"]), "*1\r\n:50\r\n");
12363        // NOCREATE is the only way to add without making the key.
12364        assert_eq!(
12365            f.run(&[b"BF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12366            "-ERR not found\r\n"
12367        );
12368        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12369        // The same mistakes as BF.RESERVE, in the sentences this command uses
12370        // for them, and one sentence where BF.RESERVE has two.
12371        assert_eq!(
12372            f.run(&[b"BF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12373            "-Bad capacity\r\n"
12374        );
12375        assert_eq!(
12376            f.run(&[b"BF.INSERT", b"i", b"ERROR", b"2", b"ITEMS", b"a"]),
12377            "-Bad error rate\r\n"
12378        );
12379        assert_eq!(
12380            f.run(&[b"BF.INSERT", b"i", b"EXPANSION", b"99999", b"ITEMS", b"a"]),
12381            "-Bad expansion\r\n"
12382        );
12383        // An option is matched on its first letter and not on the word, so a
12384        // token nobody meant as an option is one anyway if it starts with the
12385        // right letter. NOSUCHTHING is NONSCALING here, and the filter it
12386        // builds says so.
12387        assert_eq!(
12388            f.run(&[b"BF.INSERT", b"ns", b"NOSUCHTHING", b"ITEMS", b"a"]),
12389            "*1\r\n:1\r\n"
12390        );
12391        assert_eq!(f.run(&[b"BF.INFO", b"ns", b"EXPANSION"]), "*1\r\n$-1\r\n");
12392        // Only E and N need a second look, one for ERROR against EXPANSION and
12393        // the other for NOCREATE against NONSCALING, and both stop as soon as
12394        // they can tell the two apart.
12395        assert_eq!(
12396            f.run(&[b"BF.INSERT", b"e1", b"E", b"4", b"ITEMS", b"a"]),
12397            "*1\r\n:1\r\n"
12398        );
12399        assert_eq!(f.run(&[b"BF.INFO", b"e1", b"EXPANSION"]), "*1\r\n:4\r\n");
12400        assert_eq!(
12401            f.run(&[b"BF.INSERT", b"e2", b"ER", b"0.5", b"ITEMS", b"a"]),
12402            "*1\r\n:1\r\n"
12403        );
12404        assert_eq!(
12405            f.run(&[b"BF.INSERT", b"gone", b"NOC", b"ITEMS", b"a"]),
12406            "-ERR not found\r\n"
12407        );
12408        // A letter that starts nothing is the one case that is refused.
12409        assert_eq!(
12410            f.run(&[b"BF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12411            "-Unknown argument received\r\n"
12412        );
12413        // Everything after ITEMS is an item, even when it spells an option.
12414        assert_eq!(
12415            f.run(&[b"BF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12416            "*1\r\n:1\r\n"
12417        );
12418        // And ITEMS with nothing after it is the same as leaving it out.
12419        assert!(
12420            f.run(&[b"BF.INSERT", b"i", b"ITEMS"])
12421                .contains("wrong number of arguments")
12422        );
12423    }
12424
12425    /// A filter dumped a chunk at a time and put back into another key is the
12426    /// same filter.
12427    #[test]
12428    fn a_dump_replays_into_a_filter_that_answers_the_same() {
12429        let mut f = Fixture::new();
12430        f.run(&[b"BF.RESERVE", b"src", b"0.01", b"10"]);
12431        for i in 0..25u32 {
12432            f.run(&[b"BF.ADD", b"src", i.to_string().as_bytes()]);
12433        }
12434        assert_eq!(f.run(&[b"BF.INFO", b"src", b"FILTERS"]), "*1\r\n:2\r\n");
12435
12436        // Iterator zero asks for the header and every one after it is a running
12437        // byte offset, and a chunk never spans two links.
12438        let mut iter = b"0".to_vec();
12439        let mut chunks = 0;
12440        loop {
12441            let raw = f.raw(&[b"BF.SCANDUMP", b"src", &iter]);
12442            let text = String::from_utf8_lossy(&raw).into_owned();
12443            let next = text
12444                .split("\r\n")
12445                .nth(1)
12446                .and_then(|n| n.strip_prefix(':'))
12447                .expect("a two element reply of an iterator and a chunk")
12448                .to_owned();
12449            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12450            let data = &body[body
12451                .windows(2)
12452                .position(|w| w == b"\r\n")
12453                .expect("a length line")
12454                + 2..body.len() - 2];
12455            if next == "0" {
12456                assert!(data.is_empty(), "the last chunk is empty");
12457                break;
12458            }
12459            let put = f.run(&[b"BF.LOADCHUNK", b"dst", next.as_bytes(), data]);
12460            assert_eq!(put, "+OK\r\n", "loading chunk {chunks}");
12461            iter = next.into_bytes();
12462            chunks += 1;
12463        }
12464        assert_eq!(chunks, 3, "a header and one chunk per link");
12465
12466        assert_eq!(f.run(&[b"BF.INFO", b"dst"]), f.run(&[b"BF.INFO", b"src"]));
12467        assert_eq!(f.run(&[b"BF.DEBUG", b"dst"]), f.run(&[b"BF.DEBUG", b"src"]));
12468        for i in 0..25u32 {
12469            assert_eq!(
12470                f.run(&[b"BF.EXISTS", b"dst", i.to_string().as_bytes()]),
12471                ":1\r\n"
12472            );
12473        }
12474
12475        // A header on top of a filter is refused rather than merged, and so is
12476        // one that no filter wrote.
12477        assert_eq!(
12478            f.run(&[b"BF.LOADCHUNK", b"dst", b"1", b"anything"]),
12479            "-ERR received bad data\r\n"
12480        );
12481        assert_eq!(
12482            f.run(&[b"BF.LOADCHUNK", b"fresh", b"1", b"anything"]),
12483            "-ERR received bad data\r\n"
12484        );
12485        // An offset past the end of the filter names itself.
12486        assert_eq!(
12487            f.run(&[b"BF.LOADCHUNK", b"dst", b"99999", b"x"]),
12488            "-ERR invalid offset - no link found\r\n"
12489        );
12490        assert_eq!(
12491            f.run(&[b"BF.LOADCHUNK", b"dst", b"nope", b"x"]),
12492            "-ERR Second argument must be numeric\r\n"
12493        );
12494        // The same complaint without the prefix on the way out, which is the
12495        // module's inconsistency and not a slip here.
12496        assert_eq!(
12497            f.run(&[b"BF.SCANDUMP", b"src", b"nope"]),
12498            "-Second argument must be numeric\r\n"
12499        );
12500    }
12501
12502    /// The argument checks, which have a sentence each and read numbers the way
12503    /// Redis reads them everywhere else.
12504    #[test]
12505    fn reserve_reads_its_numbers_the_way_string2ll_does() {
12506        let mut f = Fixture::new();
12507        for (args, want) in [
12508            (vec![&b"abc"[..], b"10"], "-ERR bad error rate\r\n"),
12509            (vec![&b"nan"[..], b"10"], "-ERR bad error rate\r\n"),
12510            (
12511                vec![&b"0"[..], b"10"],
12512                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12513            ),
12514            (
12515                vec![&b"1"[..], b"10"],
12516                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12517            ),
12518            (
12519                vec![&b"inf"[..], b"10"],
12520                "-ERR error rate must be in the range (0.000000, 1.000000)\r\n",
12521            ),
12522            (vec![&b"0.01"[..], b"+10"], "-ERR bad capacity\r\n"),
12523            (vec![&b"0.01"[..], b"1e2"], "-ERR bad capacity\r\n"),
12524            (vec![&b"0.01"[..], b"007"], "-ERR bad capacity\r\n"),
12525            (
12526                vec![&b"0.01"[..], b"0"],
12527                "-ERR capacity must be in the range [1, 1073741824]\r\n",
12528            ),
12529            (
12530                vec![&b"0.01"[..], b"1073741825"],
12531                "-ERR capacity must be in the range [1, 1073741824]\r\n",
12532            ),
12533        ] {
12534            let mut cmd = vec![&b"BF.RESERVE"[..], b"k"];
12535            cmd.extend(args.iter().copied());
12536            assert_eq!(f.run(&cmd), want, "{}", String::from_utf8_lossy(args[0]));
12537        }
12538        assert_eq!(
12539            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION"]),
12540            "-ERR no expansion\r\n"
12541        );
12542        assert_eq!(
12543            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"abc"]),
12544            "-ERR bad expansion\r\n"
12545        );
12546        assert_eq!(
12547            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"EXPANSION", b"32769"]),
12548            "-ERR expansion must be in the range [0, 32768]\r\n"
12549        );
12550        // Trailing rubbish after the capacity is ignored rather than refused.
12551        assert_eq!(
12552            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10", b"junk"]),
12553            "+OK\r\n"
12554        );
12555        assert_eq!(
12556            f.run(&[b"BF.RESERVE", b"k", b"0.01", b"10"]),
12557            "-ERR item exists\r\n"
12558        );
12559        assert_eq!(
12560            f.run(&[b"BF.INFO", b"k", b"nosuchfield"]),
12561            "-Invalid information value\r\n"
12562        );
12563        assert!(
12564            f.run(&[b"BF.INFO", b"k", b"CAPACITY", b"SIZE"])
12565                .contains("wrong number of arguments")
12566        );
12567    }
12568
12569    /// The RESP3 shapes, which are where this family differs most from RESP2.
12570    #[test]
12571    fn the_bloom_family_answers_in_resp3_spelling_too() {
12572        let mut f = Fixture::new();
12573        f.out.set_proto(Proto::Resp3);
12574        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#t\r\n");
12575        assert_eq!(f.run(&[b"BF.ADD", b"b", b"a"]), "#f\r\n");
12576        assert_eq!(f.run(&[b"BF.MADD", b"b", b"a", b"c"]), "*2\r\n#f\r\n#t\r\n");
12577        assert_eq!(f.run(&[b"BF.EXISTS", b"b", b"a"]), "#t\r\n");
12578        assert_eq!(
12579            f.run(&[b"BF.MEXISTS", b"b", b"a", b"z"]),
12580            "*2\r\n#t\r\n#f\r\n"
12581        );
12582        // The count stays an integer, because it counts rather than answers.
12583        assert_eq!(f.run(&[b"BF.CARD", b"b"]), ":2\r\n");
12584        assert_eq!(
12585            f.run(&[b"BF.INFO", b"b"]),
12586            "%5\r\n+Capacity\r\n:100\r\n+Size\r\n:240\r\n\
12587             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:2\r\n\
12588             +Expansion rate\r\n:2\r\n"
12589        );
12590        // One field is a map of one here and a bare array of one on RESP2, so
12591        // this is the reply where the two protocols carry different facts.
12592        assert_eq!(
12593            f.run(&[b"BF.INFO", b"b", b"CAPACITY"]),
12594            "%1\r\n+Capacity\r\n:100\r\n"
12595        );
12596    }
12597
12598    // ---------------------------------------------------------------- cuckoo
12599
12600    /// A dump header, which is the four counts and the three widths a filter
12601    /// writes in front of its fingerprints.
12602    ///
12603    /// Written by hand rather than taken from a `CF.SCANDUMP`, because what the
12604    /// tests below want out of it is the states a filter cannot be put into
12605    /// from the wire.
12606    fn cf_header(
12607        items: u64,
12608        buckets: u64,
12609        deletes: u64,
12610        filters: u64,
12611        geometry: [u16; 3],
12612    ) -> Vec<u8> {
12613        let mut out = Vec::with_capacity(38);
12614        for n in [items, buckets, deletes, filters] {
12615            out.extend_from_slice(&n.to_le_bytes());
12616        }
12617        for n in geometry {
12618            out.extend_from_slice(&n.to_le_bytes());
12619        }
12620        out
12621    }
12622
12623    /// The filter a client gets when it does not describe one, and the thing a
12624    /// cuckoo filter does that a Bloom filter cannot, which is count copies and
12625    /// take them out again.
12626    #[test]
12627    fn cf_add_makes_the_filter_and_counts_the_copies() {
12628        let mut f = Fixture::new();
12629        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12630        assert_eq!(f.run(&[b"CF.ADD", b"d", b"hello"]), ":1\r\n");
12631        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":2\r\n");
12632        // The NX form is the one that looks first, which is why it is a command
12633        // of its own rather than an option.
12634        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"hello"]), ":0\r\n");
12635        assert_eq!(f.run(&[b"CF.ADDNX", b"d", b"other"]), ":1\r\n");
12636        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"hello"]), ":1\r\n");
12637        assert_eq!(f.run(&[b"CF.EXISTS", b"d", b"no"]), ":0\r\n");
12638        assert_eq!(
12639            f.run(&[b"CF.MEXISTS", b"d", b"hello", b"no"]),
12640            "*2\r\n:1\r\n:0\r\n"
12641        );
12642        // The defaults are the module's configs: 1024 entries over buckets of
12643        // two, twenty kicks and a chain that grows by one.
12644        assert_eq!(
12645            f.run(&[b"CF.INFO", b"d"]),
12646            "*16\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
12647             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:3\r\n\
12648             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:2\r\n\
12649             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12650        );
12651        assert_eq!(
12652            f.run(&[b"CF.DEBUG", b"d"]),
12653            "$79\r\nbktsize:2 buckets:512 items:3 deletes:0 filters:1 \
12654             max_iterations:20 expansion:1\r\n"
12655        );
12656        assert_eq!(f.run(&[b"TYPE", b"d"]), "+MBbloomCF\r\n");
12657        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
12658
12659        // A delete takes one copy, so the same item goes twice and then stops.
12660        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12661        assert_eq!(f.run(&[b"CF.COUNT", b"d", b"hello"]), ":1\r\n");
12662        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":1\r\n");
12663        assert_eq!(f.run(&[b"CF.DEL", b"d", b"hello"]), ":0\r\n");
12664        assert_eq!(f.run(&[b"CF.COMPACT", b"d"]), "+OK\r\n");
12665
12666        // A key with no filter under it gets three different sentences and one
12667        // plain miss, depending on which command asked.
12668        assert_eq!(f.run(&[b"CF.INFO", b"gone"]), "-ERR not found\r\n");
12669        assert_eq!(f.run(&[b"CF.DEL", b"gone", b"x"]), "-Not found\r\n");
12670        assert_eq!(
12671            f.run(&[b"CF.COMPACT", b"gone"]),
12672            "-Cuckoo filter was not found\r\n"
12673        );
12674        assert_eq!(f.run(&[b"CF.EXISTS", b"gone", b"x"]), ":0\r\n");
12675        // And `CF.COMPACT` is declared as taking any number of keys and takes
12676        // exactly one, which is the module's own arity being wrong rather than
12677        // this table's.
12678        assert!(
12679            f.run(&[b"CF.COMPACT", b"a", b"b"])
12680                .contains("wrong number of arguments")
12681        );
12682    }
12683
12684    /// The four that only read fingerprints treat a key holding something else
12685    /// as a key with no filter, and everything else answers `WRONGTYPE`.
12686    #[test]
12687    fn a_wrong_type_is_a_miss_to_the_four_that_only_read_fingerprints() {
12688        let mut f = Fixture::new();
12689        f.run(&[b"SET", b"s", b"text"]);
12690        assert_eq!(f.run(&[b"CF.EXISTS", b"s", b"x"]), ":0\r\n");
12691        assert_eq!(f.run(&[b"CF.MEXISTS", b"s", b"x"]), "*1\r\n:0\r\n");
12692        assert_eq!(f.run(&[b"CF.COUNT", b"s", b"x"]), ":0\r\n");
12693        // `CF.DEL` writes and is still in that group, and `CF.COMPACT` writes
12694        // and is declared read only, so neither of the two halves of the family
12695        // is the same set as the flags say.
12696        assert_eq!(f.run(&[b"CF.DEL", b"s", b"x"]), "-Not found\r\n");
12697        assert_eq!(
12698            f.run(&[b"CF.COMPACT", b"s"]),
12699            "-Cuckoo filter was not found\r\n"
12700        );
12701        for cmd in [
12702            vec![&b"CF.ADD"[..], b"s", b"x"],
12703            vec![&b"CF.ADDNX"[..], b"s", b"x"],
12704            vec![&b"CF.INSERT"[..], b"s", b"ITEMS", b"x"],
12705            vec![&b"CF.INSERTNX"[..], b"s", b"ITEMS", b"x"],
12706            vec![&b"CF.INFO"[..], b"s"],
12707            vec![&b"CF.DEBUG"[..], b"s"],
12708            vec![&b"CF.SCANDUMP"[..], b"s", b"0"],
12709            vec![&b"CF.LOADCHUNK"[..], b"s", b"2", b"x"],
12710            vec![&b"CF.RESERVE"[..], b"s", b"64"],
12711        ] {
12712            let name = String::from_utf8_lossy(cmd[0]).into_owned();
12713            assert!(f.run(&cmd).starts_with("-WRONGTYPE"), "{name}");
12714        }
12715    }
12716
12717    /// `CF.RESERVE` reads its options by name in an order of its own, and the
12718    /// first pair with a given name is the only one it looks at.
12719    #[test]
12720    fn reserve_complains_about_its_options_in_the_order_it_looks_for_them() {
12721        let mut f = Fixture::new();
12722        assert_eq!(
12723            f.run(&[
12724                b"CF.RESERVE",
12725                b"r",
12726                b"64",
12727                b"BUCKETSIZE",
12728                b"1",
12729                b"MAXITERATIONS",
12730                b"7",
12731                b"EXPANSION",
12732                b"4"
12733            ]),
12734            "+OK\r\n"
12735        );
12736        assert_eq!(
12737            f.run(&[b"CF.DEBUG", b"r"]),
12738            "$77\r\nbktsize:1 buckets:64 items:0 deletes:0 filters:1 \
12739             max_iterations:7 expansion:4\r\n"
12740        );
12741        assert_eq!(f.run(&[b"CF.RESERVE", b"r", b"64"]), "-ERR item exists\r\n");
12742
12743        assert_eq!(f.run(&[b"CF.RESERVE", b"q", b"abc"]), "-Bad capacity\r\n");
12744        assert_eq!(
12745            f.run(&[b"CF.RESERVE", b"q", b"1"]),
12746            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
12747        );
12748        // The range is the bucket size's and not a constant, so a capacity that
12749        // was fine at two slots a bucket is not at four.
12750        assert_eq!(
12751            f.run(&[b"CF.RESERVE", b"q", b"7", b"BUCKETSIZE", b"4"]),
12752            "-Capacity must be in the range [2 * BUCKETSIZE, 1073741824]\r\n"
12753        );
12754        assert_eq!(
12755            f.run(&[b"CF.RESERVE", b"q", b"8", b"BUCKETSIZE", b"4"]),
12756            "+OK\r\n"
12757        );
12758
12759        // The capacity is checked last, so a command that is wrong twice
12760        // answers about the option. Which option it answers about is the order
12761        // the module looks for them in and not the order they were written, so
12762        // a bad kick budget wins over a bad bucket size wherever the two sit.
12763        assert_eq!(
12764            f.run(&[b"CF.RESERVE", b"q2", b"64", b"BUCKETSIZE", b"0"]),
12765            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
12766        );
12767        assert_eq!(
12768            f.run(&[
12769                b"CF.RESERVE",
12770                b"q2",
12771                b"64",
12772                b"EXPANSION",
12773                b"xx",
12774                b"BUCKETSIZE",
12775                b"0"
12776            ]),
12777            "-BUCKETSIZE: value must be in the range [1, 255]\r\n"
12778        );
12779        assert_eq!(
12780            f.run(&[
12781                b"CF.RESERVE",
12782                b"q2",
12783                b"64",
12784                b"MAXITERATIONS",
12785                b"0",
12786                b"BUCKETSIZE",
12787                b"0"
12788            ]),
12789            "-MAXITERATIONS: value must be in the range [1, 65535]\r\n"
12790        );
12791        // A second pair with a name that has already been read is not looked at
12792        // at all, so this one is a filter with buckets of one rather than an
12793        // error about a bucket size of zero.
12794        assert_eq!(
12795            f.run(&[
12796                b"CF.RESERVE",
12797                b"q3",
12798                b"64",
12799                b"BUCKETSIZE",
12800                b"1",
12801                b"BUCKETSIZE",
12802                b"0"
12803            ]),
12804            "+OK\r\n"
12805        );
12806        // A pair nobody knows is dropped, which is the opposite of what
12807        // `CF.INSERT` does with the same mistake.
12808        assert_eq!(
12809            f.run(&[b"CF.RESERVE", b"q4", b"64", b"NOSUCH", b"9"]),
12810            "+OK\r\n"
12811        );
12812        assert_eq!(
12813            f.run(&[b"CF.DEBUG", b"q4"]),
12814            "$78\r\nbktsize:2 buckets:32 items:0 deletes:0 filters:1 \
12815             max_iterations:20 expansion:1\r\n"
12816        );
12817        // And an option with nothing after it leaves an odd number of them,
12818        // which is an arity error rather than a complaint about the option.
12819        assert!(
12820            f.run(&[b"CF.RESERVE", b"q5", b"64", b"BUCKETSIZE"])
12821                .contains("wrong number of arguments")
12822        );
12823    }
12824
12825    /// `CF.INSERT` is a reserve and a multi add, with a grammar that agrees
12826    /// with `CF.RESERVE` about nothing.
12827    #[test]
12828    fn insert_checks_every_occurrence_and_matches_on_the_first_letter() {
12829        let mut f = Fixture::new();
12830        assert_eq!(
12831            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"64", b"ITEMS", b"a", b"b"]),
12832            "*2\r\n:1\r\n:1\r\n"
12833        );
12834        assert_eq!(
12835            f.run(&[b"CF.DEBUG", b"i"]),
12836            "$78\r\nbktsize:2 buckets:32 items:2 deletes:0 filters:1 \
12837             max_iterations:20 expansion:1\r\n"
12838        );
12839        // The NX form has three answers rather than two, which is why it stays
12840        // integers on both protocols.
12841        assert_eq!(
12842            f.run(&[b"CF.INSERTNX", b"i", b"ITEMS", b"a", b"c"]),
12843            "*2\r\n:0\r\n:1\r\n"
12844        );
12845        assert_eq!(
12846            f.run(&[b"CF.INSERT", b"gone", b"NOCREATE", b"ITEMS", b"a"]),
12847            "-ERR not found\r\n"
12848        );
12849        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
12850
12851        assert_eq!(
12852            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"abc", b"ITEMS", b"a"]),
12853            "-Bad capacity\r\n"
12854        );
12855        // The bucket size cannot be given here, so the range names the config
12856        // that holds it instead of the option `CF.RESERVE` names.
12857        assert_eq!(
12858            f.run(&[b"CF.INSERT", b"i", b"CAPACITY", b"2", b"ITEMS", b"a"]),
12859            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
12860        );
12861        // Every occurrence is checked, which is where this differs from
12862        // `CF.RESERVE`: the second `CAPACITY` is an error even though the first
12863        // one is the one that would have been used.
12864        assert_eq!(
12865            f.run(&[
12866                b"CF.INSERT",
12867                b"i",
12868                b"CAPACITY",
12869                b"8",
12870                b"CAPACITY",
12871                b"2",
12872                b"ITEMS",
12873                b"a"
12874            ]),
12875            "-Capacity must be in the range [cf-bucket-size * 2, 1073741824]\r\n"
12876        );
12877        // An option is one letter and not a word, so `NOSUCH` is `NOCREATE` and
12878        // `ITEMSXYZ` is `ITEMS`, and only a letter that starts nothing is
12879        // refused.
12880        assert_eq!(
12881            f.run(&[b"CF.INSERT", b"i", b"NOSUCH", b"ITEMS", b"a"]),
12882            "*1\r\n:1\r\n"
12883        );
12884        assert_eq!(
12885            f.run(&[b"CF.INSERT", b"i", b"ITEMSXYZ", b"a"]),
12886            "*1\r\n:1\r\n"
12887        );
12888        assert_eq!(
12889            f.run(&[b"CF.INSERT", b"i", b"ZZZ", b"ITEMS", b"a"]),
12890            "-Unknown argument received\r\n"
12891        );
12892        // Everything after ITEMS is an item, even when it spells an option.
12893        assert_eq!(
12894            f.run(&[b"CF.INSERT", b"i", b"ITEMS", b"NOCREATE"]),
12895            "*1\r\n:1\r\n"
12896        );
12897        // And the two ways of sending no items at all are the same complaint.
12898        assert!(
12899            f.run(&[b"CF.INSERT", b"i", b"ITEMS"])
12900                .contains("wrong number of arguments")
12901        );
12902        assert!(
12903            f.run(&[b"CF.INSERT", b"i", b"CAPACITY"])
12904                .contains("wrong number of arguments")
12905        );
12906    }
12907
12908    /// The two walls a filter can hit, which say different things and are not
12909    /// the same wall.
12910    #[test]
12911    fn a_full_filter_and_one_that_ran_out_of_filters_answer_differently() {
12912        let mut f = Fixture::new();
12913        f.run(&[
12914            b"CF.RESERVE",
12915            b"s",
12916            b"4",
12917            b"BUCKETSIZE",
12918            b"1",
12919            b"EXPANSION",
12920            b"0",
12921        ]);
12922        for i in 0..4u32 {
12923            assert_eq!(
12924                f.run(&[b"CF.ADD", b"s", i.to_string().as_bytes()]),
12925                ":1\r\n"
12926            );
12927        }
12928        assert_eq!(f.run(&[b"CF.ADD", b"s", b"4"]), "-Filter is full\r\n");
12929        assert_eq!(f.run(&[b"CF.ADDNX", b"s", b"zz"]), "-Filter is full\r\n");
12930        // The add commands say it in a sentence and the insert commands say it
12931        // in the array, one value per item, and the array is never short.
12932        assert_eq!(
12933            f.run(&[b"CF.INSERT", b"s", b"ITEMS", b"p", b"q"]),
12934            "*2\r\n:-1\r\n:-1\r\n"
12935        );
12936        assert_eq!(
12937            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"0", b"q"]),
12938            "*2\r\n:0\r\n:-1\r\n"
12939        );
12940
12941        // A chain that is allowed to grow stops for a different reason, and the
12942        // count it stops at is the filter limit rather than the room: this one
12943        // gives up with three slots free. Loading a chain that already has
12944        // every filter it is allowed shows why, since it refuses an item
12945        // straight into an empty one.
12946        let full = cf_header(0, 4, 0, 32, [1, 20, 1]);
12947        assert_eq!(f.run(&[b"CF.LOADCHUNK", b"g", b"1", &full]), "+OK\r\n");
12948        assert_eq!(
12949            f.run(&[b"CF.ADD", b"g", b"q"]),
12950            "-Maximum expansions reached\r\n"
12951        );
12952        assert_eq!(
12953            f.run(&[b"CF.INFO", b"g"]),
12954            "*16\r\n+Size\r\n:680\r\n+Number of buckets\r\n:4\r\n\
12955             +Number of filters\r\n:32\r\n+Number of items inserted\r\n:0\r\n\
12956             +Number of items deleted\r\n:0\r\n+Bucket size\r\n:1\r\n\
12957             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
12958        );
12959    }
12960
12961    /// A filter dumped a chunk at a time and put back under another key is the
12962    /// same filter, and the headers that describe one nobody could build are
12963    /// refused on the way in.
12964    #[test]
12965    fn a_cuckoo_dump_replays_into_a_filter_that_answers_the_same() {
12966        let mut f = Fixture::new();
12967        f.run(&[
12968            b"CF.RESERVE",
12969            b"src",
12970            b"8",
12971            b"BUCKETSIZE",
12972            b"2",
12973            b"EXPANSION",
12974            b"2",
12975        ]);
12976        for i in 0..40u32 {
12977            f.run(&[b"CF.ADD", b"src", i.to_string().as_bytes()]);
12978        }
12979        // Position zero asks for the header and every one after it is a byte
12980        // offset across every filter laid end to end, and the walk ends on a
12981        // zero and a nil rather than an empty chunk.
12982        let mut pos = b"0".to_vec();
12983        let mut chunks = 0;
12984        loop {
12985            let raw = f.raw(&[b"CF.SCANDUMP", b"src", &pos]);
12986            let head = String::from_utf8_lossy(&raw[..raw.len().min(24)]).into_owned();
12987            let next = head
12988                .split("\r\n")
12989                .nth(1)
12990                .and_then(|n| n.strip_prefix(':'))
12991                .expect("a two element reply of a position and a chunk")
12992                .to_owned();
12993            if next == "0" {
12994                assert!(raw.ends_with(b"$-1\r\n"), "the walk ends on a nil");
12995                break;
12996            }
12997            let body = &raw[raw.iter().position(|&b| b == b'$').expect("a bulk chunk")..];
12998            let at = body
12999                .windows(2)
13000                .position(|w| w == b"\r\n")
13001                .expect("a length line")
13002                + 2;
13003            let data = &body[at..body.len() - 2];
13004            assert_eq!(
13005                f.run(&[b"CF.LOADCHUNK", b"dst", next.as_bytes(), data]),
13006                "+OK\r\n",
13007                "loading chunk {chunks}"
13008            );
13009            pos = next.into_bytes();
13010            chunks += 1;
13011        }
13012        assert!(chunks >= 2, "a header and at least one chunk");
13013
13014        assert_eq!(f.run(&[b"CF.INFO", b"dst"]), f.run(&[b"CF.INFO", b"src"]));
13015        assert_eq!(f.run(&[b"CF.DEBUG", b"dst"]), f.run(&[b"CF.DEBUG", b"src"]));
13016        for i in 0..40u32 {
13017            assert_eq!(
13018                f.run(&[b"CF.EXISTS", b"dst", i.to_string().as_bytes()]),
13019                ":1\r\n"
13020            );
13021        }
13022
13023        // A filter with nothing in it hands out no header at all, so a client
13024        // that dumps one has nothing to load back.
13025        f.run(&[b"CF.RESERVE", b"empty", b"4", b"BUCKETSIZE", b"1"]);
13026        assert_eq!(
13027            f.run(&[b"CF.SCANDUMP", b"empty", b"0"]),
13028            "*2\r\n:0\r\n$-1\r\n"
13029        );
13030
13031        // The positions this end will not take, which are not the same set at
13032        // both ends: a dump refuses a negative one and a load takes it as an
13033        // offset and fails to find anything there.
13034        assert_eq!(
13035            f.run(&[b"CF.SCANDUMP", b"src", b"nope"]),
13036            "-Invalid position\r\n"
13037        );
13038        assert_eq!(
13039            f.run(&[b"CF.SCANDUMP", b"src", b"-1"]),
13040            "-Invalid position\r\n"
13041        );
13042        assert_eq!(
13043            f.run(&[b"CF.LOADCHUNK", b"dst", b"0", b"x"]),
13044            "-Invalid position\r\n"
13045        );
13046        assert_eq!(
13047            f.run(&[b"CF.LOADCHUNK", b"dst", b"99999", b"x"]),
13048            "-Couldn't load chunk!\r\n"
13049        );
13050        // A header on top of a filter is refused rather than merged.
13051        let good = cf_header(0, 8, 0, 1, [2, 20, 1]);
13052        assert_eq!(
13053            f.run(&[b"CF.LOADCHUNK", b"dst", b"1", &good]),
13054            "-ERR item exists\r\n"
13055        );
13056        // A chunk that is not the size of a header where a header should have
13057        // been is one sentence, and one that is the size of a header and
13058        // describes a filter nobody could build is another.
13059        assert_eq!(
13060            f.run(&[b"CF.LOADCHUNK", b"n1", b"1", b"short"]),
13061            "-Invalid header\r\n"
13062        );
13063        for (why, bad) in [
13064            ("no filters at all", cf_header(0, 8, 0, 0, [2, 20, 1])),
13065            ("no buckets", cf_header(0, 0, 0, 1, [2, 20, 1])),
13066            (
13067                "a bucket count that is not a power of two",
13068                cf_header(0, 3, 0, 1, [2, 20, 1]),
13069            ),
13070            ("an empty bucket", cf_header(0, 8, 0, 1, [0, 20, 1])),
13071            ("no kicks", cf_header(0, 8, 0, 1, [2, 0, 1])),
13072            (
13073                "a growth nobody could reach",
13074                cf_header(0, 8, 0, 1, [2, 20, 32769]),
13075            ),
13076            (
13077                "a chain that cannot grow and did",
13078                cf_header(0, 8, 0, 2, [2, 20, 0]),
13079            ),
13080            // The count is written in eight bytes and read into two, so a
13081            // number that is a multiple of the second arrives as none.
13082            (
13083                "a filter count that wraps",
13084                cf_header(0, 8, 0, 65_536, [2, 20, 1]),
13085            ),
13086        ] {
13087            assert_eq!(
13088                f.run(&[b"CF.LOADCHUNK", b"bad", b"1", &bad]),
13089                "-Couldn't create filter!\r\n",
13090                "{why}"
13091            );
13092        }
13093    }
13094
13095    /// The RESP3 shapes, which are where this family differs most from RESP2
13096    /// and where one of its answers stops being readable.
13097    #[test]
13098    fn the_cuckoo_family_answers_in_resp3_spelling_too() {
13099        let mut f = Fixture::new();
13100        f.out.set_proto(Proto::Resp3);
13101        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13102        assert_eq!(f.run(&[b"CF.ADD", b"c", b"a"]), "#t\r\n");
13103        assert_eq!(f.run(&[b"CF.ADDNX", b"c", b"a"]), "#f\r\n");
13104        assert_eq!(f.run(&[b"CF.EXISTS", b"c", b"a"]), "#t\r\n");
13105        assert_eq!(
13106            f.run(&[b"CF.MEXISTS", b"c", b"a", b"z"]),
13107            "*2\r\n#t\r\n#f\r\n"
13108        );
13109        assert_eq!(f.run(&[b"CF.DEL", b"c", b"a"]), "#t\r\n");
13110        assert_eq!(f.run(&[b"CF.DEL", b"c", b"z"]), "#f\r\n");
13111        // The count stays an integer, because it counts rather than answers.
13112        assert_eq!(f.run(&[b"CF.COUNT", b"c", b"a"]), ":1\r\n");
13113        assert_eq!(
13114            f.run(&[b"CF.INFO", b"c"]),
13115            "%8\r\n+Size\r\n:1080\r\n+Number of buckets\r\n:512\r\n\
13116             +Number of filters\r\n:1\r\n+Number of items inserted\r\n:1\r\n\
13117             +Number of items deleted\r\n:1\r\n+Bucket size\r\n:2\r\n\
13118             +Expansion rate\r\n:1\r\n+Max iterations\r\n:20\r\n"
13119        );
13120
13121        // `CF.INSERT` writes a boolean per item here and an integer per item on
13122        // RESP2, and minus one has nowhere to go in a boolean, so a RESP3
13123        // client cannot tell an item that did not fit from one that is already
13124        // there. `CF.INSERTNX` keeps its integers for exactly that reason.
13125        f.run(&[
13126            b"CF.RESERVE",
13127            b"s",
13128            b"4",
13129            b"BUCKETSIZE",
13130            b"1",
13131            b"EXPANSION",
13132            b"0",
13133        ]);
13134        assert_eq!(
13135            f.run(&[
13136                b"CF.INSERT",
13137                b"s",
13138                b"ITEMS",
13139                b"a",
13140                b"b",
13141                b"c",
13142                b"d",
13143                b"e",
13144                b"f"
13145            ]),
13146            "*6\r\n#t\r\n#t\r\n#t\r\n#f\r\n#f\r\n#f\r\n"
13147        );
13148        assert_eq!(
13149            f.run(&[b"CF.INSERTNX", b"s", b"ITEMS", b"a", b"zz"]),
13150            "*2\r\n:0\r\n:-1\r\n"
13151        );
13152        assert_eq!(f.run(&[b"CF.ADD", b"s", b"zzz"]), "-Filter is full\r\n");
13153        // The end of a dump is a nil and not an empty chunk, which is one
13154        // underscore here and a negative length on RESP2.
13155        assert_eq!(f.run(&[b"CF.SCANDUMP", b"c", b"9999"]), "*2\r\n:0\r\n_\r\n");
13156    }
13157
13158    // ------------------------------------------------------------------- cms
13159
13160    /// A sketch is made from either end, and both constructors look at the key
13161    /// before they look at their arguments.
13162    #[test]
13163    fn a_sketch_is_made_from_a_size_or_from_an_error_rate() {
13164        let mut f = Fixture::new();
13165        assert_eq!(f.run(&[b"CMS.INITBYDIM", b"d", b"100", b"5"]), "+OK\r\n");
13166        assert_eq!(
13167            f.run(&[b"CMS.INFO", b"d"]),
13168            "*6\r\n+width\r\n:100\r\n+depth\r\n:5\r\n+count\r\n:0\r\n"
13169        );
13170        assert_eq!(f.run(&[b"TYPE", b"d"]), "+CMSk-TYPE\r\n");
13171        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"d"]), "$3\r\nraw\r\n");
13172        // Two over the error rounded up, and the log of the probability over the
13173        // log of a half rounded up, which for these two is 200 by 6.
13174        assert_eq!(
13175            f.run(&[b"CMS.INITBYPROB", b"p", b"0.01", b"0.03"]),
13176            "+OK\r\n"
13177        );
13178        assert_eq!(
13179            f.run(&[b"CMS.INFO", b"p"]),
13180            "*6\r\n+width\r\n:200\r\n+depth\r\n:6\r\n+count\r\n:0\r\n"
13181        );
13182        // The key is checked first, so a width of zero at a key that is already
13183        // there is about the key and not about the width.
13184        assert_eq!(
13185            f.run(&[b"CMS.INITBYDIM", b"d", b"0", b"2"]),
13186            "-CMS: key already exists\r\n"
13187        );
13188        assert_eq!(
13189            f.run(&[b"CMS.INITBYDIM", b"new", b"0", b"2"]),
13190            "-CMS: invalid width\r\n"
13191        );
13192        assert_eq!(
13193            f.run(&[b"CMS.INITBYDIM", b"new", b"2", b"0"]),
13194            "-CMS: invalid depth\r\n"
13195        );
13196        assert_eq!(
13197            f.run(&[b"CMS.INITBYPROB", b"new", b"0", b"0.5"]),
13198            "-CMS: invalid overestimation value\r\n"
13199        );
13200        assert_eq!(
13201            f.run(&[b"CMS.INITBYPROB", b"new", b"0.1", b"1"]),
13202            "-CMS: invalid prob value\r\n"
13203        );
13204        // A probability whose float conversion is zero has no depth, and a width
13205        // past a signed sixty four bit integer has no width, and both are the
13206        // same sentence.
13207        assert_eq!(
13208            f.run(&[b"CMS.INITBYPROB", b"new", b"0.5", b"1e-46"]),
13209            "-CMS: invalid init arguments\r\n"
13210        );
13211        // And a sketch bigger than a gibibyte of counters is refused here where
13212        // the reference reserves address space nobody has touched, which is
13213        // D-47.
13214        assert_eq!(
13215            f.run(&[b"CMS.INITBYDIM", b"new", b"268435457", b"1"]),
13216            "-CMS: Insufficient memory to create the key\r\n"
13217        );
13218        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
13219    }
13220
13221    /// Every pair is parsed before any of them lands, the counters saturate,
13222    /// and the count is a signed total of what was asked for.
13223    #[test]
13224    fn increments_are_parsed_whole_and_the_counters_saturate() {
13225        let mut f = Fixture::new();
13226        f.run(&[b"CMS.INITBYDIM", b"c", b"100", b"4"]);
13227        assert_eq!(
13228            f.run(&[b"CMS.INCRBY", b"c", b"a", b"3", b"b", b"4"]),
13229            "*2\r\n:3\r\n:4\r\n"
13230        );
13231        // An item that is incremented twice in one call sees its own first
13232        // increment in the reply to the second.
13233        assert_eq!(
13234            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"a", b"1"]),
13235            "*2\r\n:4\r\n:5\r\n"
13236        );
13237        // A bad number anywhere means nothing at all is applied.
13238        assert_eq!(
13239            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"x"]),
13240            "-CMS: Cannot parse number\r\n"
13241        );
13242        assert_eq!(
13243            f.run(&[b"CMS.INCRBY", b"c", b"a", b"9", b"b", b"-1"]),
13244            "-CMS: Number cannot be negative\r\n"
13245        );
13246        assert_eq!(
13247            f.run(&[b"CMS.QUERY", b"c", b"a", b"b"]),
13248            "*2\r\n:5\r\n:4\r\n"
13249        );
13250        // The counters stop at four billion and the item that stopped says so in
13251        // its own slot while the one beside it answers a number.
13252        f.run(&[b"CMS.INCRBY", b"c", b"a", b"4294967295"]);
13253        assert_eq!(
13254            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b", b"1"]),
13255            "*2\r\n-CMS: INCRBY overflow\r\n:5\r\n"
13256        );
13257        assert_eq!(f.run(&[b"CMS.QUERY", b"c", b"a"]), "*1\r\n:4294967295\r\n");
13258        // The count is what was asked for rather than what landed, and it is
13259        // signed, so a big enough total comes back negative.
13260        f.run(&[b"CMS.INITBYDIM", b"w", b"4", b"1"]);
13261        f.run(&[b"CMS.INCRBY", b"w", b"x", b"9223372036854775807"]);
13262        f.run(&[b"CMS.INCRBY", b"w", b"x", b"1"]);
13263        assert_eq!(
13264            f.run(&[b"CMS.INFO", b"w"]),
13265            "*6\r\n+width\r\n:4\r\n+depth\r\n:1\r\n+count\r\n:-9223372036854775808\r\n"
13266        );
13267        // An odd number of arguments after the key is an arity error and not a
13268        // syntax one.
13269        assert!(
13270            f.run(&[b"CMS.INCRBY", b"c", b"a", b"1", b"b"])
13271                .contains("wrong number of arguments")
13272        );
13273        assert_eq!(
13274            f.run(&[b"CMS.INCRBY", b"nope", b"a", b"1"]),
13275            "-CMS: key does not exist\r\n"
13276        );
13277        assert_eq!(
13278            f.run(&[b"CMS.QUERY", b"nope", b"a"]),
13279            "-CMS: key does not exist\r\n"
13280        );
13281    }
13282
13283    /// A merge overwrites its destination, and it is worked out in full before
13284    /// any of it is written.
13285    #[test]
13286    fn a_merge_lands_whole_or_not_at_all() {
13287        let mut f = Fixture::new();
13288        for name in [&b"m1"[..], b"m2", b"dst"] {
13289            f.run(&[b"CMS.INITBYDIM", name, b"64", b"3"]);
13290        }
13291        f.run(&[b"CMS.INCRBY", b"m1", b"a", b"5"]);
13292        f.run(&[b"CMS.INCRBY", b"m2", b"a", b"7"]);
13293        assert_eq!(
13294            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13295            "+OK\r\n"
13296        );
13297        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13298        // Overwritten and not added to, so the same merge twice is the same
13299        // answer twice.
13300        assert_eq!(
13301            f.run(&[b"CMS.MERGE", b"dst", b"2", b"m1", b"m2"]),
13302            "+OK\r\n"
13303        );
13304        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:12\r\n");
13305        assert_eq!(
13306            f.run(&[
13307                b"CMS.MERGE",
13308                b"dst",
13309                b"2",
13310                b"m1",
13311                b"m2",
13312                b"WEIGHTS",
13313                b"2",
13314                b"3"
13315            ]),
13316            "+OK\r\n"
13317        );
13318        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13319        // A cell times a weight is checked wide rather than wrapped, so this is
13320        // a refusal and the destination is left exactly as it was.
13321        assert_eq!(
13322            f.run(&[
13323                b"CMS.MERGE",
13324                b"dst",
13325                b"1",
13326                b"m1",
13327                b"WEIGHTS",
13328                b"4611686018427387904"
13329            ]),
13330            "-CMS: MERGE overflow\r\n"
13331        );
13332        assert_eq!(f.run(&[b"CMS.QUERY", b"dst", b"a"]), "*1\r\n:31\r\n");
13333        // The destination comes first, then the count, then the layout, then the
13334        // weights, then the sources one at a time.
13335        f.run(&[b"CMS.INITBYDIM", b"wide", b"128", b"3"]);
13336        assert_eq!(
13337            f.run(&[b"CMS.MERGE", b"gone", b"1", b"m1"]),
13338            "-CMS: key does not exist\r\n"
13339        );
13340        assert_eq!(
13341            f.run(&[b"CMS.MERGE", b"dst", b"0", b"m1"]),
13342            "-CMS: Number of keys must be positive\r\n"
13343        );
13344        assert_eq!(
13345            f.run(&[b"CMS.MERGE", b"dst", b"3", b"m1"]),
13346            "-CMS: wrong number of keys\r\n"
13347        );
13348        assert_eq!(
13349            f.run(&[b"CMS.MERGE", b"dst", b"1", b"m1", b"WEIGHTS", b"1", b"2"]),
13350            "-CMS: wrong number of keys/weights\r\n"
13351        );
13352        assert_eq!(
13353            f.run(&[b"CMS.MERGE", b"dst", b"1", b"wide"]),
13354            "-CMS: width/depth is not equal\r\n"
13355        );
13356        assert_eq!(
13357            f.run(&[b"CMS.MERGE", b"dst", b"1", b"gone"]),
13358            "-CMS: key does not exist\r\n"
13359        );
13360    }
13361
13362    /// A key holding anything else is `WRONGTYPE` to all six, and a key holding
13363    /// a sketch is refused by the two commands that would have to serialise it.
13364    #[test]
13365    fn a_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13366        let mut f = Fixture::new();
13367        f.run(&[b"SET", b"s", b"text"]);
13368        for cmd in [
13369            vec![&b"CMS.INITBYDIM"[..], b"s", b"8", b"2"],
13370            vec![&b"CMS.INCRBY"[..], b"s", b"a", b"1"],
13371            vec![&b"CMS.QUERY"[..], b"s", b"a"],
13372            vec![&b"CMS.INFO"[..], b"s"],
13373            vec![&b"CMS.MERGE"[..], b"s", b"1", b"s"],
13374        ] {
13375            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13376            let reply = f.run(&cmd);
13377            // The two constructors see the key before anything else and say so
13378            // in the module's own words, and the rest are `WRONGTYPE`.
13379            assert!(
13380                reply.starts_with("-WRONGTYPE") || reply == "-CMS: key already exists\r\n",
13381                "{name}: {reply}"
13382            );
13383        }
13384        f.run(&[b"CMS.INITBYDIM", b"c", b"64", b"2"]);
13385        // Redis refuses to copy a module key that has no copy callback, and
13386        // these are its words rather than ours. `DUMP` is the other half of
13387        // D-48: the reference has a payload for one of these and we do not.
13388        assert_eq!(
13389            f.run(&[b"COPY", b"c", b"c2"]),
13390            "-ERR not supported for this module key\r\n"
13391        );
13392        assert_eq!(
13393            f.run(&[b"DUMP", b"c"]),
13394            "-ERR DUMP is not supported for this module key\r\n"
13395        );
13396        // A graph is nobody's module and keeps its own sentence.
13397        f.run(&[b"G.NADD", b"g", b"a"]);
13398        assert_eq!(
13399            f.run(&[b"COPY", b"g", b"g2"]),
13400            "-ERR COPY is not supported for a graph\r\n"
13401        );
13402        assert_eq!(
13403            f.run(&[b"DUMP", b"g"]),
13404            "-ERR DUMP is not supported for a graph\r\n"
13405        );
13406        // Everything that does not need a byte shape works on a sketch key the
13407        // way it works on any other.
13408        assert_eq!(f.run(&[b"EXPIRE", b"c", b"100"]), ":1\r\n");
13409        assert_eq!(f.run(&[b"PERSIST", b"c"]), ":1\r\n");
13410        assert_eq!(f.run(&[b"RENAME", b"c", b"c3"]), "+OK\r\n");
13411        assert_eq!(f.run(&[b"TYPE", b"c3"]), "+CMSk-TYPE\r\n");
13412        assert_eq!(f.run(&[b"DEL", b"c3"]), ":1\r\n");
13413    }
13414
13415    // ------------------------------------------------------------------ topk
13416
13417    /// `TOPK.RESERVE` takes three arguments or six, and looks at the key before
13418    /// it looks at any of them.
13419    #[test]
13420    fn a_reserve_takes_three_arguments_or_six() {
13421        let mut f = Fixture::new();
13422        assert_eq!(f.run(&[b"TOPK.RESERVE", b"t", b"5"]), "+OK\r\n");
13423        assert_eq!(
13424            f.run(&[b"TOPK.INFO", b"t"]),
13425            "*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"
13426        );
13427        // Four arguments and five are an arity error rather than a defaulted
13428        // depth or decay.
13429        for cmd in [
13430            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8"],
13431            vec![&b"TOPK.RESERVE"[..], b"u", b"5", b"8", b"7"],
13432        ] {
13433            assert!(f.run(&cmd).contains("wrong number of arguments"));
13434        }
13435        assert_eq!(
13436            f.run(&[b"TOPK.RESERVE", b"u", b"5", b"8", b"7", b"0.5"]),
13437            "+OK\r\n"
13438        );
13439        // The key is checked first, so a reserve with nothing else right at a
13440        // key that is taken still says the key is taken.
13441        assert_eq!(
13442            f.run(&[b"TOPK.RESERVE", b"u", b"0", b"0", b"0", b"9"]),
13443            "-TopK: key already exists\r\n"
13444        );
13445        assert_eq!(
13446            f.run(&[b"TOPK.RESERVE", b"v", b"0"]),
13447            "-TopK: invalid k\r\n"
13448        );
13449        assert_eq!(
13450            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"0", b"7", b"0.9"]),
13451            "-TopK: invalid width\r\n"
13452        );
13453        assert_eq!(
13454            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"x", b"0.9"]),
13455            "-TopK: invalid depth\r\n"
13456        );
13457        // Zero is out and one is in, which is the module's `> 0` and `<= 1`.
13458        assert_eq!(
13459            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"0"]),
13460            "-TopK: invalid decay value. must be '<= 1' & '> 0'\r\n"
13461        );
13462        assert_eq!(
13463            f.run(&[b"TOPK.RESERVE", b"v", b"1", b"8", b"7", b"1"]),
13464            "+OK\r\n"
13465        );
13466        // Past the cap, with the one sentence in the family that has a prefix.
13467        assert_eq!(
13468            f.run(&[
13469                b"TOPK.RESERVE",
13470                b"w",
13471                b"1",
13472                b"4294967295",
13473                b"4294967295",
13474                b"0.9"
13475            ]),
13476            "-ERR Insufficient memory to create topk data structure\r\n"
13477        );
13478    }
13479
13480    /// What the sketch keeps, and the three ways of asking about it.
13481    #[test]
13482    fn the_kept_set_is_what_query_and_list_answer_from() {
13483        let mut f = Fixture::new();
13484        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"1000", b"5", b"0.9"]);
13485        // A null an item while there is room, then the name of whatever was
13486        // pushed out.
13487        assert_eq!(
13488            f.run(&[b"TOPK.ADD", b"t", b"a", b"b"]),
13489            "*2\r\n$-1\r\n$-1\r\n"
13490        );
13491        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"a", b"10"]), "*1\r\n$-1\r\n");
13492        // Two slots are full and `c` arrives with a count of one, which is not
13493        // under the smallest kept count, so it takes that slot straight away.
13494        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"c"]), "*1\r\n$1\r\nb\r\n");
13495        assert_eq!(f.run(&[b"TOPK.INCRBY", b"t", b"c", b"5"]), "*1\r\n$-1\r\n");
13496        assert_eq!(
13497            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b", b"c"]),
13498            "*3\r\n:1\r\n:0\r\n:1\r\n"
13499        );
13500        // The table still counts what the kept set let go of.
13501        assert_eq!(
13502            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13503            "*3\r\n:11\r\n:1\r\n:6\r\n"
13504        );
13505        assert_eq!(f.run(&[b"TOPK.LIST", b"t"]), "*2\r\n$1\r\na\r\n$1\r\nc\r\n");
13506        assert_eq!(
13507            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"]),
13508            "*4\r\n$1\r\na\r\n:11\r\n$1\r\nc\r\n:6\r\n"
13509        );
13510        // Any prefix of the keyword turns the counts on, the empty string
13511        // included, and only a longer word or a different one is refused.
13512        assert_eq!(
13513            f.run(&[b"TOPK.LIST", b"t", b"w"]),
13514            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13515        );
13516        assert_eq!(
13517            f.run(&[b"TOPK.LIST", b"t", b""]),
13518            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNT"])
13519        );
13520        assert_eq!(
13521            f.run(&[b"TOPK.LIST", b"t", b"WITHCOUNTS"]),
13522            "-WITHCOUNT keyword expected\r\n"
13523        );
13524        // And the keyword is looked at before the key, so a missing key with a
13525        // bad keyword complains about the keyword.
13526        assert_eq!(
13527            f.run(&[b"TOPK.LIST", b"missing", b"nope"]),
13528            "-WITHCOUNT keyword expected\r\n"
13529        );
13530        assert_eq!(
13531            f.run(&[b"TOPK.LIST", b"missing"]),
13532            "-TopK: key does not exist\r\n"
13533        );
13534        // An item counted zero times is kept and not listed.
13535        f.run(&[b"TOPK.RESERVE", b"z", b"3"]);
13536        assert_eq!(
13537            f.run(&[b"TOPK.INCRBY", b"z", b"nothing", b"0"]),
13538            "*1\r\n$-1\r\n"
13539        );
13540        assert_eq!(f.run(&[b"TOPK.QUERY", b"z", b"nothing"]), "*1\r\n:1\r\n");
13541        assert_eq!(f.run(&[b"TOPK.LIST", b"z"]), "*0\r\n");
13542    }
13543
13544    /// `TOPK.INCRBY` applies as it goes, so a bad increment leaves everything
13545    /// before it counted, and the reply counts what it wrote.
13546    #[test]
13547    fn an_increment_is_applied_as_it_goes_and_stops_at_a_bad_one() {
13548        let mut f = Fixture::new();
13549        f.run(&[b"TOPK.RESERVE", b"t", b"5", b"1000", b"5", b"0.9"]);
13550        // Three pairs, the middle one bad: two elements come back, one of them
13551        // the error, and the array header says two rather than three. That last
13552        // part is D-51 and it is why a client here stays in step.
13553        assert_eq!(
13554            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"3", b"b", b"-1", b"c", b"4"]),
13555            format!(
13556                "*2\r\n$-1\r\n-{}\r\n",
13557                "TopK: increment must be an integer greater or equal to 0                            and smaller or equal to 100,000"
13558            )
13559        );
13560        assert_eq!(
13561            f.run(&[b"TOPK.COUNT", b"t", b"a", b"b", b"c"]),
13562            "*3\r\n:3\r\n:0\r\n:0\r\n"
13563        );
13564        // A hundred thousand is in and one more is out.
13565        assert_eq!(
13566            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100000"]),
13567            "*1\r\n$-1\r\n"
13568        );
13569        assert!(
13570            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"100001"])
13571                .contains("smaller or equal to 100,000")
13572        );
13573        // Pairs have to be pairs.
13574        assert!(
13575            f.run(&[b"TOPK.INCRBY", b"t", b"a", b"1", b"b"])
13576                .contains("wrong number of arguments")
13577        );
13578        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:100003\r\n");
13579    }
13580
13581    /// The RESP3 shapes, which are the two the protocols disagree about.
13582    #[test]
13583    fn a_query_is_a_bool_and_info_is_a_map_on_resp3() {
13584        let mut f = Fixture::new();
13585        f.run(&[b"HELLO", b"3"]);
13586        f.run(&[b"TOPK.RESERVE", b"t", b"2", b"8", b"7", b"0.5"]);
13587        f.run(&[b"TOPK.ADD", b"t", b"a"]);
13588        assert_eq!(
13589            f.run(&[b"TOPK.QUERY", b"t", b"a", b"b"]),
13590            "*2\r\n#t\r\n#f\r\n"
13591        );
13592        // The count stays an integer on both protocols.
13593        assert_eq!(f.run(&[b"TOPK.COUNT", b"t", b"a"]), "*1\r\n:1\r\n");
13594        assert_eq!(
13595            f.run(&[b"TOPK.INFO", b"t"]),
13596            "%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"
13597        );
13598        assert_eq!(f.run(&[b"TOPK.ADD", b"t", b"a"]), "*1\r\n_\r\n");
13599    }
13600
13601    /// A top k key answers the module sentences the other sketch families
13602    /// answer, and its own word for its type.
13603    #[test]
13604    fn a_top_k_sketch_is_a_module_key_to_the_rest_of_the_keyspace() {
13605        let mut f = Fixture::new();
13606        f.run(&[b"SET", b"s", b"text"]);
13607        for cmd in [
13608            vec![&b"TOPK.RESERVE"[..], b"s", b"5"],
13609            vec![&b"TOPK.ADD"[..], b"s", b"a"],
13610            vec![&b"TOPK.INCRBY"[..], b"s", b"a", b"1"],
13611            vec![&b"TOPK.QUERY"[..], b"s", b"a"],
13612            vec![&b"TOPK.COUNT"[..], b"s", b"a"],
13613            vec![&b"TOPK.LIST"[..], b"s"],
13614            vec![&b"TOPK.INFO"[..], b"s"],
13615        ] {
13616            let name = String::from_utf8_lossy(cmd[0]).into_owned();
13617            let reply = f.run(&cmd);
13618            assert!(
13619                reply.starts_with("-WRONGTYPE") || reply == "-TopK: key already exists\r\n",
13620                "{name}: {reply}"
13621            );
13622        }
13623        f.run(&[b"TOPK.RESERVE", b"t", b"5"]);
13624        assert_eq!(
13625            f.run(&[b"COPY", b"t", b"t2"]),
13626            "-ERR not supported for this module key\r\n"
13627        );
13628        assert_eq!(
13629            f.run(&[b"DUMP", b"t"]),
13630            "-ERR DUMP is not supported for this module key\r\n"
13631        );
13632        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
13633        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
13634        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
13635        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TopK-TYPE\r\n");
13636        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
13637        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
13638        // Every one of the six that is not the constructor says the same thing
13639        // about a key that is not there.
13640        assert_eq!(
13641            f.run(&[b"TOPK.INFO", b"t3"]),
13642            "-TopK: key does not exist\r\n"
13643        );
13644    }
13645
13646    // --------------------------------------------------------------- tdigest
13647
13648    /// `TDIGEST.CREATE` takes two arguments or four, and the keyword search is a
13649    /// search rather than a lookup.
13650    #[test]
13651    fn a_create_takes_two_arguments_or_four_and_reads_the_last_one() {
13652        let mut f = Fixture::new();
13653        assert_eq!(f.run(&[b"TDIGEST.CREATE", b"t"]), "+OK\r\n");
13654        // A hundred is the default and the capacity is six times it plus ten.
13655        assert_eq!(
13656            f.run(&[b"TDIGEST.INFO", b"t"]),
13657            "*18\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:0\r\n\
13658             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:0\r\n+Unmerged weight\r\n:0\r\n\
13659             +Observations\r\n:0\r\n+Total compressions\r\n:0\r\n+Memory usage\r\n:9840\r\n"
13660        );
13661        assert_eq!(
13662            f.run(&[b"TDIGEST.CREATE", b"t"]),
13663            "-ERR T-Digest: key already exists\r\n"
13664        );
13665        // Three arguments is an arity error and not a missing keyword.
13666        assert!(
13667            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION"])
13668                .contains("wrong number of arguments")
13669        );
13670        assert_eq!(
13671            f.run(&[b"TDIGEST.CREATE", b"u", b"COMPRESSION", b"1000"]),
13672            "+OK\r\n"
13673        );
13674        assert_eq!(
13675            f.run(&[b"TDIGEST.CREATE", b"v", b"compression", b"1"]),
13676            "+OK\r\n"
13677        );
13678        // The word is looked for across both trailing arguments and the number
13679        // is then read out of the last one whatever was found, so this looks for
13680        // a number inside the word `COMPRESSION` and does not find one.
13681        assert_eq!(
13682            f.run(&[b"TDIGEST.CREATE", b"w", b"100", b"COMPRESSION"]),
13683            "-ERR T-Digest: error parsing compression parameter\r\n"
13684        );
13685        assert_eq!(
13686            f.run(&[b"TDIGEST.CREATE", b"w", b"NOPE", b"100"]),
13687            "-ERR T-Digest: wrong keyword\r\n"
13688        );
13689        assert_eq!(
13690            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"1.5"]),
13691            "-ERR T-Digest: error parsing compression parameter\r\n"
13692        );
13693        assert_eq!(
13694            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"0"]),
13695            "-ERR T-Digest: compression parameter needs to be a positive integer\r\n"
13696        );
13697        // The reference's own ceiling, which is where the capacity stops fitting
13698        // in an int, and one past it.
13699        assert_eq!(
13700            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"357913942"]),
13701            "-ERR T-Digest: allocation failed\r\n"
13702        );
13703        // And ours, which is a gibibyte of centroids and is D-52.
13704        assert_eq!(
13705            f.run(&[b"TDIGEST.CREATE", b"w", b"COMPRESSION", b"100000000"]),
13706            "-ERR T-Digest: allocation failed\r\n"
13707        );
13708        // The key is checked before the arguments, so a bad compression at a key
13709        // that is already a digest still says the key is taken.
13710        assert_eq!(
13711            f.run(&[b"TDIGEST.CREATE", b"t", b"COMPRESSION", b"0"]),
13712            "-ERR T-Digest: key already exists\r\n"
13713        );
13714    }
13715
13716    /// The four samples every note about this family is written against, and the
13717    /// answers a real 8.10.1 gives for them.
13718    #[test]
13719    fn the_quantile_family_answers_what_the_module_answers() {
13720        let mut f = Fixture::new();
13721        f.run(&[b"TDIGEST.CREATE", b"s"]);
13722        assert_eq!(
13723            f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]),
13724            "+OK\r\n"
13725        );
13726        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), "$1\r\n1\r\n");
13727        assert_eq!(f.run(&[b"TDIGEST.MAX", b"s"]), "$1\r\n4\r\n");
13728        // The cdf of a sample is the weight below it plus half its own.
13729        assert_eq!(
13730            f.run(&[b"TDIGEST.CDF", b"s", b"1", b"2", b"3", b"4"]),
13731            "*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"
13732        );
13733        assert_eq!(
13734            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"0.5", b"1"]),
13735            "*3\r\n$1\r\n1\r\n$1\r\n3\r\n$1\r\n4\r\n"
13736        );
13737        // Out of order, the walk restarts, and 0.5 answers 3 either way while
13738        // the two after it are read from the front again.
13739        assert_eq!(
13740            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0.5", b"0.1", b"0.9"]),
13741            "*3\r\n$1\r\n3\r\n$1\r\n1\r\n$1\r\n4\r\n"
13742        );
13743        assert_eq!(
13744            f.run(&[b"TDIGEST.RANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
13745            "*5\r\n:-1\r\n:0\r\n:2\r\n:3\r\n:4\r\n"
13746        );
13747        assert_eq!(
13748            f.run(&[b"TDIGEST.REVRANK", b"s", b"0", b"1", b"3", b"4", b"5"]),
13749            "*5\r\n:4\r\n:3\r\n:1\r\n:0\r\n:-1\r\n"
13750        );
13751        assert_eq!(
13752            f.run(&[b"TDIGEST.BYRANK", b"s", b"0", b"1", b"3", b"4"]),
13753            "*4\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n4\r\n$3\r\ninf\r\n"
13754        );
13755        assert_eq!(
13756            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"0", b"1", b"3", b"4"]),
13757            "*4\r\n$1\r\n4\r\n$1\r\n3\r\n$1\r\n1\r\n$4\r\n-inf\r\n"
13758        );
13759        assert_eq!(
13760            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0", b"1"]),
13761            "$3\r\n2.5\r\n"
13762        );
13763        assert_eq!(
13764            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.25", b"0.75"]),
13765            "$3\r\n2.5\r\n"
13766        );
13767        // The ranges, which are separate sentences from the parse failures.
13768        assert_eq!(
13769            f.run(&[b"TDIGEST.QUANTILE", b"s", b"1.1"]),
13770            "-ERR T-Digest: quantile should be in [0,1]\r\n"
13771        );
13772        assert_eq!(
13773            f.run(&[b"TDIGEST.QUANTILE", b"s", b"zzz"]),
13774            "-ERR T-Digest: error parsing quantile\r\n"
13775        );
13776        assert_eq!(
13777            f.run(&[b"TDIGEST.CDF", b"s", b"zzz"]),
13778            "-ERR T-Digest: error parsing cdf\r\n"
13779        );
13780        assert_eq!(
13781            f.run(&[b"TDIGEST.RANK", b"s", b"zzz"]),
13782            "-ERR T-Digest: error parsing value\r\n"
13783        );
13784        assert_eq!(
13785            f.run(&[b"TDIGEST.BYRANK", b"s", b"-1"]),
13786            "-ERR T-Digest: rank needs to be non negative\r\n"
13787        );
13788        assert_eq!(
13789            f.run(&[b"TDIGEST.BYRANK", b"s", b"1.5"]),
13790            "-ERR T-Digest: error parsing rank\r\n"
13791        );
13792        // Both cuts have their own parse sentence and share the range one, and
13793        // equal cuts are refused rather than answering nothing.
13794        assert_eq!(
13795            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"zzz", b"0.9"]),
13796            "-ERR T-Digest: error parsing low_cut_percentile\r\n"
13797        );
13798        assert_eq!(
13799            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"zzz"]),
13800            "-ERR T-Digest: error parsing high_cut_percentile\r\n"
13801        );
13802        assert_eq!(
13803            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.1", b"1.1"]),
13804            "-ERR T-Digest: low_cut_percentile and high_cut_percentile should be in [0,1]\r\n"
13805        );
13806        assert_eq!(
13807            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"s", b"0.5", b"0.5"]),
13808            "-ERR T-Digest: low_cut_percentile should be lower than high_cut_percentile\r\n"
13809        );
13810    }
13811
13812    /// An empty digest answers every question, and answers most of them with
13813    /// something that is not a number.
13814    #[test]
13815    fn an_empty_digest_has_an_answer_for_everything() {
13816        let mut f = Fixture::new();
13817        f.run(&[b"TDIGEST.CREATE", b"e"]);
13818        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
13819        assert_eq!(f.run(&[b"TDIGEST.MAX", b"e"]), "$3\r\nnan\r\n");
13820        assert_eq!(
13821            f.run(&[b"TDIGEST.QUANTILE", b"e", b"0", b"1"]),
13822            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
13823        );
13824        assert_eq!(f.run(&[b"TDIGEST.CDF", b"e", b"0"]), "*1\r\n$3\r\nnan\r\n");
13825        assert_eq!(
13826            f.run(&[b"TDIGEST.TRIMMED_MEAN", b"e", b"0.1", b"0.9"]),
13827            "$3\r\nnan\r\n"
13828        );
13829        // Minus two, which is a number no rank on a digest with samples in it
13830        // can ever be.
13831        assert_eq!(
13832            f.run(&[b"TDIGEST.RANK", b"e", b"0", b"1"]),
13833            "*2\r\n:-2\r\n:-2\r\n"
13834        );
13835        assert_eq!(
13836            f.run(&[b"TDIGEST.REVRANK", b"e", b"0", b"1"]),
13837            "*2\r\n:-2\r\n:-2\r\n"
13838        );
13839        assert_eq!(
13840            f.run(&[b"TDIGEST.BYRANK", b"e", b"0", b"5"]),
13841            "*2\r\n$3\r\nnan\r\n$3\r\nnan\r\n"
13842        );
13843        // A reset puts a digest with samples back into exactly this state.
13844        f.run(&[b"TDIGEST.ADD", b"e", b"1", b"2", b"3"]);
13845        assert_eq!(f.run(&[b"TDIGEST.RESET", b"e"]), "+OK\r\n");
13846        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), "$3\r\nnan\r\n");
13847        // Down to the compression count, so a reset digest and a fresh one of
13848        // the same compression report the same nine numbers.
13849        f.run(&[b"TDIGEST.CREATE", b"e2"]);
13850        assert_eq!(
13851            f.run(&[b"TDIGEST.INFO", b"e"]),
13852            f.run(&[b"TDIGEST.INFO", b"e2"])
13853        );
13854    }
13855
13856    /// The double parser is Redis's and not this engine's, and the two disagree
13857    /// at both ends of the range.
13858    #[test]
13859    fn a_sample_is_read_the_way_redis_reads_a_double() {
13860        let mut f = Fixture::new();
13861        f.run(&[b"TDIGEST.CREATE", b"a"]);
13862        // Overflow and underflow are parse failures rather than an infinity and
13863        // a zero, which is where this parts company with the rest of the engine.
13864        for bad in [
13865            &b"nan"[..],
13866            b"1e400",
13867            b"-1e400",
13868            b"1e309",
13869            b"1e-400",
13870            b"",
13871            b" 1",
13872            b"1 ",
13873            b"1e",
13874            b"--1",
13875        ] {
13876            assert_eq!(
13877                f.run(&[b"TDIGEST.ADD", b"a", bad]),
13878                "-ERR T-Digest: error parsing val parameter\r\n",
13879                "{}",
13880                String::from_utf8_lossy(bad)
13881            );
13882        }
13883        // An infinity spelled out parses and is then refused for being one, with
13884        // a different sentence.
13885        for word in [&b"inf"[..], b"-inf", b"+INF", b"Infinity"] {
13886            assert_eq!(
13887                f.run(&[b"TDIGEST.ADD", b"a", word]),
13888                "-ERR T-Digest: val parameter needs to be a finite number\r\n",
13889                "{}",
13890                String::from_utf8_lossy(word)
13891            );
13892        }
13893        // These all parse: hex, a bare point either side, and the smallest
13894        // subnormal the reference will take.
13895        for good in [&b"0x10"[..], b".5", b"1.", b"1e-320", b"-0", b"0"] {
13896            assert_eq!(
13897                f.run(&[b"TDIGEST.ADD", b"a", good]),
13898                "+OK\r\n",
13899                "{}",
13900                String::from_utf8_lossy(good)
13901            );
13902        }
13903        // Nothing landed from the failures, so six samples is what there is.
13904        assert!(
13905            f.run(&[b"TDIGEST.INFO", b"a"])
13906                .contains("Observations\r\n:6\r\n")
13907        );
13908        // Every value is parsed before any is added, so this whole command is a
13909        // no op.
13910        assert_eq!(
13911            f.run(&[b"TDIGEST.ADD", b"a", b"1", b"zzz"]),
13912            "-ERR T-Digest: error parsing val parameter\r\n"
13913        );
13914        assert!(
13915            f.run(&[b"TDIGEST.INFO", b"a"])
13916                .contains("Observations\r\n:6\r\n")
13917        );
13918    }
13919
13920    /// What a merge does to its destination, to its inputs and to the buffer
13921    /// split `TDIGEST.INFO` reports.
13922    #[test]
13923    fn a_merge_sweeps_the_destination_between_its_inputs() {
13924        let mut f = Fixture::new();
13925        f.run(&[b"TDIGEST.CREATE", b"m1", b"COMPRESSION", b"100"]);
13926        f.run(&[b"TDIGEST.ADD", b"m1", b"1", b"2", b"3"]);
13927        f.run(&[b"TDIGEST.CREATE", b"m2", b"COMPRESSION", b"200"]);
13928        f.run(&[b"TDIGEST.ADD", b"m2", b"4", b"5", b"6"]);
13929        assert_eq!(
13930            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"m2"]),
13931            "+OK\r\n"
13932        );
13933        // The destination did not exist, so the compression is the largest of
13934        // the inputs. The three from the first input were swept in before the
13935        // three from the second arrived, which is the one visible effect of the
13936        // reference folding one input at a time.
13937        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
13938        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
13939        assert!(info.contains("Merged nodes\r\n:3\r\n"), "{info}");
13940        assert!(info.contains("Unmerged nodes\r\n:3\r\n"), "{info}");
13941        assert!(info.contains("Total compressions\r\n:1\r\n"), "{info}");
13942        assert_eq!(f.run(&[b"TDIGEST.MIN", b"d"]), "$1\r\n1\r\n");
13943        assert_eq!(f.run(&[b"TDIGEST.MAX", b"d"]), "$1\r\n6\r\n");
13944        // Reading a source sweeps it too, so a merge writes to keys it only
13945        // reads from.
13946        assert!(
13947            f.run(&[b"TDIGEST.INFO", b"m1"])
13948                .contains("Merged nodes\r\n:3\r\n")
13949        );
13950        // Without OVERRIDE the destination joins its own inputs, so this takes
13951        // it to nine observations and keeps its own compression.
13952        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1"]);
13953        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
13954        assert!(info.contains("Observations\r\n:9\r\n"), "{info}");
13955        assert!(info.contains("Compression\r\n:200\r\n"), "{info}");
13956        // With OVERRIDE the old destination is dropped and the compression goes
13957        // back to the largest of the inputs.
13958        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"OVERRIDE"]);
13959        let info = f.run(&[b"TDIGEST.INFO", b"d"]);
13960        assert!(info.contains("Observations\r\n:3\r\n"), "{info}");
13961        assert!(info.contains("Compression\r\n:100\r\n"), "{info}");
13962        // And COMPRESSION beats both.
13963        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION", b"500"]);
13964        assert!(
13965            f.run(&[b"TDIGEST.INFO", b"d"])
13966                .contains("Compression\r\n:500\r\n")
13967        );
13968        // Naming the destination as a source folds it in twice.
13969        f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"d"]);
13970        assert!(
13971            f.run(&[b"TDIGEST.INFO", b"d"])
13972                .contains("Observations\r\n:12\r\n")
13973        );
13974        // The arguments, in the order the reference checks them.
13975        assert_eq!(
13976            f.run(&[b"TDIGEST.MERGE", b"d", b"zzz", b"m1"]),
13977            "-ERR T-Digest: error parsing numkeys\r\n"
13978        );
13979        assert_eq!(
13980            f.run(&[b"TDIGEST.MERGE", b"d", b"0", b"m1"]),
13981            "-ERR T-Digest: numkeys needs to be a positive integer\r\n"
13982        );
13983        assert!(
13984            f.run(&[b"TDIGEST.MERGE", b"d", b"3", b"m1", b"m2"])
13985                .contains("wrong number of arguments")
13986        );
13987        assert!(
13988            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"COMPRESSION"])
13989                .contains("wrong number of arguments")
13990        );
13991        assert_eq!(
13992            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"m1", b"NOPE"]),
13993            "-ERR T-Digest: wrong keyword\r\n"
13994        );
13995        // A source that is not there stops the whole thing, and the destination
13996        // is left as it was.
13997        assert_eq!(
13998            f.run(&[b"TDIGEST.MERGE", b"d", b"2", b"m1", b"gone"]),
13999            "-ERR T-Digest: key does not exist\r\n"
14000        );
14001        assert!(
14002            f.run(&[b"TDIGEST.INFO", b"d"])
14003                .contains("Observations\r\n:12\r\n")
14004        );
14005        // A destination that is not there and is also named as a source is the
14006        // same sentence rather than an empty merge.
14007        assert_eq!(
14008            f.run(&[b"TDIGEST.MERGE", b"gone", b"1", b"gone"]),
14009            "-ERR T-Digest: key does not exist\r\n"
14010        );
14011    }
14012
14013    /// The RESP3 shapes, which are the two the protocols disagree about.
14014    #[test]
14015    fn a_digest_answers_doubles_and_a_map_on_resp3() {
14016        let mut f = Fixture::new();
14017        f.run(&[b"HELLO", b"3"]);
14018        f.run(&[b"TDIGEST.CREATE", b"s"]);
14019        f.run(&[b"TDIGEST.ADD", b"s", b"1", b"2", b"3", b"4"]);
14020        assert_eq!(f.run(&[b"TDIGEST.MIN", b"s"]), ",1\r\n");
14021        assert_eq!(
14022            f.run(&[b"TDIGEST.QUANTILE", b"s", b"0", b"1"]),
14023            "*2\r\n,1\r\n,4\r\n"
14024        );
14025        assert_eq!(f.run(&[b"TDIGEST.CDF", b"s", b"1"]), "*1\r\n,0.125\r\n");
14026        // The two infinities and the NaN go out as the bare words.
14027        assert_eq!(f.run(&[b"TDIGEST.BYRANK", b"s", b"4"]), "*1\r\n,inf\r\n");
14028        assert_eq!(
14029            f.run(&[b"TDIGEST.BYREVRANK", b"s", b"4"]),
14030            "*1\r\n,-inf\r\n"
14031        );
14032        f.run(&[b"TDIGEST.CREATE", b"e"]);
14033        assert_eq!(f.run(&[b"TDIGEST.MIN", b"e"]), ",nan\r\n");
14034        // The ranks stay integers on both protocols.
14035        assert_eq!(f.run(&[b"TDIGEST.RANK", b"s", b"1"]), "*1\r\n:0\r\n");
14036        // Every question above swept the buffer in, so the four samples are all
14037        // merged by now and the compression count says it happened once.
14038        assert_eq!(
14039            f.run(&[b"TDIGEST.INFO", b"s"]),
14040            "%9\r\n+Compression\r\n:100\r\n+Capacity\r\n:610\r\n+Merged nodes\r\n:4\r\n\
14041             +Unmerged nodes\r\n:0\r\n+Merged weight\r\n:4\r\n+Unmerged weight\r\n:0\r\n\
14042             +Observations\r\n:4\r\n+Total compressions\r\n:1\r\n+Memory usage\r\n:9840\r\n"
14043        );
14044    }
14045
14046    /// A t digest key answers the module sentences the other sketch families
14047    /// answer, and its own word for its type.
14048    #[test]
14049    fn a_t_digest_is_a_module_key_to_the_rest_of_the_keyspace() {
14050        let mut f = Fixture::new();
14051        f.run(&[b"SET", b"s", b"text"]);
14052        for cmd in [
14053            vec![&b"TDIGEST.CREATE"[..], b"s"],
14054            vec![&b"TDIGEST.RESET"[..], b"s"],
14055            vec![&b"TDIGEST.ADD"[..], b"s", b"1"],
14056            vec![&b"TDIGEST.MIN"[..], b"s"],
14057            vec![&b"TDIGEST.MAX"[..], b"s"],
14058            vec![&b"TDIGEST.QUANTILE"[..], b"s", b"0.5"],
14059            vec![&b"TDIGEST.CDF"[..], b"s", b"1"],
14060            vec![&b"TDIGEST.TRIMMED_MEAN"[..], b"s", b"0.1", b"0.9"],
14061            vec![&b"TDIGEST.RANK"[..], b"s", b"1"],
14062            vec![&b"TDIGEST.REVRANK"[..], b"s", b"1"],
14063            vec![&b"TDIGEST.BYRANK"[..], b"s", b"0"],
14064            vec![&b"TDIGEST.BYREVRANK"[..], b"s", b"0"],
14065            vec![&b"TDIGEST.INFO"[..], b"s"],
14066        ] {
14067            let name = String::from_utf8_lossy(cmd[0]).into_owned();
14068            let reply = f.run(&cmd);
14069            assert!(reply.starts_with("-WRONGTYPE"), "{name}: {reply}");
14070        }
14071        // The merge checks its destination the same way, and its sources too.
14072        f.run(&[b"TDIGEST.CREATE", b"t"]);
14073        assert!(
14074            f.run(&[b"TDIGEST.MERGE", b"s", b"1", b"t"])
14075                .starts_with("-WRONGTYPE")
14076        );
14077        assert!(
14078            f.run(&[b"TDIGEST.MERGE", b"d", b"1", b"s"])
14079                .starts_with("-WRONGTYPE")
14080        );
14081        assert_eq!(
14082            f.run(&[b"COPY", b"t", b"t2"]),
14083            "-ERR not supported for this module key\r\n"
14084        );
14085        assert_eq!(
14086            f.run(&[b"DUMP", b"t"]),
14087            "-ERR DUMP is not supported for this module key\r\n"
14088        );
14089        assert_eq!(f.run(&[b"EXPIRE", b"t", b"100"]), ":1\r\n");
14090        assert_eq!(f.run(&[b"PERSIST", b"t"]), ":1\r\n");
14091        assert_eq!(f.run(&[b"RENAME", b"t", b"t3"]), "+OK\r\n");
14092        assert_eq!(f.run(&[b"TYPE", b"t3"]), "+TDIS-TYPE\r\n");
14093        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"t3"]), "$3\r\nraw\r\n");
14094        assert_eq!(f.run(&[b"DEL", b"t3"]), ":1\r\n");
14095        // An empty digest is still a key, so the twelve that are not the
14096        // constructor all say the same thing once it is gone.
14097        assert_eq!(
14098            f.run(&[b"TDIGEST.INFO", b"t3"]),
14099            "-ERR T-Digest: key does not exist\r\n"
14100        );
14101        // The key is looked at before the arguments, so a bad argument at a key
14102        // that is not there still says the key is not there.
14103        assert_eq!(
14104            f.run(&[b"TDIGEST.QUANTILE", b"t3", b"zzz"]),
14105            "-ERR T-Digest: key does not exist\r\n"
14106        );
14107    }
14108
14109    /// The three shapes an `XADD` id can take, and the one rule behind all of
14110    /// them.
14111    #[test]
14112    fn xadd_ids_only_ever_go_up() {
14113        let mut f = Fixture::new();
14114        // A bare millisecond is that millisecond and sequence zero.
14115        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
14116        // And `5-*` is the next free sequence inside it.
14117        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
14118        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
14119        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
14120        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
14121
14122        assert!(
14123            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
14124                .contains("equal or smaller")
14125        );
14126        assert!(
14127            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
14128                .contains("must be greater than 0-0")
14129        );
14130        assert!(
14131            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
14132                .contains("Invalid stream ID")
14133        );
14134        // The pairs have to be pairs, and Redis calls an odd one an arity error
14135        // rather than a syntax error even though the table has already passed.
14136        assert!(
14137            f.run(&[b"XADD", b"s", b"*", b"a"])
14138                .contains("wrong number of arguments")
14139        );
14140
14141        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
14142        // producer can tell nobody is consuming this yet from the write landed.
14143        assert_eq!(
14144            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
14145            "$-1\r\n"
14146        );
14147        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
14148        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
14149        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
14150    }
14151
14152    /// The trim options, which are three keywords that disagree about how many
14153    /// arguments they take.
14154    #[test]
14155    fn trimming_reads_its_options_the_way_redis_does() {
14156        let mut f = Fixture::new();
14157        for i in 1..=10u32 {
14158            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
14159        }
14160        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
14161        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
14162        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
14163        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14164
14165        // One argument after the keyword and the `~` is read as the threshold,
14166        // which is what a real server does and is the reason this is a number
14167        // complaint and not a syntax one.
14168        assert!(
14169            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
14170                .contains("not an integer")
14171        );
14172        assert!(
14173            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
14174                .contains("MAXLEN argument must be >= 0")
14175        );
14176        // The strategy check runs before the approximation check, so a LIMIT
14177        // with neither is told about the missing strategy.
14178        assert!(
14179            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
14180                .contains("without specifying a trimming strategy")
14181        );
14182        assert!(
14183            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
14184                .contains("without the special ~ option")
14185        );
14186        assert!(
14187            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
14188                .contains("at the same time are not compatible")
14189        );
14190        // NOMKSTREAM is XADD's and XTRIM does not take it.
14191        assert!(
14192            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
14193                .contains("syntax error")
14194        );
14195        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
14196    }
14197
14198    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
14199    #[test]
14200    fn xrange_looks_the_key_up_before_it_reads_the_count() {
14201        let mut f = Fixture::new();
14202        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
14203        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
14204
14205        assert_eq!(
14206            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
14207            "*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\
14208             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
14209        );
14210        assert_eq!(
14211            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
14212            "*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"
14213        );
14214        // The exclusive bound is stepped after the missing sequence is filled
14215        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
14216        // `6-1` is still in the range.
14217        assert_eq!(
14218            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
14219            "*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\
14220             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
14221        );
14222        assert_eq!(
14223            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
14224            "*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"
14225        );
14226        assert!(
14227            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
14228                .contains("Invalid stream ID")
14229        );
14230
14231        // The two kinds of nothing. A key that is not there is an empty array
14232        // and a key that is there with a count of zero is a null array, because
14233        // the lookup happens first.
14234        assert_eq!(
14235            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
14236            "*0\r\n"
14237        );
14238        assert_eq!(
14239            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
14240            "*-1\r\n"
14241        );
14242        f.run(&[b"SET", b"str", b"v"]);
14243        assert!(
14244            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
14245                .starts_with("-WRONGTYPE")
14246        );
14247        // The count is read in a loop, so the last one wins.
14248        assert_eq!(
14249            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
14250            "*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"
14251        );
14252    }
14253
14254    /// `XDEL` and `XACK` check every id before they touch any of them.
14255    #[test]
14256    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
14257        let mut f = Fixture::new();
14258        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14259        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14260        assert!(
14261            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
14262                .contains("Invalid stream ID")
14263        );
14264        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14265        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
14266        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
14267        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
14268        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
14269    }
14270
14271    /// `XGROUP`, and the two different complaints it makes about arguments.
14272    #[test]
14273    fn xgroup_has_an_arity_per_subcommand() {
14274        let mut f = Fixture::new();
14275        assert!(
14276            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
14277                .contains("requires the key")
14278        );
14279        assert_eq!(
14280            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
14281            "+OK\r\n"
14282        );
14283        // A second CREATE is BUSYGROUP and not an ordinary error, because a
14284        // client racing another one to make a group branches on the prefix.
14285        assert!(
14286            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
14287                .starts_with("-BUSYGROUP")
14288        );
14289        assert_eq!(
14290            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
14291            ":1\r\n"
14292        );
14293        assert_eq!(
14294            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
14295            ":0\r\n"
14296        );
14297        assert_eq!(
14298            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
14299            ":0\r\n"
14300        );
14301
14302        // Below the subcommand's own arity is an arity error naming the pair.
14303        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
14304        assert!(
14305            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
14306            "{short}"
14307        );
14308        // At or above it in a shape the handler will not take is the other one.
14309        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
14310        assert!(
14311            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
14312            "{odd}"
14313        );
14314        assert!(
14315            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
14316                .contains("Try XGROUP HELP")
14317        );
14318
14319        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
14320        assert!(
14321            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
14322                .starts_with("-NOGROUP")
14323        );
14324        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
14325        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
14326        assert!(
14327            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
14328                .contains("requires the key")
14329        );
14330    }
14331
14332    /// A group read, an acknowledgement, and what is left in between.
14333    #[test]
14334    fn xreadgroup_hands_out_and_xack_takes_back() {
14335        let mut f = Fixture::new();
14336        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14337        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14338        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14339
14340        let first = f.run(&[
14341            b"XREADGROUP",
14342            b"GROUP",
14343            b"g",
14344            b"c1",
14345            b"COUNT",
14346            b"1",
14347            b"STREAMS",
14348            b"s",
14349            b">",
14350        ]);
14351        assert_eq!(
14352            first,
14353            "*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"
14354        );
14355        // A history read names its stream even with nothing to show, which is
14356        // the difference between it and a `>` read that found nothing.
14357        assert_eq!(
14358            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
14359            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
14360        );
14361        assert_eq!(
14362            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
14363            "*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"
14364        );
14365
14366        assert_eq!(
14367            f.run(&[b"XPENDING", b"s", b"g"]),
14368            "*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"
14369        );
14370        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
14371        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
14372        // Empty is four nulls and not a zero with three empty things.
14373        assert_eq!(
14374            f.run(&[b"XPENDING", b"s", b"g"]),
14375            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
14376        );
14377
14378        // A history read of an entry that has since been deleted is the id with
14379        // a null beside it, so the consumer can still acknowledge it.
14380        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14381        f.run(&[b"XDEL", b"s", b"2-1"]);
14382        assert_eq!(
14383            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
14384            "*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"
14385        );
14386
14387        // The group lookup runs before the id parse, so a `+` at a stream with
14388        // no such group is told about the group and not about the id.
14389        assert!(
14390            f.run(&[
14391                b"XREADGROUP",
14392                b"GROUP",
14393                b"nope",
14394                b"c",
14395                b"STREAMS",
14396                b"s",
14397                b"+"
14398            ])
14399            .starts_with("-NOGROUP")
14400        );
14401        assert!(
14402            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
14403                .contains("meaningless in the context of XREADGROUP")
14404        );
14405        assert!(
14406            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
14407                .contains("only supported by XREADGROUP")
14408        );
14409        assert!(
14410            f.run(&[
14411                b"XREADGROUP",
14412                b"GROUP",
14413                b"g",
14414                b"c",
14415                b"STREAMS",
14416                b"s",
14417                b"a",
14418                b"b"
14419            ])
14420            .contains("Unbalanced 'xreadgroup' list of streams")
14421        );
14422    }
14423
14424    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
14425    /// answer.
14426    #[test]
14427    fn xread_with_no_block_writes_the_null_itself() {
14428        let mut f = Fixture::new();
14429        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14430        assert_eq!(
14431            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
14432            "*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"
14433        );
14434        // Nothing new is a null array and not an empty one, and a stream with
14435        // nothing new is left out rather than sent with an empty list.
14436        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
14437        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
14438        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
14439        assert_eq!(
14440            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
14441            "*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"
14442        );
14443        // `$` is the last id, so nothing that is already there comes back.
14444        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
14445        // And `+` is the last entry, whatever COUNT says.
14446        assert_eq!(
14447            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
14448            "*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"
14449        );
14450        // A count of zero means unlimited here, which is the opposite of what it
14451        // means to XRANGE.
14452        assert_eq!(
14453            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
14454            "*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"
14455        );
14456        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
14457        assert!(
14458            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
14459                .contains("not an integer")
14460        );
14461        assert!(
14462            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
14463                .contains("timeout is negative")
14464        );
14465        assert!(
14466            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
14467                .contains("Unbalanced 'xread' list of streams")
14468        );
14469    }
14470
14471    /// A blocked reader, and the two ways it stops being blocked.
14472    #[test]
14473    fn a_blocked_xread_wakes_on_the_next_entry() {
14474        let mut f = Fixture::new();
14475        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14476        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
14477        assert_eq!(flow, Flow::Block);
14478        assert!(reply.is_empty());
14479
14480        // Everybody parked on the stream gets the entry, because a read takes
14481        // nothing away. That is the difference between this and BLPOP.
14482        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
14483        assert_eq!(flow, Flow::Block);
14484        assert_eq!(f.server.waiters().len(), 2);
14485
14486        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14487        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";
14488        for at in 0..2 {
14489            let mut out = Out::new(Proto::Resp2);
14490            assert!(f.server.serve_waiter(at, 0, &mut out));
14491            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
14492        }
14493
14494        // And a deadline that runs out is a null array, the same as a plain
14495        // XREAD that found nothing.
14496        f.server.waiters_mut().forget(7);
14497        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
14498        assert_eq!(flow, Flow::Block);
14499        let mut out = Out::new(Proto::Resp2);
14500        assert!(!f.server.serve_waiter(0, 0, &mut out));
14501        assert!(out.as_slice().is_empty());
14502        assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
14503        assert_eq!(
14504            core::str::from_utf8(out.as_slice()).expect("ascii"),
14505            "*-1\r\n"
14506        );
14507    }
14508
14509    /// A blocked group reader whose group is destroyed under it.
14510    #[test]
14511    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
14512        let mut f = Fixture::new();
14513        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14514        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
14515        let (flow, _) = f.flow(&[
14516            b"XREADGROUP",
14517            b"GROUP",
14518            b"g",
14519            b"c",
14520            b"BLOCK",
14521            b"0",
14522            b"STREAMS",
14523            b"s",
14524            b">",
14525        ]);
14526        assert_eq!(flow, Flow::Block);
14527
14528        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
14529        let mut out = Out::new(Proto::Resp2);
14530        assert!(f.server.serve_waiter(0, 0, &mut out));
14531        // The ordinary sentence and not a special one about having been parked,
14532        // which is what a running 8.10 sends.
14533        assert_eq!(
14534            core::str::from_utf8(out.as_slice()).expect("ascii"),
14535            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
14536        );
14537    }
14538
14539    /// `XCLAIM`, whose argument shape is the odd one in the group.
14540    #[test]
14541    fn xclaim_reads_ids_until_one_will_not_parse() {
14542        let mut f = Fixture::new();
14543        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14544        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14545        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14546        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14547
14548        // Everything after the first argument that is not an id is an option, so
14549        // a `-` is an unrecognised option and not a bad id.
14550        assert!(
14551            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
14552                .contains("Unrecognized XCLAIM option '-'")
14553        );
14554        assert_eq!(
14555            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
14556            "*1\r\n$3\r\n1-1\r\n"
14557        );
14558        // An id that is pending but whose entry has gone is an empty answer, and
14559        // it leaves the pending list on the way past.
14560        f.run(&[b"XDEL", b"s", b"2-1"]);
14561        assert_eq!(
14562            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
14563            "*0\r\n"
14564        );
14565        assert!(
14566            f.run(&[b"XPENDING", b"s", b"g"])
14567                .starts_with("*4\r\n:1\r\n")
14568        );
14569        assert!(
14570            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
14571                .starts_with("-NOGROUP")
14572        );
14573        assert!(
14574            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
14575                .contains("Invalid min-idle-time argument for XCLAIM")
14576        );
14577    }
14578
14579    /// `XAUTOCLAIM`, and the third value nobody expects.
14580    #[test]
14581    fn xautoclaim_reports_what_it_dropped() {
14582        let mut f = Fixture::new();
14583        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14584        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14585        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14586        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14587        f.run(&[b"XDEL", b"s", b"1-1"]);
14588
14589        // The cursor, what was claimed, and what was dropped for no longer being
14590        // in the stream. The third one is what makes a sweep converge.
14591        assert_eq!(
14592            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
14593            "*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"
14594        );
14595        assert!(
14596            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
14597                .contains("COUNT must be > 0")
14598        );
14599        assert!(
14600            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
14601                .starts_with("-NOGROUP")
14602        );
14603    }
14604
14605    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
14606    #[test]
14607    fn xdelex_answers_one_integer_an_id() {
14608        let mut f = Fixture::new();
14609        for i in 1..=4 {
14610            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
14611        }
14612        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14613        f.run(&[
14614            b"XREADGROUP",
14615            b"GROUP",
14616            b"g",
14617            b"c",
14618            b"COUNT",
14619            b"2",
14620            b"STREAMS",
14621            b"s",
14622            b">",
14623        ]);
14624
14625        // One means gone and minus one means it was not there to start with.
14626        assert_eq!(
14627            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
14628            "*2\r\n:1\r\n:-1\r\n"
14629        );
14630        // `KEEPREF` leaves the pending entry behind, so the group still counts
14631        // the one it was handed even though the entry has gone.
14632        assert!(
14633            f.run(&[b"XPENDING", b"s", b"g"])
14634                .starts_with("*4\r\n:2\r\n")
14635        );
14636        // `DELREF` takes it out of every pending list on the way past.
14637        assert_eq!(
14638            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
14639            "*1\r\n:1\r\n"
14640        );
14641        // `1-1` is still in the list, because the delete before it said KEEPREF.
14642        assert_eq!(
14643            f.run(&[b"XPENDING", b"s", b"g"]),
14644            "*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"
14645        );
14646
14647        // Two means somebody still wants it, and the question is wider than the
14648        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
14649        // refused even though no consumer has ever been handed it.
14650        assert_eq!(
14651            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
14652            "*2\r\n:2\r\n:2\r\n"
14653        );
14654
14655        // A key that is not there answers minus ones without reading the IDs.
14656        assert_eq!(
14657            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
14658            "*2\r\n:-1\r\n:-1\r\n"
14659        );
14660        // A key that is there validates every ID before deleting any of them.
14661        assert!(
14662            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
14663                .starts_with("-ERR Invalid stream ID")
14664        );
14665        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14666
14667        assert!(
14668            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
14669                .contains("Number of IDs must be a positive integer")
14670        );
14671        assert!(
14672            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
14673                .contains("The `numids` parameter must match the number of arguments")
14674        );
14675        // The condition is one word, so a second one is a syntax error, and so
14676        // is one ID more than the count promised.
14677        assert!(
14678            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
14679                .starts_with("-ERR syntax error")
14680        );
14681        assert!(
14682            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
14683                .starts_with("-ERR syntax error")
14684        );
14685        // The key is looked up first, so the wrong type beats the syntax.
14686        f.run(&[b"SET", b"str", b"v"]);
14687        assert!(
14688            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
14689                .starts_with("-WRONGTYPE")
14690        );
14691    }
14692
14693    /// `XACKDEL`, whose reply is about the pending list and not about the log.
14694    #[test]
14695    fn xackdel_reports_what_the_group_was_holding() {
14696        let mut f = Fixture::new();
14697        for i in 1..=3 {
14698            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
14699        }
14700        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14701        f.run(&[
14702            b"XREADGROUP",
14703            b"GROUP",
14704            b"g",
14705            b"c",
14706            b"COUNT",
14707            b"1",
14708            b"STREAMS",
14709            b"s",
14710            b">",
14711        ]);
14712
14713        // Minus one is not about the stream: `2-1` is sitting there unread and
14714        // still answers minus one, because the group was not holding it. It also
14715        // stays, since only an ID that was acknowledged is deleted.
14716        assert_eq!(
14717            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
14718            "*2\r\n:1\r\n:-1\r\n"
14719        );
14720        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14721
14722        // A missing group is minus one an ID and not a NOGROUP.
14723        assert_eq!(
14724            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
14725            "*1\r\n:-1\r\n"
14726        );
14727        assert_eq!(
14728            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
14729            "*1\r\n:-1\r\n"
14730        );
14731
14732        // The acknowledgement happens whatever the condition says, so an ACKED
14733        // that answers two has still emptied the pending list.
14734        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
14735        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
14736        assert_eq!(
14737            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
14738            "*1\r\n:2\r\n"
14739        );
14740        assert_eq!(
14741            f.run(&[b"XPENDING", b"s", b"g"]),
14742            "*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"
14743        );
14744        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
14745    }
14746
14747    /// `XNACK`, which hands an entry back to nobody.
14748    #[test]
14749    fn xnack_releases_an_entry_for_the_next_claim() {
14750        let mut f = Fixture::new();
14751        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14752        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14753        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14754        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14755        // Twice, so the delivery count is two and the words have something to
14756        // do with it.
14757        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
14758
14759        assert_eq!(
14760            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
14761            ":1\r\n"
14762        );
14763        // No owner, no idle time, and the count left where it was. A released
14764        // entry reads as idle for longer than any min-idle-time, which is what
14765        // puts it at the front of the next claim.
14766        assert_eq!(
14767            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
14768            "*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"
14769        );
14770        // The consumer no longer holds it, so a filtered XPENDING skips it.
14771        assert_eq!(
14772            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
14773            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
14774        );
14775        // The bookmark did not move, so a `>` read will not hand it out again.
14776        assert_eq!(
14777            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
14778            "*-1\r\n"
14779        );
14780        // A claim at any min-idle-time takes it.
14781        assert_eq!(
14782            f.run(&[
14783                b"XAUTOCLAIM",
14784                b"s",
14785                b"g",
14786                b"c2",
14787                b"99999999",
14788                b"-",
14789                b"JUSTID"
14790            ]),
14791            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
14792        );
14793
14794        // `SILENT` takes one off the count rather than putting it back to zero,
14795        // which only shows on an entry that has been handed out more than once.
14796        // It was delivered and then claimed, so it is on two and goes to one.
14797        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
14798        assert!(
14799            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14800                .contains(":-1\r\n:1\r\n")
14801        );
14802        // And it stops at zero rather than wrapping.
14803        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
14804        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
14805        assert!(
14806            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14807                .contains(":-1\r\n:0\r\n")
14808        );
14809        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
14810        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
14811        assert!(
14812            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14813                .contains(":9223372036854775807\r\n")
14814        );
14815        f.run(&[
14816            b"XNACK",
14817            b"s",
14818            b"g",
14819            b"FATAL",
14820            b"IDS",
14821            b"1",
14822            b"1-1",
14823            b"RETRYCOUNT",
14824            b"3",
14825        ]);
14826        assert!(
14827            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14828                .contains(":-1\r\n:3\r\n")
14829        );
14830
14831        // Releasing something the group is not holding is zero, and `FORCE`
14832        // makes the pending entry rather than answering zero. A forced entry
14833        // starts at zero, since there was no earlier count to keep.
14834        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
14835        assert_eq!(
14836            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
14837            ":0\r\n"
14838        );
14839        assert_eq!(
14840            f.run(&[
14841                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
14842            ]),
14843            ":1\r\n"
14844        );
14845        assert!(
14846            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
14847                .contains(":-1\r\n:0\r\n")
14848        );
14849        // `FORCE` on an ID the stream does not have is still zero.
14850        assert_eq!(
14851            f.run(&[
14852                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
14853            ]),
14854            ":0\r\n"
14855        );
14856
14857        // The group is looked up before the mode word, and it raises rather
14858        // than answering per ID the way the two delete commands do.
14859        assert_eq!(
14860            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
14861            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
14862        );
14863        assert!(
14864            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
14865                .starts_with("-ERR")
14866        );
14867        // Its own sentences, which are not the ones XDELEX uses.
14868        assert!(
14869            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
14870                .contains("numids must be a positive integer")
14871        );
14872        assert!(
14873            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
14874                .contains("number of IDs doesn't match numids")
14875        );
14876        // Everything past the counted IDs is an option, so one too many is an
14877        // option nobody recognises and not a count that does not add up.
14878        assert!(
14879            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
14880                .contains("Unrecognized XNACK option '2-1'")
14881        );
14882    }
14883
14884    /// `XINFO`, which is where the shape of the storage shows through.
14885    #[test]
14886    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
14887        let mut f = Fixture::new();
14888        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14889        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
14890        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14891        f.run(&[
14892            b"XREADGROUP",
14893            b"GROUP",
14894            b"g",
14895            b"c1",
14896            b"COUNT",
14897            b"1",
14898            b"STREAMS",
14899            b"s",
14900            b">",
14901        ]);
14902
14903        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
14904        // Ten pairs, since the six idempotency fields have nothing behind them
14905        // here and a zero would claim they had. That is D-27.
14906        assert!(info.starts_with("*20\r\n"), "{info}");
14907        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
14908        assert!(
14909            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
14910            "{info}"
14911        );
14912        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
14913        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
14914
14915        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
14916        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
14917        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
14918        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
14919        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
14920
14921        // A consumer that has never been given anything reports minus one for
14922        // inactive rather than the moment it turned up, which is what tells a
14923        // worker that is stuck from one that has nothing to do.
14924        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
14925        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
14926        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
14927        assert!(
14928            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
14929            "{consumers}"
14930        );
14931        // And in name order, which the storage does not hold them in.
14932        let c1 = consumers.find("c1").unwrap();
14933        let c2 = consumers.find("c2").unwrap();
14934        assert!(c1 < c2, "{consumers}");
14935
14936        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
14937        assert!(full.starts_with("*18\r\n"), "{full}");
14938        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
14939        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
14940
14941        assert!(
14942            f.run(&[b"XINFO", b"STREAM", b"missing"])
14943                .contains("no such key")
14944        );
14945        assert!(
14946            f.run(&[b"XINFO", b"GROUPS", b"missing"])
14947                .contains("no such key")
14948        );
14949        assert!(
14950            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
14951                .starts_with("-NOGROUP")
14952        );
14953        assert!(
14954            f.run(&[b"XINFO", b"NOSUCH", b"s"])
14955                .contains("Try XINFO HELP")
14956        );
14957        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
14958        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
14959    }
14960
14961    /// `XPENDING`'s long form, which reads its arguments by counting them.
14962    #[test]
14963    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
14964        let mut f = Fixture::new();
14965        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
14966        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
14967        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
14968
14969        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
14970        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");
14971        assert_eq!(
14972            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
14973            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
14974        );
14975        // A consumer nobody has heard of holds nothing rather than erroring.
14976        assert_eq!(
14977            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
14978            "*0\r\n"
14979        );
14980        assert_eq!(
14981            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
14982            list
14983        );
14984        // IDLE is only read at position three.
14985        assert!(
14986            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
14987                .contains("syntax error")
14988        );
14989        assert!(
14990            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
14991                .contains("syntax error")
14992        );
14993        assert_eq!(
14994            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
14995            "*0\r\n"
14996        );
14997        assert!(
14998            f.run(&[b"XPENDING", b"missing", b"g"])
14999                .starts_with("-NOGROUP")
15000        );
15001    }
15002
15003    /// `XSETID`, which is three counters and two refusals.
15004    #[test]
15005    fn xsetid_will_not_go_below_what_is_there() {
15006        let mut f = Fixture::new();
15007        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
15008        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
15009        assert_eq!(
15010            f.run(&[
15011                b"XSETID",
15012                b"s",
15013                b"10-1",
15014                b"ENTRIESADDED",
15015                b"7",
15016                b"MAXDELETEDID",
15017                b"9-1"
15018            ]),
15019            "+OK\r\n"
15020        );
15021        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
15022        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
15023        assert!(
15024            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
15025            "{info}"
15026        );
15027
15028        assert!(
15029            f.run(&[b"XSETID", b"s", b"1-1"])
15030                .contains("smaller than the target stream top item")
15031        );
15032        assert!(
15033            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
15034                .contains("entries_added must be positive")
15035        );
15036        assert!(
15037            f.run(&[b"XSETID", b"missing", b"1-1"])
15038                .contains("no such key")
15039        );
15040    }
15041
15042    /// RESP3, where the two reads answer a map and the entries stay an array.
15043    #[test]
15044    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
15045        let mut f = Fixture::new();
15046        f.run(&[b"HELLO", b"3"]);
15047        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
15048        // A map header and then the key and the entries side by side, with no
15049        // two element array wrapping the pair.
15050        assert_eq!(
15051            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
15052            "%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"
15053        );
15054        // The fields are still one flat array and not a map, which is Redis's
15055        // shape and is what every consumer written before RESP3 expects.
15056        assert_eq!(
15057            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
15058            "*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"
15059        );
15060        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
15061    }
15062
15063    /// A store to migrate values into, so a test can watch the inversion.
15064    ///
15065    /// A vector rather than a file for the same reason the tier's own tests use
15066    /// one: the file work has not attached a real store yet, and what this is
15067    /// checking is the policy above the store rather than the store.
15068    struct Mem {
15069        blobs: Vec<Vec<u8>>,
15070    }
15071
15072    impl yo_kv::cold::Blocks for Mem {
15073        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
15074            self.blobs.push(bytes.to_vec());
15075            Ok(yo_common::Addr::new(
15076                yo_common::Space::Log,
15077                (self.blobs.len() - 1) as u64,
15078            ))
15079        }
15080
15081        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
15082            self.blobs
15083                .get(at.offset() as usize)
15084                .map(Vec::as_slice)
15085                .ok_or_else(|| {
15086                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
15087                })
15088        }
15089
15090        fn bytes(&self) -> u64 {
15091            self.blobs.iter().map(|b| b.len() as u64).sum()
15092        }
15093    }
15094
15095    /// A server holding several segments of strings, with somewhere to put them.
15096    ///
15097    /// Answers the fixture and what it was holding when it stopped filling.
15098    fn filled(attach: bool) -> (Fixture, usize) {
15099        let mut f = Fixture::new();
15100        if attach {
15101            f.server.db(0).attach(Box::new(Mem { blobs: Vec::new() }));
15102        }
15103        let val = vec![b'v'; 256];
15104        for i in 0..24000u32 {
15105            let k = format!("key:{i:08}");
15106            f.run(&[b"SET", k.as_bytes(), &val]);
15107        }
15108        let full = f.server.memory_bytes();
15109        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
15110        (f, full)
15111    }
15112
15113    /// Write until the server is under `limit` or the writes run out.
15114    ///
15115    /// The same shape the eviction test uses. A memory limit is enforced in
15116    /// front of a command, so nothing happens until something is written, and
15117    /// the budget means one command does not do the whole job.
15118    fn press(f: &mut Fixture, limit: usize) {
15119        let val = vec![b'v'; 256];
15120        for i in 0..3000u32 {
15121            let k = format!("new:{i:08}");
15122            assert_eq!(
15123                f.run(&[b"SET", k.as_bytes(), &val]),
15124                "+OK\r\n",
15125                "write {i} was refused"
15126            );
15127            f.server.refresh_memory();
15128            if f.server.memory_bytes() <= limit {
15129                return;
15130            }
15131        }
15132        panic!(
15133            "it never got under: {} against {limit}",
15134            f.server.memory_bytes()
15135        );
15136    }
15137
15138    #[test]
15139    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
15140        let mut f = Fixture::new();
15141        assert_eq!(
15142            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
15143            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
15144            "no limit is the default"
15145        );
15146        // The same memory value parser `maxmemory` uses, and the same trap in
15147        // it, plus the one spelling that means no limit at all.
15148        for (typed, bytes) in [
15149            (&b"0"[..], "0"),
15150            (b"1024", "1024"),
15151            (b"1k", "1000"),
15152            (b"1gb", "1073741824"),
15153            (b"-1", "-1"),
15154        ] {
15155            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
15156            assert_eq!(
15157                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
15158                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
15159                "set {}",
15160                String::from_utf8_lossy(typed)
15161            );
15162        }
15163        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
15164            assert_eq!(
15165                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
15166                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
15167                "refused {}",
15168                String::from_utf8_lossy(bad)
15169            );
15170        }
15171        // Nothing is attached, so the answer to a memory limit is still Redis's.
15172        let info = f.run(&[b"INFO", b"memory"]);
15173        assert!(info.contains("maxstore:-1"), "{info}");
15174        assert!(info.contains("yo_memory_regime:evict"), "{info}");
15175        assert!(info.contains("yo_store_bytes:0"), "{info}");
15176    }
15177
15178    #[test]
15179    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
15180        // The inversion. The same pressure that makes a Redis server throw keys
15181        // away makes this one move values to the file, and afterwards every key
15182        // is still there and still answers with what was stored in it.
15183        let (mut f, full) = filled(true);
15184        let keys = f.run(&[b"DBSIZE"]);
15185        assert!(
15186            f.run(&[b"INFO", b"memory"])
15187                .contains("yo_memory_regime:migrate"),
15188            "a database with somewhere to put values migrates"
15189        );
15190
15191        let limit = full - 2 * 1024 * 1024;
15192        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
15193        f.run(&[
15194            b"CONFIG",
15195            b"SET",
15196            b"maxmemory",
15197            limit.to_string().as_bytes(),
15198        ]);
15199        press(&mut f, limit);
15200
15201        assert!(
15202            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
15203            "nothing was thrown away"
15204        );
15205        let after: usize = f.run(&[b"DBSIZE"])[1..]
15206            .trim_end()
15207            .parse()
15208            .expect("a count");
15209        let before: usize = keys[1..].trim_end().parse().expect("a count");
15210        assert!(after > before, "the keys that came in are all still here");
15211        assert!(
15212            f.server.store_bytes() > 0,
15213            "and what came out of memory went to the file"
15214        );
15215        // And the values read back, which is the part that makes it a migration
15216        // rather than a loss.
15217        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
15218        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
15219        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
15220    }
15221
15222    #[test]
15223    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
15224        // The documented setting for a drop in cache. A file that may hold
15225        // nothing cannot be migrated to, so eviction is all that is left, and
15226        // the server behaves exactly as it did before any of this existed.
15227        let (mut f, full) = filled(true);
15228        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
15229        assert!(
15230            f.run(&[b"INFO", b"memory"])
15231                .contains("yo_memory_regime:evict"),
15232            "nothing may go to the file"
15233        );
15234
15235        let limit = full - 2 * 1024 * 1024;
15236        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
15237        f.run(&[
15238            b"CONFIG",
15239            b"SET",
15240            b"maxmemory",
15241            limit.to_string().as_bytes(),
15242        ]);
15243        press(&mut f, limit);
15244
15245        assert!(
15246            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
15247            "keys were thrown away, which is what was asked for"
15248        );
15249        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
15250    }
15251
15252    #[test]
15253    fn a_full_file_goes_back_to_evicting() {
15254        // A storage limit reached is a storage limit, and eviction is the right
15255        // answer to one. The budget here is a few kilobytes, so the first round
15256        // of migration fills it and everything after that is evicted.
15257        let (mut f, full) = filled(true);
15258        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
15259        let limit = full - 2 * 1024 * 1024;
15260        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
15261        f.run(&[
15262            b"CONFIG",
15263            b"SET",
15264            b"maxmemory",
15265            limit.to_string().as_bytes(),
15266        ]);
15267        press(&mut f, limit);
15268
15269        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
15270        assert!(
15271            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
15272            "and then it started evicting"
15273        );
15274        assert!(
15275            f.run(&[b"INFO", b"memory"])
15276                .contains("yo_memory_regime:evict"),
15277            "and it says so"
15278        );
15279    }
15280}