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 bits;
57mod blocking;
58mod cpu;
59mod geo;
60mod graph;
61mod hashes;
62mod hll;
63mod keyspace;
64mod lists;
65mod migrate;
66mod scan;
67mod scripting;
68mod server;
69mod sets;
70mod streams;
71mod strings;
72pub mod table;
73mod zsets;
74
75pub use args::Args;
76pub use blocking::{Parked, Waiters};
77pub use server::parse_memory;
78pub use table::{COMMANDS, Spec, arity_ok, lookup};
79
80use crate::reply::Out;
81use yo_common::{Code, Error};
82use yo_kv::cold::Blocks;
83use yo_kv::{Clock, Keyspace};
84
85/// How many databases a server has.
86///
87/// Redis's default is sixteen and its `databases` setting can change it. Ours
88/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
89/// constant. Nothing in the design needs the number to be fixed; nothing yet
90/// needs it not to be.
91pub const DATABASES: usize = 16;
92
93/// Every database's bit in [`Server::dirty`], which is what a fresh server
94/// starts on so that the first maintenance turn asks all of them.
95///
96/// A `u64` holds sixteen bits with room to spare, and the assertion below is
97/// what turns raising [`DATABASES`] past sixty four into a build failure rather
98/// than a shift that silently drops the databases past the end.
99const ALL_DATABASES: u64 = if DATABASES == 64 {
100    u64::MAX
101} else {
102    (1u64 << DATABASES) - 1
103};
104const _: () = assert!(DATABASES <= 64);
105
106/// How many keys one command throws away before it leaves the rest to the next.
107///
108/// A bound and not a loop to the end, because this runs in front of a client
109/// that is waiting for its reply, and a server a long way over its limit would
110/// otherwise hold that client for as long as it took to walk all the way back
111/// under. Sixty four is a batch's worth of commands, so a server that went over
112/// by what one batch allocated comes back under in one command, and a server
113/// whose limit was just cut in half works through it over the next few thousand
114/// rather than in one long stall. Redis bounds the same loop by a time slice
115/// instead of a count and hands the rest to a timer; there is no timer here, so
116/// the rest goes to the next command that runs.
117const EVICT_BUDGET: usize = 64;
118
119/// What a server says to a command that would allocate when it has no room.
120///
121/// Redis's `shared.oomerr`, word for word including the full stop, because
122/// clients match on the `OOM` prefix and people match on the sentence.
123const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
124
125/// What the connection should do after a command.
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum Flow {
128    /// Read the next command.
129    Continue,
130    /// Write what is buffered and then close, which is what `QUIT` asks for.
131    Close,
132    /// Nothing was written and nothing is owed yet.
133    ///
134    /// The client is on the waiter list and its reply comes when a key it named
135    /// has something in it or when its deadline passes, whichever happens first.
136    /// Until then the connection stops reading commands, because a client that
137    /// is waiting for an answer is not a client that has sent another question.
138    Block,
139}
140
141/// The numbers `INFO` reports that this layer cannot see for itself.
142///
143/// The reactor owns the sockets, so the reactor is what knows how many clients
144/// there are. It writes these directly and nothing here does anything with them
145/// except report them.
146#[derive(Debug, Clone, Copy, Default)]
147pub struct Stats {
148    /// Connections open right now.
149    pub clients: u64,
150    /// Connections accepted since the server started.
151    pub connections: u64,
152    /// Commands run since the server started, which this layer counts itself.
153    pub commands: u64,
154}
155
156/// One command's counters, for `INFO commandstats`.
157///
158/// Three of Redis's five. `usec` and `usec_per_call` are not here because
159/// nothing times a command, and timing one means two clock reads around a call
160/// that takes tens of nanoseconds to begin with. Redis pays that because Redis
161/// has room for it; this does not, and a zero under a name that says microseconds
162/// is worse than an absent field, which is the same rule the rest of `INFO`
163/// follows.
164#[derive(Debug, Clone, Copy, Default)]
165pub struct CommandStat {
166    /// Times the command ran, whatever it answered.
167    pub calls: u64,
168    /// Times it was turned away before it ran, which is the wrong number of
169    /// arguments or no room under `maxmemory`.
170    pub rejected: u64,
171    /// Times it ran and answered with an error.
172    pub failed: u64,
173}
174
175impl CommandStat {
176    /// Whether this command has ever been seen.
177    ///
178    /// A row that has not is left out of the reply, which is what Redis does and
179    /// is why the section is a handful of lines on a working server rather than
180    /// one line per command in the table.
181    const fn seen(&self) -> bool {
182        self.calls != 0 || self.rejected != 0 || self.failed != 0
183    }
184}
185
186/// A counter per command, indexed the way [`table::index_of`] says.
187///
188/// A flat array and not a map, because the dispatcher is already holding the
189/// spec and the spec's position in the table is two addresses subtracted. That
190/// makes the counting a load, an add and a store on a row the previous command
191/// of the same name has already pulled into cache.
192struct CommandStats(Box<[CommandStat]>);
193
194impl Default for CommandStats {
195    fn default() -> CommandStats {
196        CommandStats(vec![CommandStat::default(); table::count()].into_boxed_slice())
197    }
198}
199
200impl CommandStats {
201    /// The row for one command.
202    fn at(&mut self, spec: &'static Spec) -> &mut CommandStat {
203        &mut self.0[table::index_of(spec)]
204    }
205}
206
207/// Where a database gets its store from, asked by database number.
208///
209/// `None` means that database cannot have one. The caller owns whatever the
210/// stores are cut out of, which for `yodb` is one `.yo` file with a log per
211/// database, and this crate never learns what any of that is.
212pub type StoreSource = dyn FnMut(usize) -> Option<Box<dyn Blocks>>;
213
214/// Everything a server holds.
215///
216/// One of these per shard thread, not one per process: the databases inside are
217/// not `Sync` and are reached by sending their thread a command. What makes
218/// this a server rather than a shard is that it is the whole of what a
219/// connection can address.
220pub struct Server {
221    dbs: Vec<Keyspace>,
222    clock: Clock,
223    started_ms: u64,
224    /// Where the next maintenance turn starts looking, so that a database
225    /// under constant write load cannot hold the other fifteen's space.
226    next_db: usize,
227    /// One bit per database, set when a command ran against it.
228    ///
229    /// The maintenance turn after every batch used to ask all sixteen
230    /// databases whether they had anything to collect, and asking costs a load
231    /// and a store in each one. Fifteen of those are cold lines on a server
232    /// where every client is on database zero, which is every server, and the
233    /// answer is no every time. This is the cheap half of the question: a
234    /// database nobody has touched since it last said no cannot have started
235    /// saying yes.
236    dirty: u64,
237    /// What the connections are holding, kept by the engine.
238    conn_bytes: usize,
239    /// The `maxmemory` limit in bytes, zero when there is not one.
240    ///
241    /// Zero is the default and it is the whole reason the check in front of
242    /// every write is one comparison against a field that is already warm.
243    maxmemory: u64,
244    /// Where a database gets a store from the first time it needs one.
245    ///
246    /// A closure and not a store, because there are sixteen databases and a
247    /// server that fills memory on database zero should not have opened
248    /// anything for the other fifteen. Nothing is asked of this until a memory
249    /// limit is actually reached, so a server that never fills memory never
250    /// opens a file, and a server that has no file never has one of these.
251    ///
252    /// `None` from the closure means that database cannot have one, which is
253    /// how the caller says the file it opened has no more room for logs.
254    store: Option<Box<StoreSource>>,
255    /// The `maxstore` limit in bytes, `None` when there is not one.
256    ///
257    /// The storage limit, and the other half of the inversion `14` section 4.1
258    /// describes. `maxmemory` is a limit on memory and the right answer to a
259    /// memory limit on a system with a file under it is to move data to the
260    /// file, not to delete it. Deleting is the right answer to a limit on the
261    /// file, and this is that limit.
262    ///
263    /// Zero is not "no limit" here, which is the one place this reads
264    /// differently from `maxmemory` and is the difference that makes a drop in
265    /// cache possible. A storage budget of zero bytes means nothing may live on
266    /// the file, so migration cannot make room and eviction is the only thing
267    /// left, which is Redis exactly. `None` is no limit and is the default,
268    /// which with `noeviction` means the database grows until the disk is full
269    /// and then writes fail, which is what a database does.
270    maxstore: Option<u64>,
271    /// What [`Server::memory_bytes`] said at the last maintenance turn.
272    ///
273    /// The reading is a walk over every collection in every database and cannot
274    /// go on a command path, so the command path reads this instead and is at
275    /// most one batch behind. What that costs is overshoot: a server can end a
276    /// batch holding one batch's worth of allocation more than its limit before
277    /// anything notices. A batch is 64 commands, so that is bounded by what 64
278    /// commands can allocate and not by how long the server runs.
279    ///
280    /// Only kept up to date when there is a limit to judge it against. A server
281    /// with no `maxmemory` never reads it and never pays for it.
282    used: usize,
283    /// Which database the next eviction draws from.
284    ///
285    /// Its own cursor and not [`Server::next_db`], because eviction and
286    /// compaction move at different rates and sharing one would make the
287    /// database that gets compacted depend on how many keys were evicted.
288    evict_db: usize,
289    /// Which database the next active expiry sweep starts at.
290    ///
291    /// A third cursor for the same reason there is a second one. A sweep runs on
292    /// every turn of the loop and compaction runs when there is dead space, so
293    /// sharing a cursor would make which database gets swept depend on which one
294    /// was last collected.
295    expire_db: usize,
296    /// The millisecond the last active expiry sweep ran on, so the next one on
297    /// the same millisecond does not bother.
298    expire_ms: u64,
299    /// Clients parked on a blocking command.
300    waiters: Waiters,
301    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
302    ///
303    /// Empty on a server nobody has migrated a key out of, which is nearly all
304    /// of them, and it costs a vector's three words to be empty.
305    peers: migrate::Peers,
306    /// The numbers the reactor keeps for `INFO`.
307    pub stats: Stats,
308    /// A counter per command, for `INFO commandstats`.
309    cmdstats: CommandStats,
310}
311
312impl Server {
313    /// A server with [`DATABASES`] empty databases on the system clock.
314    #[must_use]
315    pub fn new() -> Server {
316        let clock = Clock::system();
317        Server {
318            dbs: (0..DATABASES)
319                .map(|_| Keyspace::with_clock(clock))
320                .collect(),
321            clock,
322            started_ms: clock.now_ms(),
323            next_db: 0,
324            dirty: ALL_DATABASES,
325            conn_bytes: 0,
326            maxmemory: 0,
327            store: None,
328            maxstore: None,
329            used: 0,
330            evict_db: 0,
331            expire_db: 0,
332            expire_ms: 0,
333            waiters: Waiters::default(),
334            peers: migrate::Peers::default(),
335            stats: Stats::default(),
336            cmdstats: CommandStats::default(),
337        }
338    }
339
340    /// A server on a clock the caller moves by hand, for tests.
341    #[must_use]
342    pub fn with_clock(clock: Clock) -> Server {
343        Server {
344            dbs: (0..DATABASES)
345                .map(|_| Keyspace::with_clock(clock))
346                .collect(),
347            clock,
348            started_ms: clock.now_ms(),
349            next_db: 0,
350            dirty: ALL_DATABASES,
351            conn_bytes: 0,
352            maxmemory: 0,
353            store: None,
354            maxstore: None,
355            used: 0,
356            evict_db: 0,
357            expire_db: 0,
358            expire_ms: 0,
359            waiters: Waiters::default(),
360            peers: migrate::Peers::default(),
361            stats: Stats::default(),
362            cmdstats: CommandStats::default(),
363        }
364    }
365
366    /// One database, by index.
367    ///
368    /// # Panics
369    ///
370    /// If `i` is not a database. `SELECT` is the only way a client changes the
371    /// index and it checks, so an index that is out of range here is a bug in
372    /// the caller and not something a client can ask for.
373    pub fn db(&mut self, i: usize) -> &mut Keyspace {
374        // The borrow is mutable, so assume it is used. Anything that only reads
375        // has [`Server::db_ref`] and does not come through here.
376        self.dirty |= 1u64 << i;
377        &mut self.dbs[i]
378    }
379
380    /// One database, by index, without taking it mutably.
381    ///
382    /// What the prefetch stage needs. It runs for all 64 commands in a batch
383    /// before any of them executes, so it cannot hold the mutable borrow `run`
384    /// is about to want, and it does not need one: warming a cache line reads
385    /// nothing and changes nothing.
386    ///
387    /// # Panics
388    ///
389    /// As [`Server::db`].
390    #[must_use]
391    pub fn db_ref(&self, i: usize) -> &Keyspace {
392        &self.dbs[i]
393    }
394
395    /// Take a new clock reading and give it to every database.
396    ///
397    /// Once per turn of the event loop, which is the only place time moves. A
398    /// command asking what the time is gets the answer the whole batch got, so
399    /// two keys written by the same batch expire together (`04` section 3).
400    pub fn refresh_clock(&mut self) {
401        self.clock.refresh();
402        let now = self.clock.now_ms();
403        for db in &mut self.dbs {
404            db.clock_mut().set(now);
405        }
406    }
407
408    /// Move every clock here to `ms` by hand, for tests about expiry.
409    ///
410    /// A test cannot wait a hundred seconds and a test that waits a hundred
411    /// milliseconds is a test that fails on a loaded machine, so time moves on
412    /// request. The system clock underneath will overwrite this on the next
413    /// [`Server::refresh_clock`], which is why this is only useful in a test
414    /// that drives commands directly rather than through the event loop.
415    pub fn set_clock_ms(&mut self, ms: u64) {
416        self.clock.set(ms);
417        for db in &mut self.dbs {
418            db.clock_mut().set(ms);
419        }
420    }
421
422    /// Seconds since this server was built.
423    #[must_use]
424    pub fn uptime_secs(&self) -> u64 {
425        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
426    }
427
428    /// Bytes held by every database's index and arena, plus the read and reply
429    /// buffers of every connection.
430    ///
431    /// The buffers are in here because they are real and because Redis counts
432    /// its own, so leaving them out would make the one number people compare
433    /// flattering rather than true. They are not a database, so nothing in the
434    /// keyspace can change them and the engine has to say when they move.
435    #[must_use]
436    pub fn memory_bytes(&self) -> usize {
437        self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
438    }
439
440    /// What the keyspace itself is holding, live records only.
441    ///
442    /// `used_memory` minus this is what the store costs to run: the index, the
443    /// space dead records are sitting in until compaction gets to them, and the
444    /// connections' buffers.
445    #[must_use]
446    pub fn dataset_bytes(&self) -> usize {
447        self.dbs
448            .iter()
449            .map(|db| db.map().arena().live_bytes() as usize)
450            .sum()
451    }
452
453    /// Bytes the arenas are holding, live and dead together.
454    #[must_use]
455    pub fn arena_bytes(&self) -> usize {
456        self.dbs
457            .iter()
458            .map(|db| db.map().arena().reserved_bytes() as usize)
459            .sum()
460    }
461
462    /// Bytes the indexes are holding.
463    #[must_use]
464    pub fn index_bytes(&self) -> usize {
465        self.dbs
466            .iter()
467            .map(|db| db.map().index().memory_bytes())
468            .sum()
469    }
470
471    /// Arena segments whose pages are real, across every database.
472    #[must_use]
473    pub fn segment_count(&self) -> usize {
474        self.dbs
475            .iter()
476            .map(|db| db.map().arena().resident_segments())
477            .sum()
478    }
479
480    /// What the connections' read and reply buffers are holding.
481    #[must_use]
482    pub const fn conn_bytes(&self) -> usize {
483        self.conn_bytes
484    }
485
486    /// Note that the connections are holding `delta` bytes more than they were,
487    /// or fewer when it is negative.
488    ///
489    /// A delta and not a total because the alternative is a walk over every
490    /// connection, and the walk would have to happen on a turn of the loop
491    /// rather than when `INFO` asks, which puts the cost of a report on the
492    /// command path of a server nobody is asking.
493    pub fn note_conn_bytes(&mut self, delta: isize) {
494        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
495    }
496
497    /// Keys reclaimed by running into them after their deadline.
498    #[must_use]
499    pub fn expired_keys(&self) -> u64 {
500        self.dbs.iter().map(Keyspace::expired_keys).sum()
501    }
502
503    /// Keys thrown away to make room, which is the other number entirely.
504    #[must_use]
505    pub fn evicted_keys(&self) -> u64 {
506        self.dbs.iter().map(Keyspace::evicted_keys).sum()
507    }
508
509    /// Every command that has been seen, with its counters.
510    ///
511    /// Only the ones that have. A server reports a handful of lines rather than
512    /// one per command in the table, which is what Redis does and is the
513    /// difference between a section a person can read and one they cannot.
514    pub fn command_stats(&self) -> impl Iterator<Item = (&'static str, CommandStat)> {
515        self.cmdstats
516            .0
517            .iter()
518            .enumerate()
519            .filter(|(_, row)| row.seen())
520            .map(|(at, row)| (table::name_at(at), *row))
521    }
522
523    /// The `maxmemory` limit in bytes, zero when there is not one.
524    #[must_use]
525    pub const fn maxmemory(&self) -> u64 {
526        self.maxmemory
527    }
528
529    /// Set the limit, and take a reading straight away.
530    ///
531    /// The reading is here rather than left to the next maintenance turn because
532    /// a client that sets the limit and sends a write in the same batch expects
533    /// the write to be judged against the limit it just set, and because the
534    /// cached number is meaningless until the first time there is a limit to
535    /// compare it with.
536    ///
537    /// Turning the limit on also turns on the running total every slab keeps of
538    /// what its collections hold, and turning it off turns that back off, so a
539    /// server with no limit is not paying to count something nobody reads. The
540    /// first reading after switching it on is the walk that the total starts
541    /// from, and it is the only walk.
542    pub fn set_maxmemory(&mut self, bytes: u64) {
543        self.maxmemory = bytes;
544        for db in &mut self.dbs {
545            db.track_memory(bytes != 0);
546        }
547        self.used = self.settled_memory();
548    }
549
550    /// Say where a database should get its store from when it needs one.
551    ///
552    /// This is what turns the eviction inversion on. Until it is called every
553    /// database answers a memory limit by evicting, which is Redis, and after it
554    /// is called a database under memory pressure moves values to whatever the
555    /// closure hands back instead of throwing keys away.
556    ///
557    /// Called at most once per database and only under pressure, so a server
558    /// that is given a file and never fills memory never touches it.
559    pub fn set_store_source(
560        &mut self,
561        source: impl FnMut(usize) -> Option<Box<dyn Blocks>> + 'static,
562    ) {
563        self.store = Some(Box::new(source));
564    }
565
566    /// Whether this server has been given somewhere to put cold values.
567    #[must_use]
568    pub const fn has_store_source(&self) -> bool {
569        self.store.is_some()
570    }
571
572    /// Open database `at`'s store, if it has not got one and there is one to be
573    /// had.
574    ///
575    /// A store that will not open leaves the database where it was, which is
576    /// evicting, because a memory limit that cannot be answered by moving data
577    /// still has to be answered.
578    fn attach_store(&mut self, at: usize) {
579        if self.dbs[at].store_bytes().is_some() {
580            return;
581        }
582        let Some(source) = self.store.as_mut() else {
583            return;
584        };
585        if let Some(blocks) = source(at) {
586            self.dbs[at].attach(blocks);
587        }
588    }
589
590    /// The `maxstore` limit in bytes, `None` when there is not one.
591    #[must_use]
592    pub const fn maxstore(&self) -> Option<u64> {
593        self.maxstore
594    }
595
596    /// Set the storage limit, or clear it with `None`.
597    ///
598    /// Nothing is read here the way [`Server::set_maxmemory`] reads the memory
599    /// total, because this limit is compared against a number the store keeps
600    /// and answers on demand, not against a walk.
601    pub const fn set_maxstore(&mut self, bytes: Option<u64>) {
602        self.maxstore = bytes;
603    }
604
605    /// What every attached store is holding, for `INFO memory`.
606    ///
607    /// Zero on a server with nothing attached, which is not the same as a server
608    /// whose file is empty, and [`Server::regime`] is the field that tells those
609    /// two apart.
610    #[must_use]
611    pub fn store_bytes(&self) -> u64 {
612        self.dbs.iter().filter_map(Keyspace::store_bytes).sum()
613    }
614
615    /// What the file has been asked to do, added up over every database.
616    ///
617    /// Counters and not levels, so they only ever go up and a run is the
618    /// difference between two readings. G9 is a ratio over these: the faults a
619    /// run took, divided by the point reads it issued, has to come out at 1.05
620    /// or less with a working set ten times memory. There is no way to work that
621    /// out from outside the server, so it is reported rather than inferred.
622    ///
623    /// A fault is a read that went to the store. Whether it also went to the
624    /// device depends on the store: a log serves a read out of a resident page
625    /// without touching anything. At ten times memory almost every fault is a
626    /// real read, which is why the gate is written against this number, but the
627    /// two are not the same thing and a run tight against the bar should be
628    /// checked against what the operating system says.
629    #[must_use]
630    pub fn cold_stats(&self) -> yo_kv::tier::Stats {
631        let mut total = yo_kv::tier::Stats::default();
632        for db in &self.dbs {
633            let Some(tier) = db.tier() else { continue };
634            let s = tier.stats();
635            total.demoted += s.demoted;
636            total.promoted += s.promoted;
637            total.faults += s.faults;
638            total.served += s.served;
639            total.bytes_out += s.bytes_out;
640            total.bytes_in += s.bytes_in;
641        }
642        total
643    }
644
645    /// Which way this server answers a memory limit, in one word for `INFO`.
646    ///
647    /// `evict` is Redis: a memory limit throws keys away. `migrate` is the
648    /// inversion: a memory limit moves values to the file and nothing stored is
649    /// lost. A server reports one word rather than leaving an operator to work
650    /// it out from a limit, a setting and whether a file happens to be open.
651    #[must_use]
652    pub fn regime(&self) -> &'static str {
653        if (0..self.dbs.len()).any(|at| self.migrates(at)) {
654            "migrate"
655        } else {
656            "evict"
657        }
658    }
659
660    /// Whether database `at` answers a memory limit by moving values to the
661    /// file rather than by throwing keys away.
662    ///
663    /// Three things have to hold. There has to be somewhere to move them, which
664    /// is a store attached to that database or a source that can open one, and
665    /// on a server that was never given a file this is false everywhere and
666    /// every database behaves exactly as it did.
667    /// The storage budget has to be more than nothing, which is what
668    /// `maxstore 0` says it is not. And the file has to be under that budget,
669    /// because a full file is a storage limit reached and eviction is the right
670    /// answer to a storage limit.
671    fn migrates(&self, at: usize) -> bool {
672        if self.maxstore == Some(0) {
673            return false;
674        }
675        match self.dbs[at].store_bytes() {
676            Some(held) => self.maxstore.is_none_or(|cap| held < cap),
677            // Nothing attached, but somewhere to get one from the moment this
678            // database needs it, which is what makes the answer yes rather than
679            // no. Opening it here would mean `INFO` opened files.
680            None => self.store.is_some(),
681        }
682    }
683
684    /// Take a fresh memory reading, which the maintenance turn does once a batch.
685    ///
686    /// Nothing at all when there is no limit, which is the default and is every
687    /// server that has not asked for one.
688    pub fn refresh_memory(&mut self) {
689        if self.maxmemory != 0 {
690            self.used = self.settled_memory();
691        }
692    }
693
694    /// [`Server::memory_bytes`], asked the cheap way.
695    ///
696    /// The same number. The difference is that this asks each database only
697    /// about the collections that could have moved since the last time, which is
698    /// what a batch touched rather than what the server holds, so it can be
699    /// asked once a batch and again on every command that is over the limit.
700    fn settled_memory(&mut self) -> usize {
701        self.dbs
702            .iter_mut()
703            .map(Keyspace::settled_memory_bytes)
704            .sum::<usize>()
705            + self.conn_bytes
706    }
707
708    /// Make room under the `maxmemory` limit, throwing keys away if that is what
709    /// it takes. Answers whether there is anything left it could throw away.
710    ///
711    /// Redis runs the same thing from `processCommand` before every command and
712    /// so does this: a client that writes has to be judged at the moment it
713    /// writes, not a batch later, or the limit is a suggestion.
714    ///
715    /// Three things happen in the loop and all three are needed. Eviction picks
716    /// a key and drops it. Compaction gives the pages back, because dropping a
717    /// key marks its record dead and returns nothing on its own, so a loop that
718    /// only evicted would throw the whole keyspace away and watch the number
719    /// stay where it was. The reading is taken again each time round, because
720    /// the two of them together are the only thing that moves it.
721    ///
722    /// # Why running out of budget is not a no
723    ///
724    /// `false` means there was nothing left to evict, which is `noeviction`, or
725    /// a `volatile` policy on a database where nothing has a deadline, or a
726    /// keyspace that is already empty. It does not mean the server is still over
727    /// its limit, and that difference is Redis's: `performEvictions` answers
728    /// `EVICT_FAIL` only when it has run out of things to delete, and
729    /// `processCommand` refuses the client on that and on nothing else. Running
730    /// out of time part way through a job it is doing well comes back as
731    /// `EVICT_RUNNING` and the command goes through, because a server that is
732    /// evicting steadily and refusing every write while it does it is worse for
733    /// the client than a little overshoot.
734    ///
735    /// # What the limit is worth
736    ///
737    /// Space comes back a segment at a time and a segment is two megabytes, so
738    /// this holds a server to its limit give or take a segment. A `maxmemory` of
739    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
740    /// megabytes is asking for a precision this store does not have.
741    pub fn make_room(&mut self) -> bool {
742        if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
743            return true;
744        }
745        // The cached reading is a batch old and the batch may have compacted
746        // since, so take a fresh one before throwing anything away. It is the
747        // settled reading and not the walk, so what this costs is the handful of
748        // collections the last batch touched and not the whole database.
749        self.used = self.settled_memory();
750        let mut budget = EVICT_BUDGET;
751        while self.used as u64 > self.maxmemory {
752            let over = self.used - self.maxmemory as usize;
753            if !self.relieve_step(over) {
754                return false;
755            }
756            self.compact_hard_step();
757            self.used = self.settled_memory();
758            budget -= 1;
759            if budget == 0 {
760                break;
761            }
762        }
763        true
764    }
765
766    /// Give back `over` bytes from whichever database can, by moving values to
767    /// the file where there is one and by throwing keys away where there is not.
768    ///
769    /// The two answers are the eviction inversion and which one a database gets
770    /// is [`Server::migrates`]. Answers whether anything was given back at all,
771    /// and `false` is what refuses the client's write.
772    ///
773    /// A store that will not take the bytes counts as nothing given back, so the
774    /// write is refused rather than turned into a deletion. A disk that is
775    /// misbehaving is a reason to stop accepting writes and it is not a reason
776    /// to start losing data that was accepted already.
777    ///
778    /// Round robin from a cursor rather than always starting at database zero,
779    /// so a server using more than one of them does not empty the first before
780    /// touching the second. Almost every server is on database zero only, where
781    /// this is one call that answers and fifteen that say the map is empty.
782    fn relieve_step(&mut self, over: usize) -> bool {
783        for turn in 0..self.dbs.len() {
784            let i = (self.evict_db + turn) % self.dbs.len();
785            // An empty database has nothing to move and opening a log for one
786            // would cost a resident page window to find that out.
787            let gave = if !self.dbs[i].is_empty() && self.migrates(i) {
788                self.attach_store(i);
789                // Whether it made room and not whether it moved a key. A round
790                // that demoted nothing and handed back a segment is a round
791                // that made room, and reading only the count refuses the write
792                // that provoked it.
793                self.dbs[i]
794                    .relieve(over)
795                    .is_ok_and(yo_kv::tier::Relief::made_room)
796            } else {
797                self.dbs[i].evict_one()
798            };
799            if gave {
800                self.evict_db = (i + 1) % self.dbs.len();
801                self.dirty |= 1u64 << i;
802                return true;
803            }
804        }
805        false
806    }
807
808    /// The sweep the shard loop calls, at most once a millisecond.
809    ///
810    /// The gate is the whole difference between this and [`Server::expire_step`].
811    /// A maintenance slice runs on every turn of the loop and a turn is a
812    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
813    /// thousand times per millisecond and spend a real share of the shard on
814    /// looking for keys that cannot have died since the last look. Nothing in a
815    /// database changes fast enough to be worth asking about more often than the
816    /// clock can tell the difference, and the clock here is milliseconds.
817    ///
818    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
819    /// hertz, so this is not the thing that decides how promptly memory comes
820    /// back. What it decides is that an idle server sweeps a thousand times a
821    /// second rather than a million.
822    pub fn expire_slice(&mut self, budget: usize) -> usize {
823        let now = self.clock.now_ms();
824        if now == self.expire_ms {
825            return 0;
826        }
827        self.expire_ms = now;
828        self.expire_step(budget)
829    }
830
831    /// Sweep dead keys out of the databases, spending at most `budget` looks.
832    ///
833    /// Answers what it spent, so the caller can charge its maintenance slice for
834    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
835    ///
836    /// Round robin from its own cursor, and every database gets offered whatever
837    /// is left of the budget rather than a sixteenth of it each, so a server on
838    /// database zero only, which is nearly every server, spends the whole slice
839    /// where the keys are. The fifteen empty ones cost a comparison apiece
840    /// because a database with no key carrying a deadline says so without
841    /// drawing anything.
842    ///
843    /// The cursor moves to the database after whichever one did the work, so two
844    /// busy databases take turns instead of the lower numbered one starving the
845    /// other.
846    pub fn expire_step(&mut self, budget: usize) -> usize {
847        let mut spent = 0;
848        for turn in 0..self.dbs.len() {
849            if spent >= budget {
850                break;
851            }
852            let i = (self.expire_db + turn) % self.dbs.len();
853            let c = self.dbs[i].expire_cycle(budget - spent);
854            spent += c.examined;
855            if c.expired > 0 {
856                self.expire_db = (i + 1) % self.dbs.len();
857                self.dirty |= 1u64 << i;
858            }
859        }
860        spent
861    }
862
863    /// One slice of compaction for a server that is over its limit.
864    ///
865    /// Takes the databases in the same order [`Server::compact_step`] does and
866    /// stops at the first one that had something to move, and it asks with the
867    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
868    fn compact_hard_step(&mut self) -> Option<usize> {
869        for turn in 0..self.dbs.len() {
870            let i = (self.next_db + turn) % self.dbs.len();
871            if let Some(moved) = self.dbs[i].compact_hard() {
872                self.next_db = (i + 1) % self.dbs.len();
873                return Some(moved);
874            }
875        }
876        None
877    }
878
879    /// Give one database's dead space back, if any database has enough of it to
880    /// be worth the move. `None` when no database had a candidate.
881    ///
882    /// Once per batch, next to the clock. Overwriting a key writes a new record
883    /// and counts the old one dead, so without this a server holds everything
884    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
885    /// a key against Redis at 144 for the same load, and the whole difference
886    /// was dead records nothing ever came back for.
887    ///
888    /// At most one segment moves per call and the search starts one database
889    /// further along each time, so the cost of asking is a comparison per
890    /// database and the cost of acting is bounded by a segment.
891    pub fn compact_step(&mut self) -> Option<usize> {
892        for turn in 0..self.dbs.len() {
893            let i = (self.next_db + turn) % self.dbs.len();
894            // Nothing has run against this database since it last said it had
895            // nothing to collect, so it still has nothing to collect and the
896            // line it lives on stays where it is.
897            if self.dirty & (1 << i) == 0 {
898                continue;
899            }
900            if let Some(moved) = self.dbs[i].compact_step() {
901                self.next_db = (i + 1) % self.dbs.len();
902                return Some(moved);
903            }
904            self.dirty &= !(1u64 << i);
905        }
906        None
907    }
908}
909
910impl Default for Server {
911    fn default() -> Server {
912        Server::new()
913    }
914}
915
916/// What one connection has chosen.
917pub struct Session {
918    db: usize,
919    id: u64,
920    name: Vec<u8>,
921}
922
923impl Session {
924    /// A new connection, on database zero with no name.
925    #[must_use]
926    pub fn new(id: u64) -> Session {
927        Session {
928            db: 0,
929            id,
930            name: Vec::new(),
931        }
932    }
933
934    /// The connection id, which `HELLO` reports and `CLIENT` will.
935    #[must_use]
936    pub const fn id(&self) -> u64 {
937        self.id
938    }
939
940    /// Which database this connection is working in.
941    #[must_use]
942    pub const fn db(&self) -> usize {
943        self.db
944    }
945
946    /// The name the client gave itself, empty if it gave none.
947    #[must_use]
948    pub fn name(&self) -> &[u8] {
949        &self.name
950    }
951
952    /// Put everything back the way it was when the connection was opened.
953    ///
954    /// The protocol is not here because it is not here: it lives in the reply
955    /// buffer, and `RESET` sets it back there.
956    pub fn reset(&mut self) {
957        self.db = 0;
958        self.name.clear();
959    }
960
961    /// Record the name from `HELLO ... SETNAME`.
962    fn set_name(&mut self, name: &[u8]) {
963        yo_alloc::allow(|| {
964            self.name.clear();
965            self.name.extend_from_slice(name);
966        });
967    }
968}
969
970/// Run one command and write its reply.
971///
972/// The name is looked up and the arity is checked here, once, so that no body
973/// has to. Everything after that is the command's own.
974pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
975    // The decoder never produces a command with no name. If one ever arrives,
976    // it is not something to answer.
977    if args.is_empty() {
978        return Flow::Continue;
979    }
980    resolved(server, session, lookup(args.name()), args, out)
981}
982
983/// The same, for a caller that has already found the command.
984///
985/// The engine frames a command before it runs it, and between those two it also
986/// asks which key the command touches so the record can be prefetched. That is
987/// two more chances to look the name up, and looking it up three times to run it
988/// once is three times the cost of the cheapest thing in the path. So the engine
989/// resolves the name where it frames the command, carries the answer on the
990/// framed command, and both the other two take it from there.
991///
992/// `spec` is `None` for a name that is not a command, which is the same thing
993/// [`lookup`] says and lands in the same reply.
994pub fn resolved(
995    server: &mut Server,
996    session: &mut Session,
997    spec: Option<&'static Spec>,
998    args: Args<'_>,
999    out: &mut Out,
1000) -> Flow {
1001    if args.is_empty() {
1002        return Flow::Continue;
1003    }
1004    server.stats.commands += 1;
1005
1006    let Some(spec) = spec else {
1007        write_error(out, &args::unknown_command(args));
1008        return Flow::Continue;
1009    };
1010    if !arity_ok(spec, args.len()) {
1011        server.cmdstats.at(spec).rejected += 1;
1012        write_error(out, &args::wrong_arity(spec.name));
1013        return Flow::Continue;
1014    }
1015
1016    // The limit first, so a server with no `maxmemory`, which is the default and
1017    // is nearly all of them, pays one comparison against a field that is already
1018    // warm. Every command and not only the writes, because that is where Redis
1019    // puts it: making room is the server's job whatever the client asked for,
1020    // and the flag only decides who gets told no when there is no room to make.
1021    //
1022    // The flag is Redis's own `denyoom` and the list of commands carrying it is
1023    // Redis's list, so a command that only frees is let through with nothing
1024    // left, which is what lets a client dig itself out with `DEL`.
1025    if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
1026        server.cmdstats.at(spec).rejected += 1;
1027        out.error_line(b"OOM ", OOM);
1028        return Flow::Continue;
1029    }
1030
1031    // Which databases the maintenance turn after this batch has to ask. Marked
1032    // for every command and not only for the writes, because a read can make
1033    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
1034    // record it dropped is exactly the kind of thing the collector is for.
1035    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
1036    // two groups that hold them mark all of them rather than the session's.
1037    server.dirty |= match spec.group {
1038        "string" | "bitmap" | "hyperloglog" | "geo" | "set" | "hash" | "list" | "zset"
1039        | "array" | "stream" => 1u64 << session.db,
1040        _ => ALL_DATABASES,
1041    };
1042
1043    let mark = out.len();
1044    // Before the group, because the five that block are list commands and would
1045    // otherwise land in `lists`, which is handed one database and nothing that
1046    // could park a client. The flag is the right thing to branch on rather than
1047    // a list of names: it is what `COMMAND INFO` reports about exactly these
1048    // commands, and the sorted set and stream ones that arrive later carry it
1049    // too.
1050    let done = if spec.flags.contains(&"blocking") {
1051        blocking::execute(server, session, spec, args, out)
1052    } else {
1053        match spec.group {
1054            "string" => {
1055                let db = session.db;
1056                strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1057            }
1058            // Its own group and its own file, and the same values underneath:
1059            // a bitmap is a string, so `STRLEN` on one answers and `SETBIT` on
1060            // something a `SET` left behind works.
1061            "bitmap" => {
1062                let db = session.db;
1063                bits::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1064            }
1065            // The same again: a sketch is a string with a documented layout, so
1066            // `GET` hands one to a client and `SET` takes it back.
1067            "hyperloglog" => {
1068                let db = session.db;
1069                hll::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1070            }
1071            "set" => {
1072                let db = session.db;
1073                sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1074            }
1075            "hash" => {
1076                let db = session.db;
1077                hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1078            }
1079            "list" => {
1080                let db = session.db;
1081                lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1082            }
1083            "zset" => {
1084                let db = session.db;
1085                zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1086            }
1087            // A geo key is a sorted set and these are sorted set commands with
1088            // arithmetic on the way in and on the way out, so a client can ZREM
1089            // a place out of one and ZCARD it to count them.
1090            "geo" => {
1091                let db = session.db;
1092                geo::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1093            }
1094            "array" => {
1095                let db = session.db;
1096                arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1097            }
1098            "graph" => {
1099                let db = session.db;
1100                graph::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
1101            }
1102            // The clock is read before the database is borrowed, because every
1103            // stream command needs the time and it lives on the server. An
1104            // `XADD` with no ID, an `XCLAIM` working out what is idle and an
1105            // `XINFO` reporting it all have to agree about what moment this is.
1106            "stream" => {
1107                let db = session.db;
1108                let now = server.now_ms();
1109                streams::execute(&mut server.dbs[db], spec, args, now, out).map(|()| Flow::Continue)
1110            }
1111            // The one keyspace command that needs more than the databases,
1112            // because the socket it talks down is held on the server between
1113            // commands and not opened again for each one.
1114            "keyspace" if spec.name == "migrate" => {
1115                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
1116            }
1117            // Every database and not the one the session is on, because `COPY` takes
1118            // a `DB n` and writes into a database nobody selected.
1119            "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
1120                .map(|()| Flow::Continue),
1121            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
1122            _ => server::execute(server, session, spec, args, out),
1123        }
1124    };
1125    let flow = match done {
1126        Ok(flow) => flow,
1127        Err(e) => {
1128            out.truncate(mark);
1129            write_error(out, &e);
1130            Flow::Continue
1131        }
1132    };
1133
1134    // Counted here and not before the call, which is where Redis counts it, so
1135    // that `INFO commandstats` leaves out the `INFO` that asked for it in the
1136    // same way theirs does.
1137    //
1138    // Failure is read off the reply rather than off the `Result`, because the
1139    // two are not the same set. A command that ran out of arguments comes back
1140    // as an `Err` and a command that was sent the wrong password writes its own
1141    // error line and comes back `Ok`, and both of those are a call that failed.
1142    // The first byte at the mark is what a client would branch on, and it is `-`
1143    // for an error on either protocol and `!` for RESP3's long form.
1144    let row = server.cmdstats.at(spec);
1145    row.calls += 1;
1146    if matches!(out.as_slice().get(mark), Some(b'-' | b'!')) {
1147        row.failed += 1;
1148    }
1149    flow
1150}
1151
1152/// The error line for an error value.
1153///
1154/// The prefix is what a client branches on, and there are three of them:
1155/// `WRONGTYPE` for a command sent at the wrong kind of value, `INVALIDOBJ` for a
1156/// HyperLogLog whose opcodes do not add up, and `ERR` for everything else. The three errors that need a different one,
1157/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
1158/// than routed through here. `OOM` is not a [`Code`] of its own because
1159/// [`Code::Full`] already covers the string that is too long for
1160/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
1161fn write_error(out: &mut Out, e: &Error) {
1162    let prefix: &[u8] = match e.code() {
1163        Code::WrongType => b"WRONGTYPE ",
1164        // Only the HyperLogLog commands answer this one, and the prefix is the
1165        // sentence a client branches on to tell a sketch it cannot read from a
1166        // sketch it sent wrong.
1167        Code::Corrupt => b"INVALIDOBJ ",
1168        _ => b"ERR ",
1169    };
1170    out.error_line(prefix, e.message().as_bytes());
1171}
1172
1173#[cfg(test)]
1174mod tests {
1175    use super::*;
1176    use crate::proto::{Limits, Proto};
1177    use crate::request::Argv;
1178
1179    /// Build the wire bytes for a command.
1180    ///
1181    /// Tests go through the codec rather than around it, so an argument in a
1182    /// test is the same borrowed slice a connection produces.
1183    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
1184        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
1185        for p in parts {
1186            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
1187            wire.extend_from_slice(p);
1188            wire.extend_from_slice(b"\r\n");
1189        }
1190        wire
1191    }
1192
1193    /// A server, a connection and a buffer, driven the way the reactor will.
1194    struct Fixture {
1195        server: Server,
1196        session: Session,
1197        argv: Argv,
1198        out: Out,
1199    }
1200
1201    impl Fixture {
1202        fn new() -> Fixture {
1203            Fixture {
1204                server: Server::new(),
1205                session: Session::new(7),
1206                argv: Argv::new(),
1207                out: Out::new(Proto::Resp2),
1208            }
1209        }
1210
1211        /// Run one command and answer with the bytes it wrote.
1212        fn run(&mut self, parts: &[&[u8]]) -> String {
1213            self.flow(parts).1
1214        }
1215
1216        /// Run one command and answer with the bytes exactly as written.
1217        ///
1218        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
1219        /// every reply that is text and destroys a `DUMP` payload, since a
1220        /// payload is arbitrary bytes and a checksum on the end of them.
1221        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
1222            let wire = encode(parts);
1223            self.argv.decode(&wire, &Limits::default()).unwrap();
1224            self.out.clear();
1225            execute(
1226                &mut self.server,
1227                &mut self.session,
1228                Args::new(&self.argv, &wire),
1229                &mut self.out,
1230            );
1231            self.out.as_slice().to_vec()
1232        }
1233
1234        /// Move every clock in the server on by `ms`.
1235        fn advance(&mut self, ms: u64) {
1236            for db in 0..DATABASES {
1237                self.server.db(db).clock_mut().advance(ms);
1238            }
1239        }
1240
1241        /// The same, with what the connection should do next.
1242        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
1243            let wire = encode(parts);
1244            self.argv.decode(&wire, &Limits::default()).unwrap();
1245            self.out.clear();
1246            let flow = execute(
1247                &mut self.server,
1248                &mut self.session,
1249                Args::new(&self.argv, &wire),
1250                &mut self.out,
1251            );
1252            (
1253                flow,
1254                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
1255            )
1256        }
1257    }
1258
1259    /// What a client does all day: write the same keys again and again. Every
1260    /// one of those writes leaves the previous record behind, so a server that
1261    /// never compacts holds every version of every key it has ever been sent.
1262    #[test]
1263    fn rewriting_the_same_keys_does_not_grow_the_server() {
1264        let mut f = Fixture::new();
1265        let val = vec![b'v'; 1024];
1266        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1267
1268        for k in &keys {
1269            f.run(&[b"SET", k, &val]);
1270        }
1271        f.server.compact_step();
1272        let after_first = f.server.memory_bytes();
1273
1274        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
1275        // of it. Thirty two megabytes written to hold sixty four kilobytes,
1276        // which is the shape of a real workload and is enough churn to fill
1277        // sixteen segments if nothing ever comes back.
1278        for _ in 0..500 {
1279            for k in &keys {
1280                f.run(&[b"SET", k, &val]);
1281            }
1282            f.server.compact_step();
1283        }
1284
1285        assert!(
1286            f.server.memory_bytes() <= after_first * 2,
1287            "held {} after five hundred passes against {after_first} after one",
1288            f.server.memory_bytes()
1289        );
1290        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1291        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1292    }
1293
1294    /// The same churn on a database nobody starts on, either side of a quiet
1295    /// spell long enough for the maintenance turn to stop asking about it.
1296    ///
1297    /// The turn after each batch skips a database that has already said it has
1298    /// nothing to collect and has not been touched since, which is what keeps a
1299    /// server whose clients are all on database zero from loading and storing
1300    /// in the other fifteen every batch to be told no. Two things could go
1301    /// wrong with that. A database might never be marked at all, so this uses
1302    /// database nine, which nothing marks by accident. And a database whose
1303    /// mark was cleared might never get it back, so this drains the collector
1304    /// until it says there is nothing left, checks the mark really is gone, and
1305    /// then writes another thirty two megabytes through the same sixty four
1306    /// keys. If either went wrong the server would hold all of it.
1307    #[test]
1308    fn a_database_nobody_started_on_is_still_collected() {
1309        let mut f = Fixture::new();
1310        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
1311        let val = vec![b'v'; 1024];
1312        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
1313
1314        for k in &keys {
1315            f.run(&[b"SET", k, &val]);
1316        }
1317        while f.server.compact_step().is_some() {}
1318        assert_eq!(
1319            f.server.dirty & (1 << 9),
1320            0,
1321            "database nine was drained and should not be asked again until it is written to"
1322        );
1323        let after_first = f.server.memory_bytes();
1324
1325        for _ in 0..500 {
1326            for k in &keys {
1327                f.run(&[b"SET", k, &val]);
1328            }
1329            f.server.compact_step();
1330        }
1331
1332        assert!(
1333            f.server.memory_bytes() <= after_first * 2,
1334            "held {} after five hundred passes against {after_first} after one",
1335            f.server.memory_bytes()
1336        );
1337        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
1338        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
1339        // And nothing landed anywhere else on the way.
1340        f.run(&[b"SELECT", b"0"]);
1341        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1342    }
1343
1344    #[test]
1345    fn a_command_goes_from_bytes_to_bytes() {
1346        let mut f = Fixture::new();
1347        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1348        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
1349        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1350        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
1351        // The name is matched whatever case it came in, and so are the options.
1352        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
1353        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1354    }
1355
1356    #[test]
1357    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1358        let mut f = Fixture::new();
1359        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1360        // A key named twice exists twice and can only be deleted once, and both
1361        // of those are Redis's answers rather than tidier ones.
1362        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1363        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1364        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1365        // UNLINK is the same body and reports the same way.
1366        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1367        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1368    }
1369
1370    #[test]
1371    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1372        let mut f = Fixture::new();
1373        f.run(&[b"SET", b"k", b"v"]);
1374        // A simple string on both protocols, which is unusual: most replies
1375        // that carry a word are bulk strings.
1376        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1377        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1378    }
1379
1380    #[test]
1381    fn touch_counts_the_way_exists_counts() {
1382        let mut f = Fixture::new();
1383        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1384        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1385        assert_eq!(
1386            f.run(&[b"TOUCH", b"a", b"a"]),
1387            ":2\r\n",
1388            "twice counts twice"
1389        );
1390        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1391        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1392    }
1393
1394    #[test]
1395    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1396        let mut f = Fixture::new();
1397        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1398        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1399
1400        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1401        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1402        assert_eq!(
1403            f.run(&[b"TTL", b"b"]),
1404            ":100\r\n",
1405            "the source's and not b's"
1406        );
1407        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1408    }
1409
1410    #[test]
1411    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1412        let mut f = Fixture::new();
1413        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1414        // The source is checked before the destination, so this is the error
1415        // and not the zero RENAMENX would otherwise answer for a taken name.
1416        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1417    }
1418
1419    #[test]
1420    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1421        let mut f = Fixture::new();
1422        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1423
1424        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1425        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1426        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
1427        // one call the two disagree about and neither does any work for.
1428        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1429        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1430        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1431        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1432    }
1433
1434    #[test]
1435    fn renaming_a_set_does_not_touch_a_member() {
1436        let mut f = Fixture::new();
1437        for i in 0..300 {
1438            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1439        }
1440        let before = f.server.memory_bytes();
1441
1442        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1443        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1444        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1445        assert!(
1446            f.server.memory_bytes().abs_diff(before) < 256,
1447            "the members were copied: {} against {before}",
1448            f.server.memory_bytes()
1449        );
1450    }
1451
1452    #[test]
1453    fn a_copy_is_a_second_value_and_not_a_second_name() {
1454        let mut f = Fixture::new();
1455        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1456
1457        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1458        f.run(&[b"SADD", b"t", b"m3"]);
1459        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1460        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1461    }
1462
1463    /// Every type a key can hold, copied, because two of them used to panic.
1464    ///
1465    /// `COPY` reads the value out of the source through one match on the type
1466    /// tag, and that match had a catch all at the bottom from back when a set
1467    /// and a hash were the only bodies. The list and the sorted set landed after
1468    /// it and nobody came back, so `COPY mylist other` took the shard down. It
1469    /// is an ordinary command against a type the server supports everywhere
1470    /// else, so this walks all five rather than the two that were broken: the
1471    /// point is that the next type cannot land the same way.
1472    #[test]
1473    fn every_type_can_be_copied() {
1474        let mut f = Fixture::new();
1475        f.run(&[b"SET", b"str", b"v1"]);
1476        f.run(&[b"SADD", b"set", b"m1"]);
1477        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1478        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1479        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1480
1481        for name in [
1482            &b"str"[..],
1483            &b"set"[..],
1484            &b"hash"[..],
1485            &b"list"[..],
1486            &b"zset"[..],
1487        ] {
1488            let dst = [name, b":copy"].concat();
1489            assert_eq!(
1490                f.run(&[b"COPY", name, &dst]),
1491                ":1\r\n",
1492                "copying {}",
1493                String::from_utf8_lossy(name)
1494            );
1495            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1496        }
1497
1498        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1499            let mut want = String::from("*2\r\n");
1500            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1501            want
1502        });
1503        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1504
1505        // And the copy is its own value, not a second name for the source.
1506        f.run(&[b"RPUSH", b"list:copy", b"c"]);
1507        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1508        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1509    }
1510
1511    #[test]
1512    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1513        let mut f = Fixture::new();
1514        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1515        f.run(&[b"SET", b"b", b"v2"]);
1516
1517        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1518        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1519        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1520        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1521        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1522        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1523    }
1524
1525    #[test]
1526    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1527        let mut f = Fixture::new();
1528        f.run(&[b"SET", b"a", b"v1"]);
1529
1530        // Same key, different database, so this is not the same object and is
1531        // an ordinary copy. Same key in the same database is the error below.
1532        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1533        f.run(&[b"SELECT", b"1"]);
1534        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1535        assert_eq!(
1536            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1537            ":0\r\n",
1538            "taken"
1539        );
1540        assert_eq!(
1541            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1542            ":1\r\n"
1543        );
1544    }
1545
1546    #[test]
1547    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1548        let mut f = Fixture::new();
1549        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1550        assert_eq!(
1551            f.run(&[b"SORT", b"l"]),
1552            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1553        );
1554        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
1555        assert_eq!(
1556            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1557            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1558        );
1559        assert_eq!(
1560            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1561            "*1\r\n$1\r\n2\r\n"
1562        );
1563    }
1564
1565    #[test]
1566    fn sort_reads_a_key_per_element_for_by_and_for_get() {
1567        let mut f = Fixture::new();
1568        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1569        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1570        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
1571        // misses, which is a nil in the middle of the array and not a short one.
1572        assert_eq!(
1573            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1574            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1575        );
1576    }
1577
1578    #[test]
1579    fn sort_store_writes_a_list_and_answers_its_length() {
1580        let mut f = Fixture::new();
1581        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1582        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1583        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1584        assert_eq!(
1585            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1586            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1587        );
1588        // An empty result takes the destination with it rather than leaving a
1589        // list that holds nothing.
1590        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1591        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1592    }
1593
1594    #[test]
1595    fn sort_ro_does_not_know_the_word_store() {
1596        let mut f = Fixture::new();
1597        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1598        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1599        assert_eq!(
1600            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1601            "-ERR syntax error\r\n"
1602        );
1603        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1604    }
1605
1606    #[test]
1607    fn sort_refuses_what_it_cannot_sort() {
1608        let mut f = Fixture::new();
1609        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1610        f.run(&[b"SET", b"s", b"x"]);
1611        assert_eq!(
1612            f.run(&[b"SORT", b"s"]),
1613            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1614        );
1615        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1616        assert_eq!(
1617            f.run(&[b"SORT", b"words"]),
1618            "-ERR One or more scores can't be converted into double\r\n"
1619        );
1620        assert_eq!(
1621            f.run(&[b"SORT", b"words", b"ALPHA"]),
1622            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1623        );
1624        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1625    }
1626
1627    #[test]
1628    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1629        let mut f = Fixture::new();
1630        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1631        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1632        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1633        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1634        assert_eq!(
1635            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1636            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1637        );
1638        // And back, which proves the body survived the trip rather than being
1639        // rebuilt from a copy that happened to look the same.
1640        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1641        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1642    }
1643
1644    #[test]
1645    fn move_answers_zero_when_either_end_says_no() {
1646        let mut f = Fixture::new();
1647        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1648        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1649        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1650        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1651        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1652        // The destination is taken, so nothing moves and the source is still
1653        // there with what it had.
1654        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1655        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1656        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1657        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1658    }
1659
1660    #[test]
1661    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1662        let mut f = Fixture::new();
1663        assert_eq!(
1664            f.run(&[b"MOVE", b"a", b"0"]),
1665            "-ERR source and destination objects are the same\r\n"
1666        );
1667        assert_eq!(
1668            f.run(&[b"MOVE", b"a", b"99"]),
1669            "-ERR DB index is out of range\r\n"
1670        );
1671        assert_eq!(
1672            f.run(&[b"MOVE", b"a", b"-1"]),
1673            "-ERR DB index is out of range\r\n"
1674        );
1675        assert_eq!(
1676            f.run(&[b"MOVE", b"a", b"x"]),
1677            "-ERR value is not an integer or out of range\r\n"
1678        );
1679    }
1680
1681    #[test]
1682    fn swapdb_swaps_what_two_connections_would_see() {
1683        let mut f = Fixture::new();
1684        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1685        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1686        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1687        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1688
1689        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1690        // Still on database zero, and database zero is a different database.
1691        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1692        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1693        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1694        // A database swapped with itself is fine and changes nothing.
1695        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1696        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1697    }
1698
1699    #[test]
1700    fn swapdb_says_which_index_it_could_not_read() {
1701        let mut f = Fixture::new();
1702        assert_eq!(
1703            f.run(&[b"SWAPDB", b"x", b"1"]),
1704            "-ERR invalid first DB index\r\n"
1705        );
1706        assert_eq!(
1707            f.run(&[b"SWAPDB", b"0", b"y"]),
1708            "-ERR invalid second DB index\r\n"
1709        );
1710        // A number too big to be an index on a server that keeps one in an int
1711        // is the same complaint, and a plausible one that is not ours is the
1712        // range complaint instead. The split is Redis's.
1713        assert_eq!(
1714            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
1715            "-ERR invalid first DB index\r\n"
1716        );
1717        assert_eq!(
1718            f.run(&[b"SWAPDB", b"0", b"99"]),
1719            "-ERR DB index is out of range\r\n"
1720        );
1721        assert_eq!(
1722            f.run(&[b"SWAPDB", b"-1", b"0"]),
1723            "-ERR DB index is out of range\r\n"
1724        );
1725    }
1726
1727    #[test]
1728    fn wait_answers_zero_replicas_without_waiting() {
1729        let mut f = Fixture::new();
1730        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
1731        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
1732        // A replica that is never going to arrive, and a timeout that would be
1733        // a real wait on a server that had one.
1734        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
1735        // Negative replicas is not an error, because zero is already more than
1736        // it asked for.
1737        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
1738        assert_eq!(
1739            f.run(&[b"WAIT", b"x", b"0"]),
1740            "-ERR value is not an integer or out of range\r\n"
1741        );
1742        assert_eq!(
1743            f.run(&[b"WAIT", b"0", b"-1"]),
1744            "-ERR timeout is negative\r\n"
1745        );
1746        assert_eq!(
1747            f.run(&[b"WAIT", b"0", b"1.5"]),
1748            "-ERR timeout is not an integer or out of range\r\n"
1749        );
1750    }
1751
1752    #[test]
1753    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
1754        let mut f = Fixture::new();
1755        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
1756        assert_eq!(
1757            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
1758            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
1759        );
1760        assert_eq!(
1761            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
1762            "-ERR value is out of range, value must between 0 and 1\r\n"
1763        );
1764        assert_eq!(
1765            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
1766            "-ERR value is out of range, must be positive\r\n"
1767        );
1768        // The arguments are all read before the server looks at itself, so a
1769        // bad timeout beats the append only complaint even with numlocal set.
1770        assert_eq!(
1771            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
1772            "-ERR timeout is negative\r\n"
1773        );
1774    }
1775
1776    /// The bytes inside a bulk reply, with the header and the trailing break
1777    /// taken off. Every `DUMP` test needs this and none of them care how the
1778    /// length was written.
1779    fn payload(reply: &[u8]) -> Vec<u8> {
1780        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
1781        reply[head + 2..reply.len() - 2].to_vec()
1782    }
1783
1784    #[test]
1785    fn a_value_survives_a_dump_and_a_restore() {
1786        let mut f = Fixture::new();
1787        f.run(&[b"SET", b"s", b"hello"]);
1788        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
1789        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
1790        f.run(&[b"SADD", b"u", b"x", b"y"]);
1791        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
1792        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
1793
1794        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
1795            let mut copy = key.to_vec();
1796            copy.push(b'2');
1797            let bytes = payload(&f.raw(&[b"DUMP", key]));
1798            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
1799            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
1800        }
1801
1802        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
1803        assert_eq!(
1804            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
1805            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
1806        );
1807        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
1808        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
1809        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
1810        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
1811        // The encoding survives too, since the payload names the plainest legal
1812        // type and the loader puts the value back on the rung it belongs on.
1813        assert_eq!(
1814            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
1815            f.run(&[b"OBJECT", b"ENCODING", b"t"])
1816        );
1817    }
1818
1819    #[test]
1820    fn a_dumped_hash_keeps_its_field_deadlines() {
1821        let mut f = Fixture::new();
1822        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
1823        assert_eq!(
1824            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
1825            "*1\r\n:1\r\n"
1826        );
1827        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
1828        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
1829        assert_eq!(
1830            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
1831            "*2\r\n:-1\r\n:100\r\n"
1832        );
1833    }
1834
1835    #[test]
1836    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
1837        let mut f = Fixture::new();
1838        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
1839        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1840        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
1841        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
1842        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
1843        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
1844        // An absolute deadline that has already gone is not an error. The key is
1845        // not created and the reply is the same OK a live one gets.
1846        assert_eq!(
1847            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
1848            "+OK\r\n"
1849        );
1850        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1851    }
1852
1853    #[test]
1854    fn dump_answers_nothing_for_a_key_that_is_not_there() {
1855        let mut f = Fixture::new();
1856        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
1857        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
1858        f.advance(50);
1859        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
1860    }
1861
1862    #[test]
1863    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
1864        let mut f = Fixture::new();
1865        f.run(&[b"SET", b"a", b"first"]);
1866        f.run(&[b"SET", b"b", b"second"]);
1867        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
1868        assert_eq!(
1869            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
1870            "-BUSYKEY Target key name already exists.\r\n"
1871        );
1872        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
1873        assert_eq!(
1874            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
1875            "+OK\r\n"
1876        );
1877        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
1878    }
1879
1880    /// The busy key comes before the payload, which is not the order the
1881    /// arguments read in. Whether a key is taken should not depend on whether
1882    /// the bytes behind it happened to be good.
1883    #[test]
1884    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
1885        let mut f = Fixture::new();
1886        f.run(&[b"SET", b"a", b"v"]);
1887        assert_eq!(
1888            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
1889            "-BUSYKEY Target key name already exists.\r\n"
1890        );
1891        // And the options come before even that, so a bad FREQ beats the busy
1892        // key the same way a bad DB beats a missing source in COPY.
1893        assert_eq!(
1894            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
1895            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1896        );
1897    }
1898
1899    #[test]
1900    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
1901        let mut f = Fixture::new();
1902        f.run(&[b"SET", b"a", b"hello"]);
1903        let good = payload(&f.raw(&[b"DUMP", b"a"]));
1904
1905        let mut flipped = good.clone();
1906        flipped[2] ^= 0x40;
1907        assert_eq!(
1908            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
1909            "-ERR DUMP payload version or checksum are wrong\r\n"
1910        );
1911        assert_eq!(
1912            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
1913            "-ERR DUMP payload version or checksum are wrong\r\n"
1914        );
1915        // A footer that is right over a body that is not. The type byte says
1916        // string and there is nothing behind it, so the checksum agrees and the
1917        // value does not exist.
1918        let mut truncated = good[..1].to_vec();
1919        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
1920        let crc = yo_common::crc::crc64(0, &truncated);
1921        truncated.extend_from_slice(&crc.to_le_bytes());
1922        assert_eq!(
1923            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
1924            "-ERR Bad data format\r\n"
1925        );
1926        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
1927    }
1928
1929    #[test]
1930    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
1931        let mut f = Fixture::new();
1932        f.run(&[b"SET", b"a", b"v"]);
1933        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1934        assert_eq!(
1935            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
1936            "-ERR Invalid TTL value, must be >= 0\r\n"
1937        );
1938        assert_eq!(
1939            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
1940            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
1941        );
1942        assert_eq!(
1943            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
1944            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1945        );
1946        // Both are accepted and both are then dropped, which is D-26.
1947        assert_eq!(
1948            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
1949            "+OK\r\n"
1950        );
1951        assert_eq!(
1952            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
1953            "+OK\r\n"
1954        );
1955    }
1956
1957    /// Neither word is refused for being the wrong one. Each is only accepted
1958    /// while the other is unset, so the second of the two falls through to the
1959    /// plain syntax error rather than getting a message of its own.
1960    #[test]
1961    fn restore_takes_idletime_or_freq_and_not_both() {
1962        let mut f = Fixture::new();
1963        f.run(&[b"SET", b"a", b"v"]);
1964        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1965        assert_eq!(
1966            f.run(&[
1967                b"RESTORE",
1968                b"b",
1969                b"0",
1970                &bytes,
1971                b"IDLETIME",
1972                b"1",
1973                b"FREQ",
1974                b"2"
1975            ]),
1976            "-ERR syntax error\r\n"
1977        );
1978        assert_eq!(
1979            f.run(&[
1980                b"RESTORE",
1981                b"b",
1982                b"0",
1983                &bytes,
1984                b"FREQ",
1985                b"2",
1986                b"IDLETIME",
1987                b"1"
1988            ]),
1989            "-ERR syntax error\r\n"
1990        );
1991        assert_eq!(
1992            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
1993            "-ERR syntax error\r\n"
1994        );
1995        assert_eq!(
1996            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
1997            "-ERR syntax error\r\n"
1998        );
1999    }
2000
2001    #[test]
2002    fn copy_checks_its_options_before_it_looks_for_anything() {
2003        let mut f = Fixture::new();
2004        // No key exists at all, and every one of these is still the option
2005        // complaint rather than a zero, which is the order a real server uses.
2006        assert_eq!(
2007            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
2008            "-ERR DB index is out of range\r\n"
2009        );
2010        assert_eq!(
2011            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
2012            "-ERR DB index is out of range\r\n"
2013        );
2014        assert_eq!(
2015            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
2016            "-ERR value is not an integer or out of range\r\n"
2017        );
2018        assert_eq!(
2019            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
2020            "-ERR syntax error\r\n"
2021        );
2022        assert_eq!(
2023            f.run(&[b"COPY", b"a", b"a"]),
2024            "-ERR source and destination objects are the same\r\n"
2025        );
2026        // Repeated, reordered and lowercased, and the last DB wins.
2027        assert_eq!(
2028            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
2029            ":0\r\n"
2030        );
2031    }
2032
2033    #[test]
2034    fn time_is_two_bulk_strings_and_moves() {
2035        let mut f = Fixture::new();
2036        let first = f.run(&[b"TIME"]);
2037        assert!(first.starts_with("*2\r\n$"), "got {first}");
2038        let parts: Vec<&str> = first.split("\r\n").collect();
2039        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
2040        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
2041        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
2042        assert!((0..1_000_000).contains(&micros), "got {micros}");
2043        // The coarse clock the keyspace uses is a cached millisecond that a
2044        // background tick refreshes, so a TIME built on it would answer the
2045        // same microsecond twice in a row here.
2046        assert_ne!(first, f.run(&[b"TIME"]));
2047    }
2048
2049    #[test]
2050    fn a_keyspace_scan_walks_every_key_once() {
2051        let mut f = Fixture::new();
2052        for i in 0..500 {
2053            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
2054        }
2055
2056        let mut seen: Vec<String> = Vec::new();
2057        let mut cursor = "0".to_owned();
2058        let mut calls = 0;
2059        loop {
2060            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
2061            seen.extend(keys);
2062            cursor = next;
2063            calls += 1;
2064            assert!(calls < 10_000, "the cursor is not advancing");
2065            if cursor == "0" {
2066                break;
2067            }
2068        }
2069
2070        seen.sort();
2071        seen.dedup();
2072        assert_eq!(seen.len(), 500, "every key once and only once");
2073        // And more than one call to get them, or the COUNT is being ignored and
2074        // the loop above proved nothing about resuming.
2075        assert!(calls > 1, "500 keys came back in one batch");
2076    }
2077
2078    #[test]
2079    fn a_scan_narrows_by_pattern_and_by_type() {
2080        let mut f = Fixture::new();
2081        f.run(&[b"SET", b"str", b"v"]);
2082        f.run(&[b"SADD", b"members", b"a"]);
2083        f.run(&[b"HSET", b"fields", b"f", b"v"]);
2084
2085        let all = |f: &mut Fixture, args: &[&[u8]]| {
2086            let mut out: Vec<String> = Vec::new();
2087            let mut cursor = "0".to_owned();
2088            loop {
2089                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
2090                line.extend_from_slice(args);
2091                let (next, keys) = scan_reply(&f.run(&line));
2092                out.extend(keys);
2093                cursor = next;
2094                if cursor == "0" {
2095                    break;
2096                }
2097            }
2098            out.sort();
2099            out
2100        };
2101
2102        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
2103        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
2104        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
2105        // Case insensitive, the same as Redis's own comparison.
2106        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
2107        // A type nothing can hold is not an error, it just matches nothing.
2108        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
2109        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
2110        // Both filters at once, and they are an and rather than an or.
2111        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
2112    }
2113
2114    #[test]
2115    fn a_scan_says_what_is_wrong_with_it() {
2116        let mut f = Fixture::new();
2117        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
2118        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
2119        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
2120        assert_eq!(
2121            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
2122            "-ERR syntax error\r\n"
2123        );
2124        assert_eq!(
2125            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
2126            "-ERR value is not an integer or out of range\r\n"
2127        );
2128        assert_eq!(
2129            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
2130            "-ERR syntax error\r\n"
2131        );
2132        // A cursor the client made up is a cursor. It resumes somewhere
2133        // arbitrary and answers whatever is there, which is what Redis does and
2134        // is the only behaviour that does not need the server to remember every
2135        // cursor it has handed out.
2136        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
2137    }
2138
2139    #[test]
2140    fn keys_and_randomkey_look_at_the_whole_database() {
2141        let mut f = Fixture::new();
2142        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
2143        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
2144
2145        for name in ["one", "two", "three"] {
2146            f.run(&[b"SET", name.as_bytes(), b"v"]);
2147        }
2148        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
2149        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
2150        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
2151
2152        for _ in 0..50 {
2153            let got = f.run(&[b"RANDOMKEY"]);
2154            assert!(
2155                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
2156                "got {got}"
2157            );
2158        }
2159    }
2160
2161    #[test]
2162    fn a_walk_does_not_answer_keys_that_have_expired() {
2163        let mut f = Fixture::new();
2164        f.run(&[b"SET", b"alive", b"v"]);
2165        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
2166        f.server.db(0).clock_mut().advance(2);
2167        assert_eq!(
2168            f.run(&[b"DBSIZE"]),
2169            ":2\r\n",
2170            "nothing has collected it yet"
2171        );
2172
2173        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
2174        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
2175        assert_eq!(keys, ["alive"]);
2176        for _ in 0..20 {
2177            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
2178        }
2179        // The walk collected it on the way past, which is what makes DBSIZE
2180        // here answer what Redis answers once its own cycle has been round.
2181        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2182    }
2183
2184    #[test]
2185    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
2186        let mut f = Fixture::new();
2187        f.run(&[b"SET", b"k", b"v"]);
2188        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
2189        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
2190
2191        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
2192        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2193        let ms = int(&f.run(&[b"PTTL", b"k"]));
2194        assert!((99_000..=100_000).contains(&ms), "got {ms}");
2195
2196        // The absolute pair, derived from the same one number the store kept.
2197        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
2198        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2199        assert_eq!(at, (at_ms + 500) / 1000);
2200        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
2201
2202        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
2203        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
2204        assert_eq!(
2205            f.run(&[b"PERSIST", b"k"]),
2206            ":0\r\n",
2207            "nothing to take off the second time"
2208        );
2209        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
2210        assert_eq!(
2211            f.run(&[b"GET", b"k"]),
2212            "$1\r\nv\r\n",
2213            "and the value went through all of that untouched"
2214        );
2215    }
2216
2217    #[test]
2218    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
2219        let mut f = Fixture::new();
2220        f.run(&[b"SET", b"str", b"v"]);
2221        f.run(&[b"SADD", b"set", b"a", b"b"]);
2222        f.run(&[b"HSET", b"hash", b"f", b"v"]);
2223
2224        for key in [b"str".as_slice(), b"set", b"hash"] {
2225            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
2226            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
2227        }
2228        // The body is not touched by any of that, which is the whole reason the
2229        // deadline lives in the record and the body lives somewhere else.
2230        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
2231        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
2232        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
2233    }
2234
2235    #[test]
2236    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
2237        let mut f = Fixture::new();
2238        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
2239            f.run(&[b"SET", key, b"v"]);
2240        }
2241        // Four ways of naming a moment that has passed, and all four are a
2242        // delete answering 1 rather than an error. Zero is a moment, minus one
2243        // is a moment, and the hash field commands refuse the negative one.
2244        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
2245        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
2246        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
2247        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
2248        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2249        assert_eq!(
2250            f.run(&[b"EXPIRE", b"a", b"100"]),
2251            ":0\r\n",
2252            "and the key really went, so there is nothing to put a deadline on"
2253        );
2254    }
2255
2256    #[test]
2257    fn the_four_conditions_decide_whether_the_deadline_moves() {
2258        let mut f = Fixture::new();
2259        f.run(&[b"SET", b"k", b"v"]);
2260
2261        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
2262        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
2263        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
2264        assert_eq!(
2265            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
2266            ":1\r\n",
2267            "no deadline reads as infinitely far away, so LT passes where GT fails"
2268        );
2269
2270        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
2271        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
2272        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2273        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
2274        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
2275        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2276
2277        // The condition is answered before the past check, so this is a 0 and
2278        // the key survives. The other order would delete it.
2279        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
2280        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
2281        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
2282        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
2283    }
2284
2285    #[test]
2286    fn the_conditions_are_a_set_and_not_a_keyword() {
2287        let mut f = Fixture::new();
2288        f.run(&[b"SET", b"k", b"v"]);
2289
2290        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
2291        assert_eq!(
2292            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
2293            ":0\r\n",
2294            "the same keyword twice means it once, and NX now has a deadline to fail on"
2295        );
2296
2297        // XX with LT is the one pair that is not either of them on its own: LT
2298        // alone would accept a key with no deadline and this does not.
2299        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
2300        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
2301        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
2302        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
2303        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
2304        f.run(&[b"PERSIST", b"k"]);
2305        assert_eq!(
2306            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
2307            ":0\r\n",
2308            "where LT on its own would have taken it"
2309        );
2310        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
2311    }
2312
2313    #[test]
2314    fn a_key_is_gone_once_its_moment_passes() {
2315        let mut f = Fixture::new();
2316        f.run(&[b"SET", b"k", b"v"]);
2317        f.run(&[b"EXPIRE", b"k", b"100"]);
2318
2319        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
2320        f.server.set_clock_ms(at as u64 + 1);
2321        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2322        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
2323        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
2324        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2325    }
2326
2327    #[test]
2328    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
2329        let mut f = Fixture::new();
2330        f.run(&[b"SET", b"k", b"v"]);
2331        for (bad, want) in [
2332            (
2333                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
2334                "-ERR value is not an integer or out of range\r\n",
2335            ),
2336            (
2337                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
2338                "-ERR Unsupported option MAYBE\r\n",
2339            ),
2340            (
2341                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
2342                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2343            ),
2344            (
2345                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
2346                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
2347            ),
2348            (
2349                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
2350                "-ERR GT and LT options at the same time are not compatible\r\n",
2351            ),
2352            // Seconds that overflow when multiplied into milliseconds. Every
2353            // message names the command it came from.
2354            (
2355                &[b"EXPIRE", b"k", b"9223372036854775807"],
2356                "-ERR invalid expire time in 'expire' command\r\n",
2357            ),
2358            (
2359                &[b"EXPIREAT", b"k", b"9223372036854775807"],
2360                "-ERR invalid expire time in 'expireat' command\r\n",
2361            ),
2362            (
2363                &[b"PEXPIRE", b"k", b"9223372036854775807"],
2364                "-ERR invalid expire time in 'pexpire' command\r\n",
2365            ),
2366        ] {
2367            assert_eq!(f.run(bad), want, "for {bad:?}");
2368        }
2369        assert_eq!(
2370            f.run(&[b"TTL", b"k"]),
2371            ":-1\r\n",
2372            "and none of those put a deadline on anything"
2373        );
2374
2375        // The one of the four that has no arithmetic to overflow. Redis takes
2376        // it and holds the number as given, and a record here holds forty six
2377        // bits, so it lands in the year 4199 instead. D-17.
2378        assert_eq!(
2379            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2380            ":1\r\n"
2381        );
2382        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2383    }
2384
2385    #[test]
2386    fn flushing_empties_this_database_or_every_one_of_them() {
2387        let mut f = Fixture::new();
2388        f.run(&[b"SELECT", b"0"]);
2389        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2390        f.run(&[b"SELECT", b"1"]);
2391        f.run(&[b"SET", b"c", b"3"]);
2392        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2393        // ASYNC and SYNC are both taken and neither changes anything, since the
2394        // keyspace is empty before the OK goes out either way.
2395        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2396        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2397        // Only database one was emptied.
2398        f.run(&[b"SELECT", b"0"]);
2399        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2400        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2401        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2402        f.run(&[b"SELECT", b"1"]);
2403        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2404        // Anything else after the name is a syntax error, and so is a third
2405        // argument even when the second one is a word we take.
2406        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2407        assert_eq!(
2408            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2409            "-ERR syntax error\r\n"
2410        );
2411    }
2412
2413    #[test]
2414    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2415        let mut f = Fixture::new();
2416        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2417        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2418        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2419        // Nothing is cached, so nothing is there, one answer per hash asked
2420        // about.
2421        assert_eq!(
2422            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2423            "*2\r\n:0\r\n:0\r\n"
2424        );
2425        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2426        assert_eq!(
2427            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2428            "*0\r\n"
2429        );
2430        assert_eq!(
2431            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2432            "-ERR Library not found\r\n"
2433        );
2434
2435        // Redis's two messages here are its own, one per container, and one of
2436        // them reads like a typo.
2437        assert_eq!(
2438            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2439            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2440        );
2441        assert_eq!(
2442            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2443            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2444        );
2445        // A second argument after the mode is the generic one instead, because
2446        // the count is checked before the word is looked at.
2447        assert_eq!(
2448            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2449            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2450        );
2451        assert_eq!(
2452            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2453            "-ERR Unknown argument bogus\r\n"
2454        );
2455        assert_eq!(
2456            f.run(&[b"SCRIPT", b"EXISTS"]),
2457            "-ERR wrong number of arguments for 'script|exists' command\r\n"
2458        );
2459
2460        // The ones that need an interpreter are not here, and say so rather
2461        // than answering OK to a load that loaded nothing.
2462        assert_eq!(
2463            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2464            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2465        );
2466        assert_eq!(
2467            f.run(&[b"FUNCTION", b"STATS"]),
2468            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2469        );
2470    }
2471
2472    #[test]
2473    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2474        let mut f = Fixture::new();
2475        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2476        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2477        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2478        // Read back as a string it is still an integer, written out as digits
2479        // only because somebody asked for them.
2480        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2481        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2482        // A counter that is not a number is the error the store raises and this
2483        // layer only spells, which is the whole point of the split.
2484        f.run(&[b"SET", b"k", b"hello"]);
2485        assert_eq!(
2486            f.run(&[b"INCR", b"k"]),
2487            "-ERR value is not an integer or out of range\r\n"
2488        );
2489        assert_eq!(
2490            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2491            "-ERR increment would produce NaN or Infinity\r\n"
2492        );
2493    }
2494
2495    /// Every one of these was read off a running 8.8. They are the answers a
2496    /// client library's own test suite checks, and the shapes are not
2497    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
2498    /// integer, `INCREX` is a pair.
2499    #[test]
2500    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2501        let mut f = Fixture::new();
2502        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2503        // The same digest a real 8.8 answers for the same five bytes, which is
2504        // what makes `IFDEQ` usable against a mixed deployment.
2505        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2506        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2507        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2508        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2509        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2510        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2511        assert_eq!(
2512            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2513            "*2\r\n:1\r\n:0\r\n",
2514            "a refused increment reports the value it left alone and applied nothing"
2515        );
2516        assert_eq!(
2517            f.run(&[
2518                b"INCREX",
2519                b"n",
2520                b"BYINT",
2521                b"5",
2522                b"UBOUND",
2523                b"3",
2524                b"SATURATE"
2525            ]),
2526            "*2\r\n:3\r\n:2\r\n"
2527        );
2528        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2529        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2530    }
2531
2532    #[test]
2533    fn the_same_answers_come_out_in_resp3_spelling() {
2534        let mut f = Fixture::new();
2535        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2536        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2537        // A float counter is a double on RESP3 and the digits in a bulk string
2538        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
2539        assert_eq!(
2540            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2541            "*2\r\n,1.5\r\n,1.5\r\n"
2542        );
2543        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2544        // `RESET` puts the protocol back, which is the part that is easy to
2545        // miss and leaves a pooled connection speaking the wrong one.
2546        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2547        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2548    }
2549
2550    #[test]
2551    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2552        let mut f = Fixture::new();
2553        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2554        assert_eq!(flow, Flow::Continue);
2555        assert_eq!(
2556            reply,
2557            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2558        );
2559        // A name with a line ending in it cannot write its own frame into the
2560        // stream, which is the reason the error writer maps them to spaces.
2561        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2562        assert_eq!(reply.matches("\r\n").count(), 1);
2563    }
2564
2565    #[test]
2566    fn arity_is_checked_before_the_command_is() {
2567        let mut f = Fixture::new();
2568        assert_eq!(
2569            f.run(&[b"GET"]),
2570            "-ERR wrong number of arguments for 'get' command\r\n"
2571        );
2572        assert_eq!(
2573            f.run(&[b"MSET", b"k"]),
2574            "-ERR wrong number of arguments for 'mset' command\r\n"
2575        );
2576        // The table says `PING` takes one or more and a real server then
2577        // refuses three, which is the sort of thing that only shows up against
2578        // the real thing.
2579        assert_eq!(
2580            f.run(&[b"PING", b"a", b"b"]),
2581            "-ERR wrong number of arguments for 'ping' command\r\n"
2582        );
2583        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2584        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2585        // `DELEX` takes two or four and nothing between.
2586        assert_eq!(
2587            f.run(&[b"DELEX", b"k", b"IFEQ"]),
2588            "-ERR wrong number of arguments for 'delex' command\r\n"
2589        );
2590    }
2591
2592    /// The option rules, all of them measured against 8.8 rather than read off
2593    /// the documentation. The surprising one is that `SET` accepts the same
2594    /// keyword twice and `INCREX` does not.
2595    #[test]
2596    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2597        let mut f = Fixture::new();
2598        let syntax = "-ERR syntax error\r\n";
2599        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2600        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2601        assert_eq!(
2602            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2603            syntax
2604        );
2605        assert_eq!(
2606            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2607            syntax
2608        );
2609        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2610        // Twice is fine, and the last one wins.
2611        assert_eq!(
2612            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2613            "+OK\r\n"
2614        );
2615        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2616        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2617        // `INCREX` refuses what `SET` allows.
2618        assert_eq!(
2619            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2620            syntax
2621        );
2622        assert_eq!(
2623            f.run(&[b"INCREX", b"n", b"ENX"]),
2624            "-ERR ENX flag requires an expiration\r\n"
2625        );
2626        assert_eq!(
2627            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2628            "-ERR UBOUND is not an integer or out of range\r\n"
2629        );
2630        assert_eq!(
2631            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2632            "-ERR LBOUND can't be greater than UBOUND\r\n"
2633        );
2634        assert_eq!(
2635            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2636            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2637        );
2638    }
2639
2640    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
2641    /// key that is not there, which answers null without ever looking at the
2642    /// expiration it was given.
2643    #[test]
2644    fn the_expiry_rules_are_redis_own() {
2645        let mut f = Fixture::new();
2646        let bad = "-ERR invalid expire time in 'set' command\r\n";
2647        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2648        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2649        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2650        assert_eq!(
2651            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2652            bad
2653        );
2654        assert_eq!(
2655            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2656            "-ERR value is not an integer or out of range\r\n"
2657        );
2658        assert_eq!(
2659            f.run(&[b"SETEX", b"k", b"0", b"v"]),
2660            "-ERR invalid expire time in 'setex' command\r\n"
2661        );
2662        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2663        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2664        assert_eq!(
2665            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2666            "-ERR syntax error\r\n",
2667            "the option list is still checked before the key is looked up"
2668        );
2669        // A deadline in the past is accepted and the key goes with it.
2670        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2671        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2672        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2673    }
2674
2675    #[test]
2676    fn mset_takes_its_pairs_from_the_read_buffer() {
2677        let mut f = Fixture::new();
2678        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2679        assert_eq!(
2680            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2681            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2682        );
2683        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2684        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2685        assert_eq!(
2686            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2687            "-ERR wrong number of key-value pairs\r\n"
2688        );
2689        assert_eq!(
2690            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2691            "-ERR invalid numkeys value\r\n"
2692        );
2693        assert_eq!(
2694            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2695            "-ERR invalid numkeys value\r\n"
2696        );
2697    }
2698
2699    #[test]
2700    fn lcs_answers_the_length_the_string_and_the_runs() {
2701        let mut f = Fixture::new();
2702        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2703        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2704        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2705        assert_eq!(
2706            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2707            "*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"
2708        );
2709        // Without `IDX` the two options that only mean something with it are
2710        // accepted and ignored, which is what a real server does.
2711        assert_eq!(
2712            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
2713            "$6\r\nmytext\r\n"
2714        );
2715    }
2716
2717    #[test]
2718    fn select_moves_the_connection_and_the_databases_stay_apart() {
2719        let mut f = Fixture::new();
2720        f.run(&[b"SET", b"k", b"zero"]);
2721        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
2722        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2723        f.run(&[b"SET", b"k", b"four"]);
2724        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2725        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2726        assert_eq!(
2727            f.run(&[b"SELECT", b"99"]),
2728            "-ERR DB index is out of range\r\n"
2729        );
2730        assert_eq!(
2731            f.run(&[b"SELECT", b"-1"]),
2732            "-ERR DB index is out of range\r\n"
2733        );
2734        assert_eq!(
2735            f.run(&[b"SELECT", b"abc"]),
2736            "-ERR value is not an integer or out of range\r\n"
2737        );
2738        // `RESET` brings it back to zero.
2739        f.run(&[b"SELECT", b"4"]);
2740        f.run(&[b"RESET"]);
2741        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2742    }
2743
2744    #[test]
2745    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
2746        let mut f = Fixture::new();
2747        let reply = f.run(&[b"HELLO"]);
2748        assert!(reply.starts_with("*14\r\n"), "{reply}");
2749        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
2750        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
2751        assert!(
2752            reply.contains(":7\r\n"),
2753            "the connection id is in there: {reply}"
2754        );
2755        assert_eq!(
2756            f.run(&[b"HELLO", b"4"]),
2757            "-NOPROTO unsupported protocol version\r\n"
2758        );
2759        assert_eq!(
2760            f.run(&[b"HELLO", b"abc"]),
2761            "-ERR Protocol version is not an integer or out of range\r\n"
2762        );
2763        assert_eq!(
2764            f.run(&[b"HELLO", b"3", b"SETNAME"]),
2765            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
2766        );
2767        assert!(
2768            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
2769                .starts_with("%7\r\n")
2770        );
2771        assert_eq!(f.session.name(), b"bob");
2772        f.run(&[b"RESET"]);
2773        assert_eq!(f.session.name(), b"");
2774    }
2775
2776    #[test]
2777    fn command_describes_this_server_in_the_shape_a_driver_reads() {
2778        let mut f = Fixture::new();
2779        let count = format!(":{}\r\n", COMMANDS.len());
2780        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
2781        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
2782        assert_eq!(
2783            info,
2784            "*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\
2785             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
2786        );
2787        // A null in the list, and the plain one: `$-1` and not `*-1`.
2788        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
2789        assert_eq!(
2790            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
2791            "*1\r\n$8\r\ngetrange\r\n"
2792        );
2793        assert_eq!(
2794            f.run(&[b"COMMAND", b"NOPE"]),
2795            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
2796        );
2797    }
2798
2799    /// A cluster aware client asks this question and then routes on the
2800    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
2801    /// that matters.
2802    #[test]
2803    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
2804        let mut f = Fixture::new();
2805        assert_eq!(
2806            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
2807            "*1\r\n$1\r\nk\r\n"
2808        );
2809        assert_eq!(
2810            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
2811            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2812        );
2813        assert_eq!(
2814            f.run(&[
2815                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
2816            ]),
2817            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2818        );
2819        assert_eq!(
2820            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
2821            "-ERR The command has no key arguments\r\n"
2822        );
2823        assert_eq!(
2824            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
2825            "-ERR Invalid number of arguments specified for command\r\n"
2826        );
2827    }
2828
2829    #[test]
2830    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
2831        let mut f = Fixture::new();
2832        assert_eq!(
2833            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2834            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
2835        );
2836        // A pattern matches more than one, and a setting two patterns both ask
2837        // for is still sent once.
2838        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
2839        assert!(both.starts_with("*6\r\n"), "{both}");
2840        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
2841        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
2842        assert_eq!(
2843            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
2844            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
2845        );
2846        assert_eq!(
2847            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
2848            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
2849        );
2850        assert_eq!(
2851            f.run(&[b"CONFIG", b"GET"]),
2852            "-ERR wrong number of arguments for 'config|get' command\r\n"
2853        );
2854        // Too few arguments and an odd number of them are different
2855        // complaints, which is the sort of thing only the real server tells
2856        // you.
2857        assert_eq!(
2858            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
2859            "-ERR wrong number of arguments for 'config|set' command\r\n"
2860        );
2861        assert_eq!(
2862            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
2863            "-ERR syntax error\r\n"
2864        );
2865        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
2866        assert_eq!(
2867            f.run(&[b"CONFIG", b"REWRITE"]),
2868            "-ERR The server is running without a config file\r\n"
2869        );
2870    }
2871
2872    #[test]
2873    fn the_eviction_policy_reads_back_what_was_written_to_it() {
2874        let mut f = Fixture::new();
2875        assert_eq!(
2876            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2877            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
2878        );
2879        assert_eq!(
2880            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
2881            "+OK\r\n",
2882            "the name is matched without regard to case, like every other one"
2883        );
2884        assert_eq!(
2885            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2886            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2887        );
2888        // And INFO agrees with CONFIG, which it did not when it was a literal.
2889        assert!(
2890            f.run(&[b"INFO", b"memory"])
2891                .contains("maxmemory_policy:allkeys-lfu"),
2892            "INFO and CONFIG disagree about the policy"
2893        );
2894        // The refusal names every legal value in the order the real server's
2895        // enum table lists them, because a client comparing the message compares
2896        // the whole string.
2897        assert_eq!(
2898            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
2899            "-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"
2900        );
2901        // A bad pair leaves the good one in the same command alone, and the
2902        // policy is checked by the same pass that checks the numbers.
2903        assert_eq!(
2904            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2905            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2906        );
2907        f.run(&[
2908            b"CONFIG",
2909            b"SET",
2910            b"hash-max-listpack-entries",
2911            b"7",
2912            b"maxmemory-policy",
2913            b"nonsense",
2914        ]);
2915        assert_eq!(
2916            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2917            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
2918        );
2919    }
2920
2921    #[test]
2922    fn the_three_eviction_numbers_read_back_too() {
2923        let mut f = Fixture::new();
2924        for (name, default, set) in [
2925            ("maxmemory-samples", "5", "12"),
2926            ("lfu-log-factor", "10", "3"),
2927            ("lfu-decay-time", "1", "60"),
2928        ] {
2929            let get = || {
2930                format!(
2931                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
2932                    name.len(),
2933                    default.len()
2934                )
2935            };
2936            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
2937            assert_eq!(
2938                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
2939                "+OK\r\n"
2940            );
2941            assert_eq!(
2942                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
2943                format!(
2944                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
2945                    name.len(),
2946                    set.len()
2947                )
2948            );
2949            // A number that is not a number is refused with the same sentence
2950            // every other number gets, which names the setting the client typed.
2951            assert_eq!(
2952                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
2953                format!(
2954                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
2955                )
2956            );
2957        }
2958    }
2959
2960    #[test]
2961    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
2962        let mut f = Fixture::new();
2963        assert_eq!(
2964            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2965            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
2966            "no limit is the default"
2967        );
2968        // The pairing is Redis's and it is a trap: the bare letter is a power of
2969        // ten and the one with the b is a power of two.
2970        for (typed, bytes) in [
2971            (&b"1024"[..], "1024"),
2972            (b"1k", "1000"),
2973            (b"1kb", "1024"),
2974            (b"1M", "1000000"),
2975            (b"1Mb", "1048576"),
2976            (b"1gb", "1073741824"),
2977            (b"100mb", "104857600"),
2978        ] {
2979            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
2980            assert_eq!(
2981                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2982                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
2983                "set {}",
2984                String::from_utf8_lossy(typed)
2985            );
2986        }
2987        assert!(
2988            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2989            "the report agrees with the setting"
2990        );
2991
2992        // A unit nobody has heard of, and a negative number, which is not a very
2993        // large one however it is spelled.
2994        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
2995            assert_eq!(
2996                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
2997                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
2998                "refused {}",
2999                String::from_utf8_lossy(bad)
3000            );
3001        }
3002        assert!(
3003            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
3004            "and the refusal left the old one alone"
3005        );
3006    }
3007
3008    #[test]
3009    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
3010        let mut f = Fixture::new();
3011        f.run(&[b"SET", b"here", b"already"]);
3012        // A byte, which is under what an empty server holds, so nothing this
3013        // command could do would get it under. The default policy is
3014        // `noeviction`, so nothing is what it does.
3015        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
3016        assert_eq!(
3017            f.run(&[b"SET", b"k", b"v"]),
3018            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3019        );
3020        assert_eq!(
3021            f.run(&[b"LPUSH", b"l", b"v"]),
3022            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
3023        );
3024        // Reading is allowed, and so is the one thing that would help.
3025        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
3026        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
3027        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
3028
3029        // Taking the limit away lets the write through again.
3030        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3031        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
3032    }
3033
3034    #[test]
3035    fn an_allkeys_policy_makes_room_instead_of_refusing() {
3036        let mut f = Fixture::new();
3037        let val = vec![b'v'; 256];
3038        for i in 0..24000u32 {
3039            let k = format!("key:{i:08}");
3040            f.run(&[b"SET", k.as_bytes(), &val]);
3041        }
3042        let full = f.server.memory_bytes();
3043        assert!(
3044            full > 3 * 1024 * 1024,
3045            "the arena is several segments: {full}"
3046        );
3047
3048        // Two megabytes under what it is holding, which is one segment's worth,
3049        // so getting there means giving a whole segment back and not just
3050        // dropping a few records.
3051        let limit = full - 2 * 1024 * 1024;
3052        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
3053        f.run(&[
3054            b"CONFIG",
3055            b"SET",
3056            b"maxmemory",
3057            limit.to_string().as_bytes(),
3058        ]);
3059
3060        // Writes keep working the whole way down. The budget means one command
3061        // does not do it all, so this runs until the server has settled and
3062        // checks that nothing was refused on the way.
3063        for i in 0..2000u32 {
3064            let k = format!("new:{i:08}");
3065            assert_eq!(
3066                f.run(&[b"SET", k.as_bytes(), &val]),
3067                "+OK\r\n",
3068                "write {i} was refused"
3069            );
3070            f.server.refresh_memory();
3071            if f.server.memory_bytes() <= limit {
3072                break;
3073            }
3074        }
3075        assert!(
3076            f.server.memory_bytes() <= limit,
3077            "it never got under: {} against {limit}",
3078            f.server.memory_bytes()
3079        );
3080        let info = f.run(&[b"INFO", b"stats"]);
3081        assert!(!info.contains("evicted_keys:0"), "{info}");
3082        assert!(
3083            f.run(&[b"DBSIZE"]) != ":0\r\n",
3084            "and it did not empty the database to get there"
3085        );
3086    }
3087
3088    #[test]
3089    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
3090        // The limit is judged against a number kept as the collections move,
3091        // rather than found by asking all of them, and the two have to be the
3092        // same number or the limit is enforced against a fiction. This does the
3093        // things that move it, which is growing a collection, shrinking one,
3094        // changing its representation, deleting it and reusing its slot, across
3095        // all five types, and checks the two against each other as it goes.
3096        let mut f = Fixture::new();
3097        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3098        let big = vec![b'v'; 200];
3099
3100        for i in 0..400u32 {
3101            let n = i.to_string();
3102            let n = n.as_bytes();
3103            f.run(&[b"SADD", b"s", n]);
3104            f.run(&[b"SADD", b"s2", &big]);
3105            f.run(&[b"HSET", b"h", n, &big]);
3106            f.run(&[b"RPUSH", b"l", &big]);
3107            f.run(&[b"ZADD", b"z", n, n]);
3108            f.run(&[b"ARSET", b"a", n, &big]);
3109            if i % 7 == 0 {
3110                f.run(&[b"SREM", b"s", n]);
3111                f.run(&[b"HDEL", b"h", n]);
3112                f.run(&[b"LPOP", b"l"]);
3113                f.run(&[b"ZREM", b"z", n]);
3114                f.run(&[b"ARDEL", b"a", n]);
3115            }
3116            if i % 53 == 0 {
3117                // Every type deleted and made again, so a slot goes on the free
3118                // list and comes back holding something else.
3119                f.run(&[b"DEL", b"s2"]);
3120            }
3121            assert_eq!(
3122                f.server.settled_memory(),
3123                f.server.memory_bytes(),
3124                "after round {i}"
3125            );
3126        }
3127
3128        // The run has to have built something, or the two numbers agreeing is
3129        // two zeroes agreeing.
3130        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
3131        assert!(
3132            f.server.memory_bytes() > 512 * 1024,
3133            "{}",
3134            f.server.memory_bytes()
3135        );
3136
3137        // And it survives the collections going away entirely.
3138        f.run(&[b"FLUSHALL"]);
3139        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3140    }
3141
3142    #[test]
3143    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
3144        // A server with no limit does not keep the running total, so setting a
3145        // limit on a database that is already full has to start it from a walk.
3146        // If it did not, the first reading would be zero and the server would
3147        // think it had all the room in the world.
3148        let mut f = Fixture::new();
3149        for i in 0..200u32 {
3150            let n = i.to_string();
3151            f.run(&[b"SADD", b"s", n.as_bytes()]);
3152            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
3153        }
3154        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3155        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
3156
3157        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
3158        for i in 200..400u32 {
3159            let n = i.to_string();
3160            f.run(&[b"SADD", b"s", n.as_bytes()]);
3161        }
3162        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
3163        assert_eq!(
3164            f.server.settled_memory(),
3165            f.server.memory_bytes(),
3166            "the writes it was not watching are in the number it started from"
3167        );
3168    }
3169
3170    #[test]
3171    fn evicted_keys_and_expired_keys_are_different_numbers() {
3172        let mut f = Fixture::new();
3173        // Nothing has been evicted and nothing can be under the default policy,
3174        // so this stays at zero while the other one moves.
3175        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
3176        f.server.db(0).clock_mut().advance(20);
3177        f.run(&[b"GET", b"gone"]);
3178        let info = f.run(&[b"INFO", b"stats"]);
3179        assert!(info.contains("expired_keys:1"), "{info}");
3180        assert!(info.contains("evicted_keys:0"), "{info}");
3181    }
3182
3183    #[test]
3184    fn the_object_subcommands_follow_the_policy() {
3185        let mut f = Fixture::new();
3186        f.run(&[b"SET", b"s", b"v"]);
3187        // Under the default the clock is kept and the counter is not, and under
3188        // an LFU policy it is the other way round. Each subcommand refuses on
3189        // the side where its reading of the three bytes means nothing.
3190        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3191        assert!(
3192            f.run(&[b"OBJECT", b"FREQ", b"s"])
3193                .starts_with("-ERR An LFU maxmemory policy is not selected"),
3194        );
3195
3196        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
3197        assert!(
3198            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
3199                .starts_with("-ERR An LFU maxmemory policy is selected"),
3200        );
3201        // The key was written under a clock policy, so what comes back is that
3202        // clock read as a counter. It is a number and not an error, which is the
3203        // point: switching at runtime does not invalidate anything, it only makes
3204        // the old field mean something else until the key is used again.
3205        assert!(
3206            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
3207            "FREQ should answer under an LFU policy"
3208        );
3209    }
3210
3211    #[test]
3212    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
3213        let mut f = Fixture::new();
3214        f.run(&[b"SET", b"s", b"hello"]);
3215        f.run(&[b"SET", b"n", b"123"]);
3216        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
3217        f.run(&[b"SADD", b"ss", b"a", b"b"]);
3218        f.run(&[b"HSET", b"h", b"f", b"v"]);
3219        for (key, want) in [
3220            (b"s".as_slice(), "embstr"),
3221            (b"n", "int"),
3222            (b"si", "intset"),
3223            (b"ss", "listpack"),
3224            (b"h", "listpack"),
3225        ] {
3226            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
3227            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
3228        }
3229
3230        // A field deadline widens the blob rather than promoting it, and this
3231        // is the only place a client can see that happen.
3232        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
3233        assert_eq!(
3234            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3235            "$10\r\nlistpackex\r\n"
3236        );
3237
3238        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
3239        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
3240        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
3241    }
3242
3243    #[test]
3244    fn object_answers_nil_for_a_key_that_is_not_there() {
3245        let mut f = Fixture::new();
3246        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
3247            assert_eq!(
3248                f.run(&[b"OBJECT", sub, b"nokey"]),
3249                "$-1\r\n",
3250                "a nil and not an error, which is what 8.10.1 does"
3251            );
3252        }
3253        // And the key is looked up before FREQ has its complaint, so the
3254        // complaint only reaches a key that exists.
3255        f.run(&[b"SET", b"s", b"v"]);
3256        assert!(
3257            f.run(&[b"OBJECT", b"FREQ", b"s"])
3258                .starts_with("-ERR An LFU maxmemory policy is not"),
3259        );
3260        assert_eq!(
3261            f.run(&[b"OBJECT", b"NOPE", b"s"]),
3262            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
3263        );
3264        assert_eq!(
3265            f.run(&[b"OBJECT", b"ENCODING"]),
3266            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3267        );
3268        assert_eq!(
3269            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
3270            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
3271        );
3272        assert_eq!(
3273            f.run(&[b"OBJECT"]),
3274            "-ERR wrong number of arguments for 'object' command\r\n"
3275        );
3276    }
3277
3278    #[test]
3279    fn config_moves_the_ladder_and_object_encoding_agrees() {
3280        let mut f = Fixture::new();
3281        assert_eq!(
3282            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3283            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3284            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
3285        );
3286        // The old spelling is the same number under a different name, and a
3287        // glob that catches both sends both.
3288        assert_eq!(
3289            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
3290            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
3291        );
3292        assert!(
3293            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
3294                .starts_with("*8\r\n")
3295        );
3296        assert!(
3297            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
3298                .starts_with("*6\r\n")
3299        );
3300
3301        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
3302        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
3303
3304        assert_eq!(
3305            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
3306            "+OK\r\n",
3307            "written under the old name and read back under the new one"
3308        );
3309        assert_eq!(
3310            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3311            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
3312        );
3313        assert_eq!(
3314            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3315            "$8\r\nlistpack\r\n",
3316            "the hash that already exists is left exactly where it was"
3317        );
3318        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
3319        assert_eq!(
3320            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
3321            "$9\r\nhashtable\r\n",
3322            "and the next one built goes straight to a table"
3323        );
3324
3325        // The set has three of these and all three move.
3326        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
3327        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
3328        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
3329        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
3330        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
3331        assert_eq!(
3332            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
3333            "$9\r\nhashtable\r\n"
3334        );
3335    }
3336
3337    #[test]
3338    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
3339        let mut f = Fixture::new();
3340        assert_eq!(
3341            f.run(&[
3342                b"CONFIG",
3343                b"SET",
3344                b"hash-max-listpack-entries",
3345                b"7",
3346                b"set-max-listpack-entries",
3347                b"abc"
3348            ]),
3349            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
3350        );
3351        assert_eq!(
3352            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
3353            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3354            "the pair in front of the bad one did not go in"
3355        );
3356        // The name in the complaint is the one that was typed, so the old
3357        // spelling comes back as the old spelling.
3358        assert_eq!(
3359            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3360            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3361        );
3362        assert_eq!(
3363            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3364            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3365        );
3366        // A number past what an i64 holds is the parse complaint and not the
3367        // range one, which is upstream reading it before it checks it.
3368        assert_eq!(
3369            f.run(&[
3370                b"CONFIG",
3371                b"SET",
3372                b"set-max-intset-entries",
3373                b"99999999999999999999"
3374            ]),
3375            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3376        );
3377        assert_eq!(
3378            f.run(&[
3379                b"CONFIG",
3380                b"SET",
3381                b"set-max-intset-entries",
3382                b"9223372036854775807"
3383            ]),
3384            "+OK\r\n"
3385        );
3386    }
3387
3388    #[test]
3389    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3390        let mut f = Fixture::new();
3391        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3392        f.run(&[b"SELECT", b"3"]);
3393        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3394        assert_eq!(
3395            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3396            "$9\r\nhashtable\r\n",
3397            "these are one server wide number in Redis, whatever a Keyspace carries"
3398        );
3399    }
3400
3401    #[test]
3402    fn info_reports_the_numbers_it_can_stand_behind() {
3403        let mut f = Fixture::new();
3404        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3405        let all = f.run(&[b"INFO"]);
3406        assert!(all.contains("redis_version:8.8.0"), "{all}");
3407        assert!(
3408            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3409            "{all}"
3410        );
3411        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3412        assert!(all.contains("role:master"), "{all}");
3413        // One section is one section.
3414        let clients = f.run(&[b"INFO", b"clients"]);
3415        assert!(clients.contains("connected_clients:0"), "{clients}");
3416        assert!(!clients.contains("redis_version"), "{clients}");
3417        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3418    }
3419
3420    /// The sections a bare `INFO` gives back, and the ones you have to ask for.
3421    ///
3422    /// This is Redis's `unit/info-command` written against the fixture. Every
3423    /// assertion in it is one of theirs, in their order, and the two fields it
3424    /// turns on are the two that suite was failing on: `master_repl_offset`,
3425    /// which is in the default set, and `rejected_calls`, which is not.
3426    #[test]
3427    fn commandstats_is_asked_for_and_replication_is_not() {
3428        let mut f = Fixture::new();
3429        for arg in ["", "all", "default", "everything"] {
3430            let info = if arg.is_empty() {
3431                f.run(&[b"INFO"])
3432            } else {
3433                f.run(&[b"INFO", arg.as_bytes()])
3434            };
3435            assert!(info.contains("redis_version"), "{arg}: {info}");
3436            assert!(info.contains("used_cpu_user"), "{arg}: {info}");
3437            assert!(info.contains("used_memory"), "{arg}: {info}");
3438            assert!(!info.contains("sentinel_tilt"), "{arg}: {info}");
3439            let asked = arg == "all" || arg == "everything";
3440            assert_eq!(
3441                info.contains("rejected_calls"),
3442                asked,
3443                "{arg} should{} carry the command counters: {info}",
3444                if asked { "" } else { " not" }
3445            );
3446        }
3447
3448        let cpu = f.run(&[b"INFO", b"cpu"]);
3449        assert!(cpu.contains("used_cpu_user"), "{cpu}");
3450        assert!(!cpu.contains("used_memory"), "{cpu}");
3451
3452        // Their case, to make the point that a section name is not case
3453        // sensitive any more than a command name is.
3454        let stats = f.run(&[b"INFO", b"commandSTATS"]);
3455        assert!(!stats.contains("used_memory"), "{stats}");
3456        assert!(stats.contains("rejected_calls"), "{stats}");
3457
3458        // Two sections named, and neither of them pulls in a third.
3459        let pair = f.run(&[b"INFO", b"cpu", b"sentinel"]);
3460        assert!(pair.contains("used_cpu_user"), "{pair}");
3461        assert!(!pair.contains("master_repl_offset"), "{pair}");
3462
3463        let with_all = f.run(&[b"INFO", b"cpu", b"all"]);
3464        assert!(with_all.contains("used_memory"), "{with_all}");
3465        assert!(with_all.contains("master_repl_offset"), "{with_all}");
3466        assert!(with_all.contains("rejected_calls"), "{with_all}");
3467        // A section named twice is still written once.
3468        assert_eq!(
3469            with_all.matches("used_cpu_user_children").count(),
3470            1,
3471            "{with_all}"
3472        );
3473
3474        let with_default = f.run(&[b"INFO", b"cpu", b"default"]);
3475        assert!(with_default.contains("used_memory"), "{with_default}");
3476        assert!(
3477            with_default.contains("master_repl_offset"),
3478            "{with_default}"
3479        );
3480        assert!(!with_default.contains("rejected_calls"), "{with_default}");
3481        assert_eq!(
3482            with_default.matches("used_cpu_user_children").count(),
3483            1,
3484            "{with_default}"
3485        );
3486    }
3487
3488    /// The memory section says what this process may use, not what the machine
3489    /// has.
3490    ///
3491    /// The distinction is the whole point of it. A server inside a container
3492    /// that reports the host's memory is a server whose operator sizes it for
3493    /// memory it will be killed for touching, so all three numbers are there:
3494    /// what the machine has, what the cgroup allows, and the quarter of the
3495    /// tighter one that pools are sized from.
3496    #[test]
3497    fn info_memory_reports_the_cap_and_the_quarter_of_it_that_gets_used() {
3498        let mut f = Fixture::new();
3499        let info = f.run(&[b"INFO", b"memory"]);
3500        for field in [
3501            "total_system_memory:",
3502            "mem_cgroup_limit:",
3503            "mem_limit:",
3504            "mem_budget:",
3505        ] {
3506            assert!(info.contains(field), "no {field} in {info}");
3507        }
3508
3509        let field = |name: &str| -> u64 {
3510            info.lines()
3511                .find_map(|l| l.strip_prefix(name))
3512                .unwrap_or_else(|| panic!("no {name} in {info}"))
3513                .trim()
3514                .parse()
3515                .unwrap_or_else(|_| panic!("{name} is not a number in {info}"))
3516        };
3517        let limit = field("mem_limit:");
3518        assert_eq!(field("mem_budget:"), limit / 4, "{info}");
3519        // Zero means there is no limit to report, which is a real answer on a
3520        // machine with no cgroups and no way to ask how big it is.
3521        if limit != 0 {
3522            let host = field("total_system_memory:");
3523            let cgroup = field("mem_cgroup_limit:");
3524            assert!(
3525                limit == host || limit == cgroup,
3526                "the limit came from neither number: {info}"
3527            );
3528        }
3529    }
3530
3531    /// The three counters, each on the path that raises it.
3532    ///
3533    /// `calls` on a command that worked, `failed_calls` on one that ran and
3534    /// answered with an error, and `rejected_calls` on one that never ran at
3535    /// all. The last two are the pair that is easy to collapse into one number
3536    /// and that Redis keeps apart, because a client sending the wrong number of
3537    /// arguments and a client asking for a list element that is not there are
3538    /// not the same problem.
3539    #[test]
3540    fn a_command_counts_what_it_did_separately_from_what_it_refused() {
3541        let mut f = Fixture::new();
3542        f.run(&[b"SET", b"k", b"v"]);
3543        f.run(&[b"SET", b"k", b"w"]);
3544        // Ran, and answered with an error, because `k` is not a list.
3545        f.run(&[b"LPUSH", b"k", b"x"]);
3546        // Never ran: `LPUSH` takes at least three arguments.
3547        f.run(&[b"LPUSH", b"k"]);
3548
3549        let stats = f.run(&[b"INFO", b"commandstats"]);
3550        assert!(
3551            stats.contains("cmdstat_set:calls=2,rejected_calls=0,failed_calls=0"),
3552            "{stats}"
3553        );
3554        assert!(
3555            stats.contains("cmdstat_lpush:calls=1,rejected_calls=1,failed_calls=1"),
3556            "{stats}"
3557        );
3558        assert!(
3559            !stats.contains("cmdstat_zadd"),
3560            "a command nobody has sent has no row: {stats}"
3561        );
3562    }
3563
3564    /// A cache that writes with a deadline and never reads back used to hold
3565    /// every key it had ever written, because lazy expiry needs somebody to walk
3566    /// past a key before it can reclaim it and nobody ever did.
3567    #[test]
3568    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3569        let mut f = Fixture::new();
3570        for i in 0..3_000u32 {
3571            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3572        }
3573        for i in 0..1_000u32 {
3574            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3575        }
3576        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3577        f.advance(100);
3578        assert_eq!(
3579            f.run(&[b"DBSIZE"]),
3580            ":4000\r\n",
3581            "DBSIZE counts records and nothing has read past the dead ones yet"
3582        );
3583
3584        // What the shard loop does, one slice at a time.
3585        let mut spent = 0;
3586        for _ in 0..2_000 {
3587            spent += f.server.expire_step(4096);
3588            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3589                break;
3590            }
3591        }
3592        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3593        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3594        for i in 0..1_000u32 {
3595            assert_eq!(
3596                f.run(&[b"GET", format!("k{i}").as_bytes()]),
3597                "$1\r\nv\r\n",
3598                "it took a key that had no deadline"
3599            );
3600        }
3601    }
3602
3603    #[test]
3604    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3605        let mut f = Fixture::new();
3606        for i in 0..2_000u32 {
3607            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3608        }
3609        assert_eq!(f.server.expire_step(4096), 0);
3610        // And one database having them does not make the other fifteen pay.
3611        f.run(&[b"SELECT", b"3"]);
3612        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3613        f.advance(100);
3614        for _ in 0..64 {
3615            f.server.expire_step(4096);
3616        }
3617        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3618        f.run(&[b"SELECT", b"0"]);
3619        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3620        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3621    }
3622
3623    /// The gate, which is what stops a maintenance slice that runs every hundred
3624    /// nanoseconds from drawing a sample every hundred nanoseconds.
3625    #[test]
3626    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3627        let mut f = Fixture::new();
3628        for i in 0..500u32 {
3629            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3630        }
3631        f.advance(100);
3632        let at = f.server.db(0).clock().now_ms();
3633        f.server.set_clock_ms(at);
3634        // A small budget, so that one slice cannot finish the job and a second
3635        // one having nothing to do would mean the gate and not an empty
3636        // database.
3637        assert!(f.server.expire_slice(8) > 0, "the first one works");
3638        for _ in 0..1_000 {
3639            assert_eq!(
3640                f.server.expire_slice(8),
3641                0,
3642                "the millisecond has not moved and neither should this"
3643            );
3644        }
3645        assert!(
3646            f.server.db(0).expires() > 400,
3647            "there is plenty left to take"
3648        );
3649        f.server.set_clock_ms(at + 1);
3650        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3651    }
3652
3653    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
3654    /// how much of a cache is volatile was reading a constant.
3655    #[test]
3656    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3657        let mut f = Fixture::new();
3658        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3659        assert!(
3660            f.run(&[b"INFO", b"keyspace"])
3661                .contains("db0:keys=3,expires=0"),
3662            "none of them has one yet"
3663        );
3664        f.run(&[b"EXPIRE", b"a", b"1000"]);
3665        f.run(&[b"EXPIRE", b"b", b"1000"]);
3666        let two = f.run(&[b"INFO", b"keyspace"]);
3667        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3668        f.run(&[b"PERSIST", b"a"]);
3669        f.run(&[b"DEL", b"b"]);
3670        let none = f.run(&[b"INFO", b"keyspace"]);
3671        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3672
3673        // Each database answers for itself, the way Redis reports it.
3674        f.run(&[b"SELECT", b"1"]);
3675        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3676        let both = f.run(&[b"INFO", b"keyspace"]);
3677        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3678        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3679    }
3680
3681    #[cfg(unix)]
3682    #[test]
3683    fn info_cpu_reports_processor_time_that_was_really_measured() {
3684        let mut f = Fixture::new();
3685        let cpu = f.run(&[b"INFO", b"cpu"]);
3686        assert!(cpu.contains("# CPU"), "{cpu}");
3687        // Redis's unit/info-command asks for this one by name in three tests.
3688        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3689        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3690        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3691        assert!(!cpu.contains("redis_version"), "{cpu}");
3692
3693        // It is a measurement and not a constant, so it goes up when work
3694        // happens. A tight loop rather than a sleep, because sleeping is the
3695        // one thing that does not move this number.
3696        let before = used_cpu_user(&cpu);
3697        let mut n = 0u64;
3698        let mut rounds = 0;
3699        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3700            for i in 0..1_000_000u64 {
3701                n = n.wrapping_add(i.wrapping_mul(i));
3702            }
3703            rounds += 1;
3704            // A bound rather than a spin, so a platform where this number does
3705            // not move fails here instead of hanging. Even a clock with whole
3706            // millisecond granularity gets there in the first round or two.
3707            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3708        }
3709    }
3710
3711    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
3712    #[cfg(unix)]
3713    fn used_cpu_user(info: &str) -> f64 {
3714        info.lines()
3715            .find_map(|l| l.strip_prefix("used_cpu_user:"))
3716            .expect("no used_cpu_user in the reply")
3717            .trim()
3718            .parse()
3719            .expect("used_cpu_user is not a number")
3720    }
3721
3722    /// The safety net under the rule that a body checks its arguments before
3723    /// it writes anything. `MGET` writes its array header first and then reads
3724    /// each key, so if a later argument could fail the header would already be
3725    /// out. Nothing in the string group does that today and this is what would
3726    /// catch the first one that did.
3727    #[test]
3728    fn a_command_that_fails_leaves_nothing_half_written() {
3729        let mut f = Fixture::new();
3730        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
3731        assert_eq!(reply, "-ERR offset is out of range\r\n");
3732        assert!(!reply.contains(':'), "no integer went out in front of it");
3733    }
3734
3735    #[test]
3736    fn quit_answers_first_and_closes_after() {
3737        let mut f = Fixture::new();
3738        let (flow, reply) = f.flow(&[b"QUIT"]);
3739        assert_eq!(reply, "+OK\r\n");
3740        assert_eq!(flow, Flow::Close);
3741    }
3742
3743    #[test]
3744    fn the_command_counter_counts_every_command_including_the_bad_ones() {
3745        let mut f = Fixture::new();
3746        f.run(&[b"PING"]);
3747        f.run(&[b"NOPE"]);
3748        f.run(&[b"GET"]);
3749        assert_eq!(f.server.stats.commands, 3);
3750    }
3751
3752    #[test]
3753    fn a_set_goes_from_bytes_to_bytes() {
3754        let mut f = Fixture::new();
3755        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
3756        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
3757        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
3758        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
3759        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
3760        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
3761        assert_eq!(
3762            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
3763            "*3\r\n:1\r\n:0\r\n:1\r\n"
3764        );
3765        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
3766        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3767    }
3768
3769    #[test]
3770    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
3771        let mut f = Fixture::new();
3772        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
3773        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
3774        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
3775        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
3776        assert_eq!(
3777            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
3778            "*2\r\n:0\r\n:0\r\n"
3779        );
3780        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
3781    }
3782
3783    #[test]
3784    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
3785        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
3786        // and one that gets a `*` hands it a list, without either of them being
3787        // told which command was sent.
3788        let mut f = Fixture::new();
3789        f.run(&[b"SADD", b"s", b"one"]);
3790        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
3791
3792        f.run(&[b"HELLO", b"3"]);
3793        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
3794    }
3795
3796    #[test]
3797    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
3798        // An intset holds the number, so these digits exist for the first time
3799        // in the reply buffer.
3800        let mut f = Fixture::new();
3801        f.run(&[b"SADD", b"s", b"42"]);
3802        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
3803        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
3804        assert_eq!(
3805            f.run(&[b"SISMEMBER", b"s", b"042"]),
3806            ":0\r\n",
3807            "the member is the bytes and not the number they parse to"
3808        );
3809    }
3810
3811    #[test]
3812    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
3813        let mut f = Fixture::new();
3814        f.run(&[b"SET", b"str", b"v"]);
3815        f.run(&[b"SADD", b"set", b"a"]);
3816
3817        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3818        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
3819        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
3820        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
3821        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
3822        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
3823        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
3824        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
3825        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
3826
3827        // MGET is the one that does not, because Redis gives nil for the odd
3828        // key out rather than failing the good keys next to it.
3829        assert_eq!(
3830            f.run(&[b"MGET", b"str", b"set", b"nope"]),
3831            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
3832        );
3833        // And plain SET overwrites any type, which takes the body with it.
3834        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
3835        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
3836    }
3837
3838    #[test]
3839    fn a_wrongtype_leaves_nothing_half_written() {
3840        // SMISMEMBER writes an array header and then one reply per member, so
3841        // it is the first command in the server that could get a header out in
3842        // front of an error if it checked its key in the wrong order.
3843        let mut f = Fixture::new();
3844        f.run(&[b"SET", b"k", b"v"]);
3845        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
3846        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
3847        assert!(!reply.contains('*'), "an array header went out in front");
3848    }
3849
3850    #[test]
3851    fn emptying_a_set_takes_the_key_with_it() {
3852        let mut f = Fixture::new();
3853        f.run(&[b"SADD", b"s", b"a", b"b"]);
3854        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3855        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
3856        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3857        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
3858        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3859    }
3860
3861    /// Pull the cursor and the members out of one `SSCAN` reply.
3862    ///
3863    /// Crude on purpose. A test that walked a set through a real client would
3864    /// be testing the client, and what these tests are about is the shape of
3865    /// the bytes and the fact that a walk sees every member once.
3866    fn split_scan(reply: &str) -> (String, Vec<String>) {
3867        let mut lines = reply.split("\r\n");
3868        assert_eq!(lines.next(), Some("*2"), "got {reply}");
3869        lines.next().expect("the cursor header");
3870        let cursor = lines.next().expect("the cursor").to_owned();
3871        let header = lines.next().expect("the member header");
3872        let n: usize = header[1..].parse().expect("a member count");
3873        let mut members = Vec::with_capacity(n);
3874        for _ in 0..n {
3875            lines.next().expect("a member header");
3876            members.push(lines.next().expect("a member").to_owned());
3877        }
3878        (cursor, members)
3879    }
3880
3881    #[test]
3882    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
3883        let mut f = Fixture::new();
3884        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
3885
3886        let one = f.run(&[b"SPOP", b"s"]);
3887        assert!(
3888            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
3889            "got {one}"
3890        );
3891        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3892
3893        // A count takes that many, and the last one takes the key with it.
3894        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
3895        assert!(rest.starts_with("*3\r\n"), "got {rest}");
3896        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3897        // And a pop at a key that is not there is a nil, not an empty bulk.
3898        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
3899        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
3900    }
3901
3902    #[test]
3903    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
3904        // The one place in the server where the reply type carries something
3905        // the command name does not. SPOP's members are distinct so a RESP3
3906        // client can build a set out of them. SRANDMEMBER with a negative count
3907        // can hand back the same member three times, and a set would lose two.
3908        let mut f = Fixture::new();
3909        f.run(&[b"HELLO", b"3"]);
3910        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
3911
3912        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
3913        // And a positive count is an array too, since Redis makes it one.
3914        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
3915
3916        // A negative count against a set of one is where the difference bites:
3917        // the same member three times, which is a three element reply and would
3918        // have been a one element reply if it had gone out as a set.
3919        f.run(&[b"SADD", b"one", b"z"]);
3920        assert_eq!(
3921            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
3922            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
3923        );
3924    }
3925
3926    #[test]
3927    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
3928        let mut f = Fixture::new();
3929        f.run(&[b"SADD", b"s", b"only"]);
3930        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3931        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3932        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
3933
3934        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
3935        // The count form answers an empty array rather than a nil, which is the
3936        // pair of answers Redis gives and is not the pair it looks like.
3937        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
3938        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
3939        // Asking for more than is there answers all of it once and not padding.
3940        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
3941    }
3942
3943    #[test]
3944    fn a_pop_count_that_is_not_a_positive_number_says_so() {
3945        let mut f = Fixture::new();
3946        f.run(&[b"SADD", b"s", b"a"]);
3947        let bad = "-ERR value is out of range, must be positive\r\n";
3948        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
3949        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
3950        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
3951        // Zero is allowed and is a real answer rather than an error.
3952        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
3953        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
3954    }
3955
3956    #[test]
3957    fn a_scan_walks_a_set_of_any_size_exactly_once() {
3958        let mut f = Fixture::new();
3959        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
3960        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
3961            .into_iter()
3962            .chain(members.iter().map(Vec::as_slice))
3963            .collect();
3964        f.run(&args);
3965
3966        let mut seen = Vec::new();
3967        let mut cursor = "0".to_owned();
3968        loop {
3969            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
3970            let (next, got) = split_scan(&reply);
3971            seen.extend(got);
3972            cursor = next;
3973            if cursor == "0" {
3974                break;
3975            }
3976        }
3977        seen.sort();
3978        seen.dedup();
3979        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
3980
3981        // A set small enough to be a listpack answers in one call whatever
3982        // cursor it was handed, which is what Redis does for that encoding.
3983        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
3984        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
3985        assert_eq!(cursor, "0");
3986        assert_eq!(got.len(), 3);
3987        // And a key that is not there is a finished scan of nothing.
3988        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
3989    }
3990
3991    #[test]
3992    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
3993        let mut f = Fixture::new();
3994        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
3995
3996        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
3997        let mut got = got;
3998        got.sort();
3999        assert_eq!(got, ["aa", "ab"]);
4000
4001        // An integer member has no digits stored anywhere, so MATCH is the one
4002        // place a scan pays to write some.
4003        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
4004        let mut got = got;
4005        got.sort();
4006        assert_eq!(got, ["12", "13"]);
4007
4008        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
4009        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
4010        assert_eq!(
4011            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
4012            "-ERR syntax error\r\n"
4013        );
4014        // A count under one is a syntax error and not a range error, which is
4015        // the odder of Redis's two answers and the reason it is copied exactly.
4016        assert_eq!(
4017            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
4018            "-ERR syntax error\r\n"
4019        );
4020    }
4021
4022    #[test]
4023    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
4024        let mut f = Fixture::new();
4025        f.run(&[b"SADD", b"src", b"a", b"b"]);
4026        f.run(&[b"SADD", b"dst", b"c"]);
4027
4028        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
4029        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
4030        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
4031        // A member that is not in the source is a zero and moves nothing.
4032        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
4033        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
4034
4035        // A destination that does not exist gets made, and a source that runs
4036        // out goes away.
4037        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
4038        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
4039        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
4040    }
4041
4042    #[test]
4043    fn moving_checks_the_types_in_the_order_redis_checks_them() {
4044        // Not the order it looks like it should be. A source that is not there
4045        // answers zero without ever looking at the destination, so this is a
4046        // zero and not a WRONGTYPE even though the destination is a string.
4047        let mut f = Fixture::new();
4048        f.run(&[b"SET", b"str", b"v"]);
4049        f.run(&[b"SADD", b"set", b"a"]);
4050
4051        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4052        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
4053        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
4054        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
4055        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
4056        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
4057        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
4058        assert_eq!(
4059            f.run(&[b"SISMEMBER", b"set", b"a"]),
4060            ":1\r\n",
4061            "and none of that moved anything"
4062        );
4063    }
4064
4065    #[test]
4066    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4067        // SSCAN writes an outer array header before it walks, so it is the
4068        // command most likely to get bytes out in front of an error.
4069        let mut f = Fixture::new();
4070        f.run(&[b"SADD", b"s", b"a"]);
4071        for bad in [
4072            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
4073            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
4074            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
4075        ] {
4076            let reply = f.run(bad);
4077            assert!(reply.starts_with("-ERR"), "got {reply}");
4078            assert!(!reply.contains('*'), "an array header went out in front");
4079        }
4080    }
4081
4082    #[test]
4083    fn a_hash_writes_reads_and_deletes_its_fields() {
4084        let mut f = Fixture::new();
4085        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
4086        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
4087        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4088        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
4089        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
4090        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
4091        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
4092        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
4093        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
4094        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
4095
4096        // The value the client sent is `9`, so HGET h b must not find the `2`
4097        // that is a value. A search with a step of one would have.
4098        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
4099
4100        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
4101        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
4102        assert_eq!(
4103            f.run(&[b"EXISTS", b"h"]),
4104            ":0\r\n",
4105            "and losing the last field lost the key"
4106        );
4107    }
4108
4109    #[test]
4110    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
4111        let mut f = Fixture::new();
4112        f.run(&[b"HSET", b"h", b"a", b"1"]);
4113        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
4114        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
4115        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
4116        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
4117        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
4118
4119        f.run(&[b"HELLO", b"3"]);
4120        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
4121        assert_eq!(
4122            f.run(&[b"HGETALL", b"nokey"]),
4123            "%0\r\n",
4124            "a missing key is the empty hash and never a nil"
4125        );
4126        assert_eq!(
4127            f.run(&[b"HKEYS", b"h"]),
4128            "*1\r\n$1\r\na\r\n",
4129            "and the two that answer one side stay arrays"
4130        );
4131    }
4132
4133    #[test]
4134    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
4135        let mut f = Fixture::new();
4136        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
4137        assert_eq!(
4138            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
4139            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
4140            "the reply is positional, so b is a nil and not a gap"
4141        );
4142        assert_eq!(
4143            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
4144            "*2\r\n$-1\r\n$-1\r\n",
4145            "and a missing key is all nils rather than an empty array"
4146        );
4147
4148        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
4149        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
4150        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4151    }
4152
4153    #[test]
4154    fn a_hash_counts_up_and_says_so_when_it_cannot() {
4155        let mut f = Fixture::new();
4156        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
4157        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
4158        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
4159        assert_eq!(
4160            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
4161            "$4\r\n10.5\r\n",
4162            "a bulk string and not a double, on both protocols"
4163        );
4164
4165        f.run(&[b"HSET", b"h", b"s", b"words"]);
4166        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
4167        assert!(
4168            bad.starts_with("-ERR hash value is not an integer"),
4169            "{bad}"
4170        );
4171        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
4172        assert!(
4173            bad.starts_with("-ERR value is not an integer"),
4174            "a bad argument is not yet a hash value, {bad}"
4175        );
4176        assert_eq!(
4177            f.run(&[b"HGET", b"h", b"s"]),
4178            "$5\r\nwords\r\n",
4179            "and neither of them wrote anything"
4180        );
4181    }
4182
4183    #[test]
4184    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
4185        let mut f = Fixture::new();
4186        for i in 0..500 {
4187            let field = format!("field-{i}");
4188            let value = format!("value-{i}");
4189            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
4190        }
4191
4192        let mut seen: Vec<String> = Vec::new();
4193        let mut cursor = "0".to_owned();
4194        loop {
4195            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
4196            let (next, items) = scan_reply(&reply);
4197            assert_eq!(items.len() % 2, 0, "a pair went out half written");
4198            for pair in items.chunks(2) {
4199                assert_eq!(
4200                    pair[0].strip_prefix("field-"),
4201                    pair[1].strip_prefix("value-"),
4202                    "a field came back with someone else's value"
4203                );
4204                seen.push(pair[0].clone());
4205            }
4206            cursor = next;
4207            if cursor == "0" {
4208                break;
4209            }
4210        }
4211        seen.sort();
4212        seen.dedup();
4213        assert_eq!(seen.len(), 500, "every field once and only once");
4214
4215        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
4216        assert!(
4217            items.iter().all(|s| s.starts_with("field-")),
4218            "NOVALUES still sent the values"
4219        );
4220
4221        let (_, one) = scan_reply(&f.run(&[
4222            b"HSCAN",
4223            b"h",
4224            b"0",
4225            b"MATCH",
4226            b"field-499",
4227            b"COUNT",
4228            b"1000",
4229        ]));
4230        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
4231    }
4232
4233    #[test]
4234    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
4235        let mut f = Fixture::new();
4236        f.run(&[b"HSET", b"h", b"a", b"1"]);
4237        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
4238        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
4239        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
4240        assert_eq!(
4241            f.run(&[b"HRANDFIELD", b"h", b"3"]),
4242            "*1\r\n$1\r\na\r\n",
4243            "a positive count is capped at the size of the hash"
4244        );
4245        assert_eq!(
4246            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
4247            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
4248            "and a negative one repeats itself"
4249        );
4250        assert_eq!(
4251            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4252            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4253            "flat on RESP2"
4254        );
4255
4256        f.run(&[b"HELLO", b"3"]);
4257        assert_eq!(
4258            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
4259            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
4260            "and nested on RESP3, but still an array and never a map"
4261        );
4262    }
4263
4264    #[test]
4265    fn every_hash_command_says_wrongtype_and_writes_nothing() {
4266        let mut f = Fixture::new();
4267        f.run(&[b"SET", b"str", b"v"]);
4268        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4269
4270        for cmd in [
4271            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
4272            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
4273            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
4274            &[b"HGET".as_slice(), b"str", b"f"][..],
4275            &[b"HMGET".as_slice(), b"str", b"f"][..],
4276            &[b"HDEL".as_slice(), b"str", b"f"][..],
4277            &[b"HLEN".as_slice(), b"str"][..],
4278            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
4279            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
4280            &[b"HGETALL".as_slice(), b"str"][..],
4281            &[b"HKEYS".as_slice(), b"str"][..],
4282            &[b"HVALS".as_slice(), b"str"][..],
4283            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
4284            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
4285            &[b"HRANDFIELD".as_slice(), b"str"][..],
4286            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
4287            &[b"HSCAN".as_slice(), b"str", b"0"][..],
4288        ] {
4289            let reply = f.run(cmd);
4290            assert_eq!(reply, wrong, "{:?}", cmd[0]);
4291        }
4292        assert_eq!(
4293            f.run(&[b"GET", b"str"]),
4294            "$1\r\nv\r\n",
4295            "and none of them touched the value"
4296        );
4297    }
4298
4299    #[test]
4300    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
4301        let mut f = Fixture::new();
4302        f.run(&[b"HSET", b"h", b"f", b"v"]);
4303        for bad in [
4304            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
4305            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
4306            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
4307            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
4308        ] {
4309            let reply = f.run(bad);
4310            assert!(reply.starts_with("-ERR"), "got {reply}");
4311            assert!(!reply.contains('*'), "an array header went out in front");
4312        }
4313    }
4314
4315    #[test]
4316    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
4317        let mut f = Fixture::new();
4318        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4319        assert_eq!(
4320            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
4321            "*1\r\n:1\r\n"
4322        );
4323        assert_eq!(
4324            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4325            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
4326            "one answer per field, and the two sentinels are TTL's own"
4327        );
4328
4329        // The same deadline in the other three units, all of them derived from
4330        // the one number the store kept.
4331        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
4332        assert!((99_000..=100_000).contains(&ms), "got {ms}");
4333        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4334        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
4335        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
4336        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
4337
4338        assert_eq!(
4339            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
4340            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
4341            "one for the deadline taken off, and it does not say what it was"
4342        );
4343        assert_eq!(
4344            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4345            "*1\r\n:-1\r\n"
4346        );
4347        assert_eq!(
4348            f.run(&[b"HGET", b"h", b"a"]),
4349            "$1\r\n1\r\n",
4350            "and the field is still there with the value it had"
4351        );
4352    }
4353
4354    #[test]
4355    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
4356        let mut f = Fixture::new();
4357        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4358        assert_eq!(
4359            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
4360            "*1\r\n:2\r\n",
4361            "two, and not one, because nothing was stored"
4362        );
4363        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
4364        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4365
4366        assert_eq!(
4367            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
4368            "*1\r\n:2\r\n"
4369        );
4370        assert_eq!(
4371            f.run(&[b"EXISTS", b"h"]),
4372            ":0\r\n",
4373            "and the last field going took the key with it"
4374        );
4375
4376        // Zero is a delete and not an error, where minus one is an error. That
4377        // is Redis's split and it is easy to get backwards.
4378        f.run(&[b"HSET", b"h", b"a", b"1"]);
4379        assert_eq!(
4380            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
4381            "*1\r\n:2\r\n"
4382        );
4383    }
4384
4385    #[test]
4386    fn a_field_is_gone_once_its_moment_passes() {
4387        let mut f = Fixture::new();
4388        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4389        assert_eq!(
4390            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
4391            "*1\r\n:1\r\n"
4392        );
4393        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
4394
4395        // Time moves once per turn of the event loop and nowhere else, so a
4396        // test moves it by hand rather than by sleeping. There is nothing to
4397        // sleep for: the deadline is a number and so is the clock.
4398        f.server.db(0).clock_mut().advance(60);
4399        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4400        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
4401        assert_eq!(
4402            f.run(&[b"HGETALL", b"h"]),
4403            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
4404            "and the walks do not hand back a field that has expired"
4405        );
4406    }
4407
4408    #[test]
4409    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
4410        let mut f = Fixture::new();
4411        for cmd in [
4412            &[
4413                b"HEXPIRE".as_slice(),
4414                b"nokey",
4415                b"100",
4416                b"FIELDS",
4417                b"2",
4418                b"a",
4419                b"b",
4420            ][..],
4421            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
4422            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
4423            &[
4424                b"HEXPIRETIME".as_slice(),
4425                b"nokey",
4426                b"FIELDS",
4427                b"2",
4428                b"a",
4429                b"b",
4430            ][..],
4431            &[
4432                b"HPERSIST".as_slice(),
4433                b"nokey",
4434                b"FIELDS",
4435                b"2",
4436                b"a",
4437                b"b",
4438            ][..],
4439        ] {
4440            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
4441        }
4442    }
4443
4444    #[test]
4445    fn writing_a_field_clears_the_deadline_that_was_on_it() {
4446        let mut f = Fixture::new();
4447        f.run(&[b"HSET", b"h", b"a", b"1"]);
4448        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
4449        f.run(&[b"HSET", b"h", b"a", b"2"]);
4450        assert_eq!(
4451            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4452            "*1\r\n:-1\r\n",
4453            "Redis has done this since 7.4, and it is why HGETEX exists"
4454        );
4455    }
4456
4457    #[test]
4458    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
4459        let mut f = Fixture::new();
4460        f.run(&[b"HSET", b"h", b"a", b"1"]);
4461        assert_eq!(
4462            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
4463            "*1\r\n:0\r\n",
4464            "XX on a field with no deadline changes nothing"
4465        );
4466        assert_eq!(
4467            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
4468            "*1\r\n:1\r\n"
4469        );
4470        assert_eq!(
4471            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
4472            "*1\r\n:0\r\n",
4473            "and NX will not move one that is already there"
4474        );
4475        assert_eq!(
4476            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
4477            "*1\r\n:0\r\n"
4478        );
4479        assert_eq!(
4480            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
4481            "*1\r\n:1\r\n"
4482        );
4483        assert_eq!(
4484            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
4485            "*1\r\n:1\r\n"
4486        );
4487        assert_eq!(
4488            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4489            "*1\r\n:50\r\n"
4490        );
4491    }
4492
4493    #[test]
4494    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
4495        let mut f = Fixture::new();
4496        f.run(&[b"HSET", b"h", b"a", b"1"]);
4497        for (bad, want) in [
4498            (
4499                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
4500                "-ERR invalid expire time, must be >= 0",
4501            ),
4502            (
4503                &[
4504                    b"HEXPIRE".as_slice(),
4505                    b"h",
4506                    b"9999999999999999",
4507                    b"FIELDS",
4508                    b"1",
4509                    b"a",
4510                ][..],
4511                "-ERR invalid expire time in 'hexpire' command",
4512            ),
4513            (
4514                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
4515                "-ERR wrong number of arguments for 'hexpire' command",
4516            ),
4517            (
4518                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
4519                "-ERR Parameter `numFields` should be greater than 0",
4520            ),
4521            (
4522                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
4523                "-ERR wrong number of arguments",
4524            ),
4525            (
4526                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
4527                "-ERR wrong number of arguments",
4528            ),
4529        ] {
4530            let reply = f.run(bad);
4531            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4532            assert!(!reply.contains('*'), "an array header went out in front");
4533        }
4534        assert_eq!(
4535            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4536            "*1\r\n:-1\r\n",
4537            "and not one of them put a deadline on anything"
4538        );
4539    }
4540
4541    #[test]
4542    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
4543        let mut f = Fixture::new();
4544        f.run(&[b"SET", b"str", b"v"]);
4545        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4546
4547        for cmd in [
4548            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
4549            &[
4550                b"HPEXPIRE".as_slice(),
4551                b"str",
4552                b"100",
4553                b"FIELDS",
4554                b"1",
4555                b"f",
4556            ][..],
4557            &[
4558                b"HEXPIREAT".as_slice(),
4559                b"str",
4560                b"9999999999",
4561                b"FIELDS",
4562                b"1",
4563                b"f",
4564            ][..],
4565            &[
4566                b"HPEXPIREAT".as_slice(),
4567                b"str",
4568                b"9999999999999",
4569                b"FIELDS",
4570                b"1",
4571                b"f",
4572            ][..],
4573            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4574            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4575            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4576            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4577            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4578        ] {
4579            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4580        }
4581        assert_eq!(
4582            f.run(&[b"GET", b"str"]),
4583            "$1\r\nv\r\n",
4584            "and none of them touched the value"
4585        );
4586    }
4587
4588    #[test]
4589    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
4590        let mut f = Fixture::new();
4591        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4592        assert_eq!(
4593            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
4594            "*2\r\n$1\r\n1\r\n$-1\r\n",
4595            "positional, so the field that was not there is a nil in its place"
4596        );
4597        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4598        assert_eq!(
4599            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
4600            "*1\r\n$-1\r\n"
4601        );
4602        assert_eq!(
4603            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
4604            "*1\r\n$1\r\n2\r\n"
4605        );
4606        assert_eq!(
4607            f.run(&[b"EXISTS", b"h"]),
4608            ":0\r\n",
4609            "and the last field took the key"
4610        );
4611    }
4612
4613    #[test]
4614    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
4615        let mut f = Fixture::new();
4616        f.run(&[b"HSET", b"h", b"a", b"1"]);
4617        assert_eq!(
4618            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
4619            "*1\r\n$1\r\n1\r\n"
4620        );
4621        assert_eq!(
4622            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4623            "*1\r\n:-1\r\n",
4624            "no option means leave it alone, which is the one place this is not GETEX"
4625        );
4626
4627        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
4628        assert_eq!(
4629            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4630            "*1\r\n:100\r\n"
4631        );
4632        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
4633        assert_eq!(
4634            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4635            "*1\r\n:100\r\n",
4636            "and a plain read really does leave it alone"
4637        );
4638        assert_eq!(
4639            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
4640            "*1\r\n$1\r\n1\r\n"
4641        );
4642        assert_eq!(
4643            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4644            "*1\r\n:-1\r\n"
4645        );
4646
4647        assert_eq!(
4648            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
4649            "*1\r\n$1\r\n1\r\n",
4650            "the value goes out before the deadline that has already gone is applied"
4651        );
4652        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
4653        assert_eq!(
4654            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
4655            "*1\r\n$-1\r\n"
4656        );
4657    }
4658
4659    #[test]
4660    fn hsetex_writes_all_of_it_or_none_of_it() {
4661        let mut f = Fixture::new();
4662        assert_eq!(
4663            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
4664            ":1\r\n"
4665        );
4666        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4667        assert_eq!(
4668            f.run(&[
4669                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
4670            ]),
4671            ":0\r\n",
4672            "FNX wants every field named to be missing"
4673        );
4674        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4675        assert_eq!(
4676            f.run(&[b"HEXISTS", b"h", b"new"]),
4677            ":0\r\n",
4678            "and none of the list was written"
4679        );
4680        assert_eq!(
4681            f.run(&[
4682                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
4683            ]),
4684            ":0\r\n",
4685            "and FXX wants every one of them to be there"
4686        );
4687        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4688        assert_eq!(
4689            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
4690            ":1\r\n"
4691        );
4692        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4693
4694        assert_eq!(
4695            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
4696            ":0\r\n"
4697        );
4698        assert_eq!(
4699            f.run(&[b"EXISTS", b"gone"]),
4700            ":0\r\n",
4701            "a key with no fields cannot meet FXX and is not created trying"
4702        );
4703    }
4704
4705    #[test]
4706    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
4707        let mut f = Fixture::new();
4708        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
4709        assert_eq!(
4710            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4711            "*1\r\n:100\r\n"
4712        );
4713
4714        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
4715        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
4716        assert_eq!(
4717            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4718            "*1\r\n:100\r\n",
4719            "KEEPTTL put back what the write cleared"
4720        );
4721
4722        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
4723        assert_eq!(
4724            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4725            "*1\r\n:-1\r\n",
4726            "and without it a write clears the deadline the way HSET does"
4727        );
4728
4729        // Any order, because Redis reads these in a loop and not in a fixed
4730        // sequence.
4731        assert_eq!(
4732            f.run(&[
4733                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
4734            ]),
4735            ":1\r\n"
4736        );
4737        assert_eq!(
4738            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4739            "*1\r\n:100\r\n"
4740        );
4741
4742        assert_eq!(
4743            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
4744            ":1\r\n",
4745            "written, and not the separate code the HEXPIRE family has for this"
4746        );
4747        assert_eq!(
4748            f.run(&[b"EXISTS", b"h"]),
4749            ":0\r\n",
4750            "and storing it and then removing it emptied the hash"
4751        );
4752    }
4753
4754    #[test]
4755    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
4756        let mut f = Fixture::new();
4757        f.run(&[b"HSET", b"h", b"a", b"1"]);
4758        for (bad, want) in [
4759            // HGETDEL has three sentences of its own for these three mistakes.
4760            (
4761                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4762                "-ERR Number of fields must be a positive integer",
4763            ),
4764            (
4765                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4766                "-ERR The `numfields` parameter must match the number of arguments",
4767            ),
4768            (
4769                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4770                "-ERR Mandatory argument FIELDS is missing or not at the right position",
4771            ),
4772            // And HGETEX and HSETEX have three different ones between them.
4773            (
4774                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4775                "-ERR invalid number of fields",
4776            ),
4777            (
4778                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4779                "-ERR wrong number of arguments",
4780            ),
4781            (
4782                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4783                "-ERR unknown argument: FIELD",
4784            ),
4785            (
4786                &[
4787                    b"HGETEX".as_slice(),
4788                    b"h",
4789                    b"KEEPTTL",
4790                    b"FIELDS",
4791                    b"1",
4792                    b"a",
4793                ][..],
4794                "-ERR unknown argument: KEEPTTL",
4795            ),
4796            (
4797                &[
4798                    b"HGETEX".as_slice(),
4799                    b"h",
4800                    b"EX",
4801                    b"100",
4802                    b"PERSIST",
4803                    b"FIELDS",
4804                    b"1",
4805                    b"a",
4806                ][..],
4807                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
4808            ),
4809            (
4810                &[
4811                    b"HSETEX".as_slice(),
4812                    b"h",
4813                    b"EX",
4814                    b"1",
4815                    b"KEEPTTL",
4816                    b"FIELDS",
4817                    b"1",
4818                    b"a",
4819                    b"1",
4820                ][..],
4821                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
4822            ),
4823            (
4824                &[
4825                    b"HSETEX".as_slice(),
4826                    b"h",
4827                    b"FNX",
4828                    b"FXX",
4829                    b"FIELDS",
4830                    b"1",
4831                    b"a",
4832                    b"1",
4833                ][..],
4834                "-ERR Only one of FXX or FNX arguments can be specified",
4835            ),
4836            (
4837                &[
4838                    b"HSETEX".as_slice(),
4839                    b"h",
4840                    b"FIELDS",
4841                    b"2",
4842                    b"a",
4843                    b"1",
4844                    b"b",
4845                ][..],
4846                "-ERR wrong number of arguments",
4847            ),
4848            (
4849                &[
4850                    b"HGETEX".as_slice(),
4851                    b"h",
4852                    b"EX",
4853                    b"-1",
4854                    b"FIELDS",
4855                    b"1",
4856                    b"a",
4857                ][..],
4858                "-ERR invalid expire time, must be >= 0",
4859            ),
4860            (
4861                &[
4862                    b"HGETEX".as_slice(),
4863                    b"h",
4864                    b"PXAT",
4865                    b"99999999999999",
4866                    b"FIELDS",
4867                    b"1",
4868                    b"a",
4869                ][..],
4870                "-ERR invalid expire time in 'hgetex' command",
4871            ),
4872            (
4873                &[
4874                    b"HSETEX".as_slice(),
4875                    b"h",
4876                    b"EX",
4877                    b"abc",
4878                    b"FIELDS",
4879                    b"1",
4880                    b"a",
4881                    b"1",
4882                ][..],
4883                "-ERR value is not an integer or out of range",
4884            ),
4885        ] {
4886            let reply = f.run(bad);
4887            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4888            assert!(!reply.contains('*'), "an array header went out in front");
4889        }
4890        assert_eq!(
4891            f.run(&[b"HGET", b"h", b"a"]),
4892            "$1\r\n1\r\n",
4893            "and not one of them wrote anything"
4894        );
4895        assert_eq!(
4896            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4897            "*1\r\n:-1\r\n"
4898        );
4899    }
4900
4901    #[test]
4902    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
4903        let mut f = Fixture::new();
4904        f.run(&[b"SET", b"str", b"v"]);
4905        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4906        for cmd in [
4907            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4908            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4909            &[
4910                b"HGETEX".as_slice(),
4911                b"str",
4912                b"EX",
4913                b"100",
4914                b"FIELDS",
4915                b"1",
4916                b"f",
4917            ][..],
4918            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
4919        ] {
4920            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4921        }
4922        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
4923    }
4924
4925    /// The one integer of a single element array reply.
4926    /// The number out of a plain integer reply.
4927    ///
4928    /// [`int_reply`] is the same thing wrapped in a one element array, which is
4929    /// the shape every hash field command answers in.
4930    fn int(reply: &str) -> i64 {
4931        let body = reply
4932            .strip_prefix(':')
4933            .and_then(|s| s.strip_suffix("\r\n"))
4934            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
4935        body.parse().expect("an integer")
4936    }
4937
4938    fn int_reply(reply: &str) -> i64 {
4939        let body = reply
4940            .strip_prefix("*1\r\n:")
4941            .and_then(|s| s.strip_suffix("\r\n"))
4942            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
4943        body.parse().expect("an integer")
4944    }
4945
4946    /// The cursor and the flat items of a scan reply.
4947    fn scan_reply(reply: &str) -> (String, Vec<String>) {
4948        let mut lines = reply.split("\r\n");
4949        assert_eq!(lines.next(), Some("*2"), "got {reply}");
4950        lines.next().expect("the cursor header");
4951        let cursor = lines.next().expect("a cursor").to_owned();
4952        let header = lines.next().expect("an item count");
4953        let n: usize = header[1..].parse().expect("a count");
4954        let mut items = Vec::with_capacity(n);
4955        for _ in 0..n {
4956            lines.next().expect("an item header");
4957            items.push(lines.next().expect("an item").to_owned());
4958        }
4959        (cursor, items)
4960    }
4961
4962    /// The members of a set reply, sorted, since none of these promise an
4963    /// order and a test that asserted one would be asserting an accident.
4964    fn sorted(reply: &str) -> Vec<String> {
4965        let mut lines = reply.split("\r\n");
4966        let header = lines.next().expect("a header");
4967        assert!(
4968            header.starts_with('*') || header.starts_with('~'),
4969            "got {reply}"
4970        );
4971        let n: usize = header[1..].parse().expect("a member count");
4972        let mut got = Vec::with_capacity(n);
4973        for _ in 0..n {
4974            lines.next().expect("a member header");
4975            got.push(lines.next().expect("a member").to_owned());
4976        }
4977        got.sort();
4978        got
4979    }
4980
4981    #[test]
4982    fn the_algebra_answers_what_the_sets_share_and_do_not() {
4983        let mut f = Fixture::new();
4984        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4985        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4986        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
4987
4988        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
4989        assert_eq!(
4990            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
4991            ["1", "2", "3", "4", "5"]
4992        );
4993        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
4994        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
4995
4996        // A key that is not there is an empty set, which empties an
4997        // intersection and does nothing at all to a union.
4998        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
4999        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
5000        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
5001        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
5002    }
5003
5004    #[test]
5005    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
5006        let mut f = Fixture::new();
5007        f.run(&[b"SADD", b"a", b"x"]);
5008        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
5009        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
5010        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
5011
5012        f.run(&[b"HELLO", b"3"]);
5013        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
5014        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
5015        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
5016        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
5017    }
5018
5019    #[test]
5020    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
5021        let mut f = Fixture::new();
5022        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
5023        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
5024
5025        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
5026        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
5027        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
5028        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
5029        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
5030        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
5031
5032        // An empty answer deletes the destination rather than leaving an empty
5033        // set behind, and the destination may be one of the sources.
5034        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
5035        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5036        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
5037        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
5038
5039        // And a destination holding something else is overwritten, the same way
5040        // SET overwrites, rather than refused.
5041        f.run(&[b"SET", b"str", b"v"]);
5042        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
5043        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
5044    }
5045
5046    #[test]
5047    fn sintercard_counts_without_building_and_stops_at_a_limit() {
5048        let mut f = Fixture::new();
5049        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
5050        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
5051
5052        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
5053        assert_eq!(
5054            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
5055            ":2\r\n"
5056        );
5057        assert_eq!(
5058            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
5059            ":3\r\n",
5060            "a limit of zero is no limit"
5061        );
5062        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
5063        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
5064
5065        // The counted keys are what make its three error messages its own.
5066        assert_eq!(
5067            f.run(&[b"SINTERCARD", b"0", b"a"]),
5068            "-ERR numkeys should be greater than 0\r\n"
5069        );
5070        assert_eq!(
5071            f.run(&[b"SINTERCARD", b"abc", b"a"]),
5072            "-ERR numkeys should be greater than 0\r\n"
5073        );
5074        assert_eq!(
5075            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
5076            "-ERR Number of keys can't be greater than number of args\r\n"
5077        );
5078        assert_eq!(
5079            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
5080            "-ERR LIMIT can't be negative\r\n"
5081        );
5082        assert_eq!(
5083            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
5084            "-ERR syntax error\r\n"
5085        );
5086        // A key really can be called LIMIT, which is why the count exists.
5087        f.run(&[b"SADD", b"LIMIT", b"2"]);
5088        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
5089    }
5090
5091    #[test]
5092    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
5093        let mut f = Fixture::new();
5094        f.run(&[b"SADD", b"a", b"1"]);
5095        f.run(&[b"SADD", b"d", b"old"]);
5096        f.run(&[b"SET", b"str", b"v"]);
5097
5098        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5099        for bad in [
5100            &[b"SINTER".as_slice(), b"a", b"str"][..],
5101            &[b"SUNION".as_slice(), b"str"][..],
5102            &[b"SDIFF".as_slice(), b"a", b"str"][..],
5103            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
5104            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
5105            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
5106            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
5107        ] {
5108            let reply = f.run(bad);
5109            assert_eq!(reply, wrong, "for {:?}", bad[0]);
5110        }
5111        assert_eq!(
5112            f.run(&[b"SMEMBERS", b"d"]),
5113            "*1\r\n$3\r\nold\r\n",
5114            "and the destination was left alone every time"
5115        );
5116    }
5117
5118    /// The leak a set can spring that nothing on the wire would ever show: the
5119    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
5120    #[test]
5121    fn churning_sets_does_not_grow_the_server() {
5122        let mut f = Fixture::new();
5123        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
5124        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
5125            .chain(std::iter::once(&b"s"[..]))
5126            .chain(members.iter().map(Vec::as_slice))
5127            .collect();
5128
5129        f.run(&args);
5130        f.run(&[b"DEL", b"s"]);
5131        f.server.compact_step();
5132        let after_first = f.server.memory_bytes();
5133
5134        for _ in 0..200 {
5135            f.run(&args);
5136            f.run(&[b"DEL", b"s"]);
5137            f.server.compact_step();
5138        }
5139        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5140        assert!(
5141            f.server.memory_bytes() <= after_first * 2,
5142            "held {} after two hundred passes against {after_first} after one",
5143            f.server.memory_bytes()
5144        );
5145    }
5146
5147    // --------------------------------------------------------------- bitmaps
5148
5149    /// The two single bit commands, and the encoding rule underneath them.
5150    ///
5151    /// A write always leaves the value `raw` and a read never re-encodes, which
5152    /// is why the `int` key here is still `int` after a `GETBIT` and is `raw`
5153    /// with its first digit changed after a `SETBIT`.
5154    #[test]
5155    fn a_bit_is_written_and_read_back_and_a_write_unpacks_an_int() {
5156        let mut f = Fixture::new();
5157        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"1"]), ":0\r\n");
5158        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\n\u{1}\r\n");
5159        assert_eq!(f.run(&[b"GETBIT", b"k", b"7"]), ":1\r\n");
5160        assert_eq!(f.run(&[b"GETBIT", b"k", b"6"]), ":0\r\n");
5161        assert_eq!(f.run(&[b"GETBIT", b"k", b"100"]), ":0\r\n");
5162        assert_eq!(f.run(&[b"SETBIT", b"k", b"7", b"0"]), ":1\r\n");
5163
5164        // Writing a nought past the end still creates the key and still pads.
5165        assert_eq!(f.run(&[b"SETBIT", b"nk", b"0", b"0"]), ":0\r\n");
5166        assert_eq!(f.run(&[b"STRLEN", b"nk"]), ":1\r\n");
5167        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"nk"]), "$3\r\nraw\r\n");
5168
5169        f.run(&[b"SET", b"num", b"12345"]);
5170        assert_eq!(f.run(&[b"GETBIT", b"num", b"1"]), ":0\r\n");
5171        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nint\r\n");
5172        assert_eq!(f.run(&[b"SETBIT", b"num", b"1", b"1"]), ":0\r\n");
5173        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"num"]), "$3\r\nraw\r\n");
5174        assert_eq!(f.run(&[b"GET", b"num"]), "$5\r\nq2345\r\n");
5175    }
5176
5177    /// Counting, in bytes and in bits.
5178    ///
5179    /// The `0 -5 BIT` row is 25 on a real 8.10.1 and Redis's own documentation
5180    /// says 22 for it. The server is the thing being copied here.
5181    #[test]
5182    fn bits_are_counted_over_a_range_of_bytes_or_of_bits() {
5183        let mut f = Fixture::new();
5184        f.run(&[b"SET", b"mykey", b"foobar"]);
5185        assert_eq!(f.run(&[b"BITCOUNT", b"mykey"]), ":26\r\n");
5186        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"0", b"0"]), ":4\r\n");
5187        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"1", b"1"]), ":6\r\n");
5188        assert_eq!(
5189            f.run(&[b"BITCOUNT", b"mykey", b"1", b"1", b"BYTE"]),
5190            ":6\r\n"
5191        );
5192        assert_eq!(
5193            f.run(&[b"BITCOUNT", b"mykey", b"0", b"-5", b"BIT"]),
5194            ":25\r\n"
5195        );
5196        assert_eq!(
5197            f.run(&[b"BITCOUNT", b"mykey", b"5", b"30", b"BIT"]),
5198            ":17\r\n"
5199        );
5200        assert_eq!(f.run(&[b"BITCOUNT", b"nokey"]), ":0\r\n");
5201
5202        // A start past the end is left where it is and the end is pulled back,
5203        // so the range comes out backwards and counts nothing.
5204        assert_eq!(f.run(&[b"BITCOUNT", b"mykey", b"10", b"20"]), ":0\r\n");
5205
5206        // A lone start is a syntax error here, where BITPOS allows it.
5207        assert_eq!(
5208            f.run(&[b"BITCOUNT", b"mykey", b"0"]),
5209            "-ERR syntax error\r\n"
5210        );
5211        assert_eq!(
5212            f.run(&[b"BITCOUNT", b"mykey", b"0", b"1", b"NIB"]),
5213            "-ERR syntax error\r\n"
5214        );
5215    }
5216
5217    /// Searching, and the one place a miss is not minus one.
5218    ///
5219    /// A search for a nought that runs to the end of the string answers the
5220    /// length in bits, because the string is treated as if it had noughts after
5221    /// it forever. Give it an explicit end and it answers minus one instead.
5222    #[test]
5223    fn a_search_for_a_nought_past_the_end_answers_the_length_in_bits() {
5224        let mut f = Fixture::new();
5225        f.run(&[b"SET", b"ones", b"\xff\xff\xff"]);
5226        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0"]), ":24\r\n");
5227        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0"]), ":24\r\n");
5228        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"-1"]), ":-1\r\n");
5229        assert_eq!(f.run(&[b"BITPOS", b"ones", b"0", b"0", b"3"]), ":-1\r\n");
5230        assert_eq!(f.run(&[b"BITPOS", b"ones", b"1"]), ":0\r\n");
5231
5232        f.run(&[b"SET", b"mid", b"\x00\xff\xf0"]);
5233        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"0"]), ":8\r\n");
5234        assert_eq!(f.run(&[b"BITPOS", b"mid", b"1", b"2"]), ":16\r\n");
5235        assert_eq!(
5236            f.run(&[b"BITPOS", b"mid", b"1", b"0", b"-1", b"BIT"]),
5237            ":8\r\n"
5238        );
5239
5240        // A missing key is all noughts, so a one is never found and a nought is
5241        // at position zero.
5242        assert_eq!(f.run(&[b"BITPOS", b"gone", b"1"]), ":-1\r\n");
5243        assert_eq!(f.run(&[b"BITPOS", b"gone", b"0"]), ":0\r\n");
5244    }
5245
5246    /// The eight operations, with the answers a real server gives for them.
5247    #[test]
5248    fn the_eight_combinations_write_what_a_real_server_writes() {
5249        let mut f = Fixture::new();
5250        f.run(&[b"SET", b"a", b"abc"]);
5251        f.run(&[b"SET", b"b", b"abd"]);
5252        let cases: &[(&[u8], &str)] = &[
5253            (b"AND", "ab`"),
5254            (b"OR", "abg"),
5255            (b"XOR", "\u{0}\u{0}\u{7}"),
5256            (b"DIFF", "\u{0}\u{0}\u{3}"),
5257            (b"DIFF1", "\u{0}\u{0}\u{4}"),
5258            (b"ANDOR", "ab`"),
5259            (b"ONE", "\u{0}\u{0}\u{7}"),
5260        ];
5261        for (op, want) in cases {
5262            assert_eq!(f.run(&[b"BITOP", op, b"d", b"a", b"b"]), ":3\r\n", "{op:?}");
5263            assert_eq!(
5264                f.run(&[b"GET", b"d"]),
5265                format!("$3\r\n{want}\r\n"),
5266                "{op:?}"
5267            );
5268        }
5269        // The one whose answer is not text, so it is compared as bytes.
5270        assert_eq!(f.run(&[b"BITOP", b"NOT", b"d", b"a"]), ":3\r\n");
5271        assert_eq!(f.raw(&[b"GET", b"d"]), b"$3\r\n\x9e\x9d\x9c\r\n".to_vec());
5272
5273        // A missing source is a string of noughts as long as it needs to be, so
5274        // an AND against one writes three zero bytes rather than nothing.
5275        assert_eq!(f.run(&[b"BITOP", b"AND", b"d", b"a", b"gone"]), ":3\r\n");
5276        assert_eq!(f.run(&[b"GET", b"d"]), "$3\r\n\u{0}\u{0}\u{0}\r\n");
5277
5278        // Every source missing is an empty result, and an empty result takes
5279        // the destination with it.
5280        f.run(&[b"SET", b"dest", b"x"]);
5281        assert_eq!(f.run(&[b"BITOP", b"AND", b"dest", b"g1", b"g2"]), ":0\r\n");
5282        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
5283    }
5284
5285    /// What `BITOP` says when it is asked for something it cannot do.
5286    #[test]
5287    fn bitop_names_the_operation_in_its_own_complaints() {
5288        let mut f = Fixture::new();
5289        f.run(&[b"SET", b"a", b"abc"]);
5290        assert_eq!(
5291            f.run(&[b"BITOP", b"nope", b"d", b"a"]),
5292            "-ERR syntax error\r\n"
5293        );
5294        assert_eq!(
5295            f.run(&[b"BITOP", b"NOT", b"d", b"a", b"a"]),
5296            "-ERR BITOP NOT must be called with a single source key.\r\n"
5297        );
5298        for op in [&b"DIFF"[..], b"DIFF1", b"ANDOR"] {
5299            assert_eq!(
5300                f.run(&[b"BITOP", op, b"d", b"a"]),
5301                format!(
5302                    "-ERR BITOP {} must be called with at least two source keys.\r\n",
5303                    String::from_utf8_lossy(op)
5304                )
5305            );
5306        }
5307        f.run(&[b"LPUSH", b"l", b"x"]);
5308        assert_eq!(
5309            f.run(&[b"BITOP", b"AND", b"d", b"a", b"l"]),
5310            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
5311        );
5312    }
5313
5314    /// Packed fields, the three overflow policies and the `#` offset.
5315    #[test]
5316    fn bitfield_reads_and_writes_packed_fields() {
5317        let mut f = Fixture::new();
5318        assert_eq!(f.run(&[b"BITFIELD", b"bf"]), "*0\r\n");
5319        assert_eq!(f.run(&[b"EXISTS", b"bf"]), ":0\r\n");
5320
5321        assert_eq!(
5322            f.run(&[
5323                b"BITFIELD",
5324                b"bf",
5325                b"INCRBY",
5326                b"u2",
5327                b"100",
5328                b"1",
5329                b"GET",
5330                b"u4",
5331                b"0"
5332            ]),
5333            "*2\r\n:1\r\n:0\r\n"
5334        );
5335        // The field at bit 100 is two bits wide, so it ends in the thirteenth
5336        // byte and the value grew to thirteen bytes to hold it.
5337        assert_eq!(f.run(&[b"STRLEN", b"bf"]), ":13\r\n");
5338
5339        // A `#` offset counts in fields rather than in bits.
5340        assert_eq!(
5341            f.run(&[
5342                b"BITFIELD",
5343                b"bf",
5344                b"SET",
5345                b"u8",
5346                b"#0",
5347                b"255",
5348                b"GET",
5349                b"u8",
5350                b"#0"
5351            ]),
5352            "*2\r\n:0\r\n:255\r\n"
5353        );
5354
5355        assert_eq!(
5356            f.run(&[
5357                b"BITFIELD",
5358                b"bf",
5359                b"OVERFLOW",
5360                b"SAT",
5361                b"INCRBY",
5362                b"i8",
5363                b"0",
5364                b"120",
5365                b"INCRBY",
5366                b"i8",
5367                b"0",
5368                b"120"
5369            ]),
5370            "*2\r\n:119\r\n:127\r\n"
5371        );
5372        assert_eq!(
5373            f.run(&[
5374                b"BITFIELD",
5375                b"bf2",
5376                b"OVERFLOW",
5377                b"FAIL",
5378                b"INCRBY",
5379                b"u2",
5380                b"0",
5381                b"5"
5382            ]),
5383            "*1\r\n$-1\r\n"
5384        );
5385        assert_eq!(
5386            f.run(&[
5387                b"BITFIELD",
5388                b"bf3",
5389                b"OVERFLOW",
5390                b"WRAP",
5391                b"INCRBY",
5392                b"u2",
5393                b"0",
5394                b"5"
5395            ]),
5396            "*1\r\n:1\r\n"
5397        );
5398        assert_eq!(
5399            f.run(&[b"BITFIELD", b"bf3", b"GET", b"i64", b"0"]),
5400            "*1\r\n:4611686018427387904\r\n"
5401        );
5402    }
5403
5404    /// A bad subcommand anywhere in the line stops all of it.
5405    ///
5406    /// Redis checks the whole argument list before it runs any of it, so the
5407    /// `SET` in front of the bad type here never happens and the key it would
5408    /// have created is not there afterwards.
5409    #[test]
5410    fn a_bad_bitfield_subcommand_leaves_the_key_alone() {
5411        let mut f = Fixture::new();
5412        let bad_type = "-ERR Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.\r\n";
5413        assert_eq!(
5414            f.run(&[
5415                b"BITFIELD",
5416                b"bad",
5417                b"SET",
5418                b"u8",
5419                b"0",
5420                b"1",
5421                b"GET",
5422                b"u99",
5423                b"0"
5424            ]),
5425            bad_type
5426        );
5427        assert_eq!(f.run(&[b"EXISTS", b"bad"]), ":0\r\n");
5428        assert_eq!(
5429            f.run(&[b"BITFIELD", b"bad", b"GET", b"u64", b"0"]),
5430            bad_type
5431        );
5432        assert_eq!(
5433            f.run(&[b"BITFIELD", b"bad", b"GET"]),
5434            "-ERR syntax error\r\n"
5435        );
5436        assert_eq!(
5437            f.run(&[b"BITFIELD", b"bad", b"NOPE", b"u8", b"0"]),
5438            "-ERR syntax error\r\n"
5439        );
5440        assert_eq!(
5441            f.run(&[b"BITFIELD", b"bad", b"OVERFLOW"]),
5442            "-ERR syntax error\r\n"
5443        );
5444        assert_eq!(
5445            f.run(&[
5446                b"BITFIELD",
5447                b"bad",
5448                b"OVERFLOW",
5449                b"NOPE",
5450                b"GET",
5451                b"u8",
5452                b"0"
5453            ]),
5454            "-ERR Invalid OVERFLOW type specified\r\n"
5455        );
5456        assert_eq!(
5457            f.run(&[b"BITFIELD", b"bad", b"SET", b"u8", b"0", b"notanum"]),
5458            "-ERR value is not an integer or out of range\r\n"
5459        );
5460        for at in [&b"#-1"[..], b"abc"] {
5461            assert_eq!(
5462                f.run(&[b"BITFIELD", b"bad", b"GET", b"u8", at]),
5463                "-ERR bit offset is not an integer or out of range\r\n"
5464            );
5465        }
5466    }
5467
5468    /// The read only twin reads, refuses to write, and creates nothing.
5469    #[test]
5470    fn bitfield_ro_answers_gets_and_refuses_the_rest() {
5471        let mut f = Fixture::new();
5472        f.run(&[b"SET", b"n", b"123"]);
5473        assert_eq!(
5474            f.run(&[b"BITFIELD_RO", b"n", b"GET", b"u8", b"0"]),
5475            "*1\r\n:49\r\n"
5476        );
5477        // A read does not unpack an int the way a write does.
5478        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"n"]), "$3\r\nint\r\n");
5479
5480        // An OVERFLOW word is allowed even though nothing here can overflow.
5481        assert_eq!(
5482            f.run(&[
5483                b"BITFIELD_RO",
5484                b"n",
5485                b"OVERFLOW",
5486                b"SAT",
5487                b"GET",
5488                b"u8",
5489                b"0"
5490            ]),
5491            "*1\r\n:49\r\n"
5492        );
5493        for sub in [&b"SET"[..], b"INCRBY"] {
5494            assert_eq!(
5495                f.run(&[b"BITFIELD_RO", b"n", sub, b"u8", b"0", b"1"]),
5496                "-ERR BITFIELD_RO only supports the GET subcommand\r\n"
5497            );
5498        }
5499
5500        assert_eq!(
5501            f.run(&[b"BITFIELD_RO", b"gone", b"GET", b"u8", b"100"]),
5502            "*1\r\n:0\r\n"
5503        );
5504        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
5505    }
5506
5507    /// The offsets a bitmap command will not take.
5508    #[test]
5509    fn an_offset_off_the_end_of_the_world_is_refused() {
5510        let mut f = Fixture::new();
5511        let bad = "-ERR bit offset is not an integer or out of range\r\n";
5512        for arg in [&b"abc"[..], b"-1", b"4294967296"] {
5513            assert_eq!(f.run(&[b"SETBIT", b"k", arg, b"1"]), bad);
5514            assert_eq!(f.run(&[b"GETBIT", b"k", arg]), bad);
5515        }
5516        for arg in [&b"2"[..], b"-1"] {
5517            assert_eq!(
5518                f.run(&[b"BITPOS", b"k", arg]),
5519                "-ERR The bit argument must be 1 or 0.\r\n"
5520            );
5521        }
5522        assert_eq!(
5523            f.run(&[b"BITPOS", b"k", b"abc"]),
5524            "-ERR value is not an integer or out of range\r\n"
5525        );
5526        assert_eq!(
5527            f.run(&[b"BITPOS", b"k", b"0", b"5", b"BIT"]),
5528            "-ERR value is not an integer or out of range\r\n"
5529        );
5530        let bad_bit = "-ERR bit is not an integer or out of range\r\n";
5531        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"2"]), bad_bit);
5532        assert_eq!(f.run(&[b"SETBIT", b"k", b"0", b"abc"]), bad_bit);
5533    }
5534
5535    /// Every one of the seven refuses a key that is not a string.
5536    #[test]
5537    fn every_bitmap_command_says_wrongtype() {
5538        let mut f = Fixture::new();
5539        f.run(&[b"LPUSH", b"l", b"x"]);
5540        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5541        let cases: &[&[&[u8]]] = &[
5542            &[b"SETBIT", b"l", b"0", b"1"],
5543            &[b"GETBIT", b"l", b"0"],
5544            &[b"BITCOUNT", b"l"],
5545            &[b"BITPOS", b"l", b"1"],
5546            &[b"BITOP", b"AND", b"d", b"l"],
5547            &[b"BITFIELD", b"l", b"GET", b"u8", b"0"],
5548            &[b"BITFIELD_RO", b"l", b"GET", b"u8", b"0"],
5549        ];
5550        for case in cases {
5551            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
5552        }
5553    }
5554
5555    // --------------------------------------------------------- hyperloglogs
5556
5557    #[test]
5558    fn a_sketch_is_added_to_and_counted() {
5559        let mut f = Fixture::new();
5560        // Creating the key counts as a change, even with nothing to add.
5561        assert_eq!(f.run(&[b"PFADD", b"h"]), ":1\r\n");
5562        assert_eq!(f.run(&[b"PFADD", b"h"]), ":0\r\n");
5563        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":0\r\n");
5564        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":18\r\n");
5565        // And it is a string, which is not an implementation detail: a client
5566        // can `GET` a sketch out of one server and `SET` it into another.
5567        assert_eq!(f.run(&[b"TYPE", b"h"]), "+string\r\n");
5568        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$3\r\nraw\r\n");
5569
5570        assert_eq!(f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]), ":1\r\n");
5571        assert_eq!(f.run(&[b"PFADD", b"h", b"a"]), ":0\r\n");
5572        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
5573    }
5574
5575    #[test]
5576    fn the_bytes_of_a_sketch_are_the_ones_a_real_server_writes() {
5577        let mut f = Fixture::new();
5578        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
5579        // Not text, so it is compared as bytes.
5580        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";
5581        let mut reply = b"$27\r\n".to_vec();
5582        reply.extend_from_slice(want);
5583        reply.extend_from_slice(b"\r\n");
5584        assert_eq!(f.raw(&[b"GET", b"h"]), reply);
5585    }
5586
5587    #[test]
5588    fn counting_several_keys_counts_their_union() {
5589        let mut f = Fixture::new();
5590        f.run(&[b"PFADD", b"a", b"x", b"y"]);
5591        f.run(&[b"PFADD", b"b", b"y", b"z"]);
5592        assert_eq!(f.run(&[b"PFCOUNT", b"a"]), ":2\r\n");
5593        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"b"]), ":3\r\n");
5594        // A key that is not there is an empty sketch, not an error and not
5595        // something that gets created by being counted.
5596        assert_eq!(f.run(&[b"PFCOUNT", b"gone"]), ":0\r\n");
5597        assert_eq!(f.run(&[b"PFCOUNT", b"a", b"gone"]), ":2\r\n");
5598        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
5599    }
5600
5601    #[test]
5602    fn a_merge_keeps_what_the_destination_had() {
5603        let mut f = Fixture::new();
5604        f.run(&[b"PFADD", b"a", b"x", b"y"]);
5605        f.run(&[b"PFADD", b"b", b"z"]);
5606        assert_eq!(f.run(&[b"PFMERGE", b"d", b"a", b"b"]), "+OK\r\n");
5607        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":3\r\n");
5608        // The destination is one of the sources, so a second merge adds to it.
5609        f.run(&[b"PFADD", b"c", b"w"]);
5610        assert_eq!(f.run(&[b"PFMERGE", b"d", b"c"]), "+OK\r\n");
5611        assert_eq!(f.run(&[b"PFCOUNT", b"d"]), ":4\r\n");
5612        // And with no sources it is a no-op that still answers OK and still
5613        // creates a destination that was not there.
5614        assert_eq!(f.run(&[b"PFMERGE", b"fresh"]), "+OK\r\n");
5615        assert_eq!(f.run(&[b"PFCOUNT", b"fresh"]), ":0\r\n");
5616    }
5617
5618    #[test]
5619    fn the_debug_forms_answer_four_different_shapes() {
5620        let mut f = Fixture::new();
5621        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
5622        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+sparse\r\n");
5623        assert_eq!(
5624            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
5625            "$44\r\nZ:8436 v:1,1 Z:4274 v:2,1 Z:3068 v:1,1 Z:603\r\n"
5626        );
5627        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":1\r\n");
5628        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"h"]), ":0\r\n");
5629        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"h"]), "+dense\r\n");
5630        assert_eq!(f.run(&[b"STRLEN", b"h"]), ":12304\r\n");
5631        assert_eq!(f.run(&[b"PFCOUNT", b"h"]), ":3\r\n");
5632        // A dense sketch has no opcodes left to print.
5633        assert_eq!(
5634            f.run(&[b"PFDEBUG", b"DECODE", b"h"]),
5635            "-ERR HLL encoding is not sparse\r\n"
5636        );
5637
5638        // All 16384 registers, of which three are not nought.
5639        let reply = f.run(&[b"PFDEBUG", b"GETREG", b"h"]);
5640        assert!(reply.starts_with("*16384\r\n"), "{}", &reply[..16]);
5641        assert_eq!(reply.matches(":0\r\n").count(), 16381);
5642        assert_eq!(reply.matches(":1\r\n").count(), 2);
5643        assert_eq!(reply.matches(":2\r\n").count(), 1);
5644
5645        assert_eq!(f.run(&[b"PFSELFTEST"]), "+OK\r\n");
5646    }
5647
5648    #[test]
5649    fn a_string_that_is_not_a_sketch_is_refused_with_its_own_sentence() {
5650        let mut f = Fixture::new();
5651        f.run(&[b"SET", b"plain", b"not a sketch"]);
5652        let not_hll = "-WRONGTYPE Key is not a valid HyperLogLog string value.\r\n";
5653        assert_eq!(f.run(&[b"PFADD", b"plain", b"a"]), not_hll);
5654        assert_eq!(f.run(&[b"PFCOUNT", b"plain"]), not_hll);
5655        assert_eq!(f.run(&[b"PFMERGE", b"plain"]), not_hll);
5656        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"plain"]), not_hll);
5657
5658        // A key that is not a string at all gets the ordinary sentence, and a
5659        // destination that would have been written is not created.
5660        f.run(&[b"RPUSH", b"l", b"x"]);
5661        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5662        assert_eq!(f.run(&[b"PFADD", b"l", b"a"]), wrong);
5663        assert_eq!(f.run(&[b"PFCOUNT", b"l"]), wrong);
5664        assert_eq!(f.run(&[b"PFMERGE", b"dest", b"l"]), wrong);
5665        assert_eq!(f.run(&[b"EXISTS", b"dest"]), ":0\r\n");
5666        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"l"]), wrong);
5667    }
5668
5669    #[test]
5670    fn pfdebug_has_its_own_complaints() {
5671        let mut f = Fixture::new();
5672        f.run(&[b"PFADD", b"h", b"a"]);
5673        // The word is quoted exactly as the client spelled it, and this is not
5674        // the "Try X HELP." sentence every other container command uses.
5675        assert_eq!(
5676            f.run(&[b"PFDEBUG", b"NOPE", b"h"]),
5677            "-ERR Unknown PFDEBUG subcommand 'NOPE'\r\n"
5678        );
5679        // Where all three of the real commands take a missing key as empty.
5680        let gone = "-ERR The specified key does not exist\r\n";
5681        assert_eq!(f.run(&[b"PFDEBUG", b"GETREG", b"missing"]), gone);
5682        assert_eq!(f.run(&[b"PFDEBUG", b"DECODE", b"missing"]), gone);
5683        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"missing"]), gone);
5684        assert_eq!(f.run(&[b"PFDEBUG", b"TODENSE", b"missing"]), gone);
5685        assert_eq!(
5686            f.run(&[b"PFDEBUG"]),
5687            "-ERR wrong number of arguments for 'pfdebug' command\r\n"
5688        );
5689        assert_eq!(
5690            f.run(&[b"PFSELFTEST", b"x"]),
5691            "-ERR wrong number of arguments for 'pfselftest' command\r\n"
5692        );
5693    }
5694
5695    #[test]
5696    fn a_sketch_whose_opcodes_do_not_add_up_says_so() {
5697        let mut f = Fixture::new();
5698        f.run(&[b"PFADD", b"h", b"a", b"b", b"c"]);
5699        // The sketch with its last byte cut off, which is still a header and a
5700        // magic and is a run length encoding that stops short of register 16384.
5701        let reply = f.raw(&[b"GET", b"h"]);
5702        let short = reply[5..reply.len() - 3].to_vec();
5703        f.run(&[b"SET", b"h", &short]);
5704        assert_eq!(
5705            f.run(&[b"PFCOUNT", b"h"]),
5706            "-INVALIDOBJ Corrupted HLL object detected\r\n"
5707        );
5708    }
5709
5710    #[test]
5711    fn a_sketch_survives_a_dump_and_a_restore_in_both_encodings() {
5712        let mut f = Fixture::new();
5713        // One that stays sparse and one that has gone dense, since the payload
5714        // carries the bytes and the two encodings are different lengths.
5715        f.run(&[b"PFADD", b"small", b"a", b"b", b"c"]);
5716        for i in 0..10_000u32 {
5717            let ele = format!("e{i}");
5718            f.run(&[b"PFADD", b"big", ele.as_bytes()]);
5719        }
5720        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"small"]), "+sparse\r\n");
5721        assert_eq!(f.run(&[b"PFDEBUG", b"ENCODING", b"big"]), "+dense\r\n");
5722
5723        for key in [&b"small"[..], b"big"] {
5724            let mut copy = key.to_vec();
5725            copy.push(b'2');
5726            let bytes = payload(&f.raw(&[b"DUMP", key]));
5727            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
5728            // The bytes, the encoding and the estimate all come back, which is
5729            // the whole of what byte compatibility across a round trip means.
5730            assert_eq!(f.raw(&[b"GET", &copy]), f.raw(&[b"GET", key]));
5731            assert_eq!(
5732                f.run(&[b"PFDEBUG", b"ENCODING", &copy]),
5733                f.run(&[b"PFDEBUG", b"ENCODING", key])
5734            );
5735            assert_eq!(f.run(&[b"PFCOUNT", &copy]), f.run(&[b"PFCOUNT", key]));
5736        }
5737        assert_eq!(f.run(&[b"PFCOUNT", b"small2"]), ":3\r\n");
5738        assert_eq!(f.run(&[b"STRLEN", b"big2"]), ":12304\r\n");
5739    }
5740
5741    /// A RESP2 array of bulk strings, which is what most of the list replies
5742    /// are and what writing them out by hand in every assertion looks like.
5743    fn bulks(parts: &[&str]) -> String {
5744        let mut s = format!("*{}\r\n", parts.len());
5745        for p in parts {
5746            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
5747        }
5748        s
5749    }
5750
5751    #[test]
5752    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
5753        let mut f = Fixture::new();
5754        // Each element in turn goes at the head, so the last one sent is at the
5755        // front when it is over. That reads like a bug in the client and it is
5756        // what every Redis has always done.
5757        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
5758        assert_eq!(
5759            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5760            bulks(&["c", "b", "a"])
5761        );
5762        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
5763        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
5764        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
5765        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
5766        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
5767        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
5768    }
5769
5770    #[test]
5771    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
5772        let mut f = Fixture::new();
5773        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
5774        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
5775        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5776        f.run(&[b"RPUSH", b"k", b"a"]);
5777        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
5778        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
5779        assert_eq!(
5780            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5781            bulks(&["z", "a", "y"])
5782        );
5783    }
5784
5785    /// The four ways a pop can come back with nothing, which are three
5786    /// different replies and a RESP2 client can tell all of them apart.
5787    #[test]
5788    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
5789        let mut f = Fixture::new();
5790        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
5791        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
5792        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
5793        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
5794        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5795        // A count of zero against a list that is there is an empty array and
5796        // not a null array, which is the fourth answer.
5797        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
5798        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
5799        // More than there is takes what there is and the key goes with it.
5800        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
5801        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5802    }
5803
5804    #[test]
5805    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
5806        let mut f = Fixture::new();
5807        f.run(&[b"RPUSH", b"k", b"a"]);
5808        let range = "-ERR value is out of range, must be positive\r\n";
5809        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
5810        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
5811        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
5812        // Redis calls this an arity error and not a syntax error, which is a
5813        // distinction it does not always make.
5814        assert_eq!(
5815            f.run(&[b"LPOP", b"k", b"1", b"2"]),
5816            "-ERR wrong number of arguments for 'lpop' command\r\n"
5817        );
5818        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
5819    }
5820
5821    #[test]
5822    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
5823        let mut f = Fixture::new();
5824        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5825        assert_eq!(
5826            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5827            bulks(&["a", "b", "c"])
5828        );
5829        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
5830        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
5831        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
5832        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
5833        assert_eq!(
5834            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
5835            bulks(&["a", "b", "c"])
5836        );
5837        // A key that is not there is an empty range and not a nil, which is the
5838        // one place a list disagrees with a set.
5839        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
5840        assert_eq!(
5841            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
5842            "-ERR value is not an integer or out of range\r\n"
5843        );
5844    }
5845
5846    #[test]
5847    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
5848        let mut f = Fixture::new();
5849        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5850        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
5851        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
5852        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
5853        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
5854        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
5855        assert_eq!(
5856            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5857            bulks(&["a", "b", "z"])
5858        );
5859        // Both ways of missing are errors here rather than a nil, because a
5860        // list is never empty and there is nothing else the reply could be.
5861        assert_eq!(
5862            f.run(&[b"LSET", b"k", b"99", b"z"]),
5863            "-ERR index out of range\r\n"
5864        );
5865        assert_eq!(
5866            f.run(&[b"LSET", b"nope", b"0", b"z"]),
5867            "-ERR no such key\r\n"
5868        );
5869    }
5870
5871    #[test]
5872    fn linsert_says_three_things_with_one_signed_number() {
5873        let mut f = Fixture::new();
5874        // Zero for a key that is not there, which is not the same as minus one
5875        // for a pivot that is not in a list that is.
5876        assert_eq!(
5877            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
5878            ":0\r\n"
5879        );
5880        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
5881        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
5882        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
5883        assert_eq!(
5884            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5885            bulks(&["X", "a", "b", "Y"])
5886        );
5887        assert_eq!(
5888            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
5889            ":-1\r\n"
5890        );
5891        assert_eq!(
5892            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
5893            "-ERR syntax error\r\n"
5894        );
5895    }
5896
5897    #[test]
5898    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
5899        let mut f = Fixture::new();
5900        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
5901        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
5902        assert_eq!(
5903            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
5904            bulks(&["b", "c", "a"])
5905        );
5906        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
5907        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
5908        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
5909        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
5910        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5911        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
5912    }
5913
5914    #[test]
5915    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
5916        let mut f = Fixture::new();
5917        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
5918        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
5919        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
5920        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
5921        // leave `EXISTS` answering zero rather than leaving an empty one.
5922        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
5923        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
5924        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
5925    }
5926
5927    #[test]
5928    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
5929        let mut f = Fixture::new();
5930        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
5931        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
5932        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
5933        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
5934        assert_eq!(
5935            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
5936            "*2\r\n:0\r\n:3\r\n"
5937        );
5938        assert_eq!(
5939            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
5940            "*3\r\n:6\r\n:3\r\n:0\r\n"
5941        );
5942        // MAXLEN counts elements looked at and not matches found, so three
5943        // stops after `a b c` and finds the one match in it.
5944        assert_eq!(
5945            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
5946            "*1\r\n:0\r\n"
5947        );
5948        // Nothing found is three different replies depending on how it was
5949        // asked and whether the key is there at all.
5950        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
5951        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
5952        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
5953        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
5954    }
5955
5956    #[test]
5957    fn lpos_words_its_three_mistakes_the_way_redis_does() {
5958        let mut f = Fixture::new();
5959        f.run(&[b"RPUSH", b"p", b"a"]);
5960        // The whole sentence and not a prefix, because the older wording of it
5961        // is still all over the internet and clients match on the text.
5962        assert_eq!(
5963            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
5964            "-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"
5965        );
5966        assert_eq!(
5967            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
5968            "-ERR COUNT can't be negative\r\n"
5969        );
5970        assert_eq!(
5971            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
5972            "-ERR MAXLEN can't be negative\r\n"
5973        );
5974        assert_eq!(
5975            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
5976            "-ERR syntax error\r\n"
5977        );
5978        assert_eq!(
5979            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
5980            "-ERR syntax error\r\n"
5981        );
5982    }
5983
5984    #[test]
5985    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
5986        let mut f = Fixture::new();
5987        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
5988        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
5989        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
5990        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
5991        assert_eq!(
5992            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
5993            "$1\r\na\r\n"
5994        );
5995        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
5996        // The same key twice is the documented way to rotate a list and falls
5997        // out of taking the element before deciding where to put it.
5998        f.run(&[b"DEL", b"r"]);
5999        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
6000        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
6001        assert_eq!(
6002            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
6003            bulks(&["3", "1", "2"])
6004        );
6005        assert_eq!(
6006            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
6007            "$-1\r\n"
6008        );
6009        assert_eq!(
6010            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
6011            "-ERR syntax error\r\n"
6012        );
6013    }
6014
6015    #[test]
6016    fn a_move_checks_the_destination_before_it_takes_anything() {
6017        let mut f = Fixture::new();
6018        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
6019        f.run(&[b"SET", b"str", b"v"]);
6020        assert_eq!(
6021            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
6022            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
6023        );
6024        // The element is still where it was, rather than having gone nowhere.
6025        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
6026    }
6027
6028    #[test]
6029    fn lmpop_answers_from_the_first_key_that_has_anything() {
6030        let mut f = Fixture::new();
6031        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
6032        // The name of the key that answered comes back with the elements,
6033        // because the client cannot work out which one it was.
6034        assert_eq!(
6035            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
6036            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
6037        );
6038        assert_eq!(
6039            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
6040            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
6041        );
6042        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
6043        // A null array and not a null, even though what it stands in for is an
6044        // array holding a key name and then another array.
6045        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
6046    }
6047
6048    #[test]
6049    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
6050        let mut f = Fixture::new();
6051        f.run(&[b"RPUSH", b"k", b"a"]);
6052        assert_eq!(
6053            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
6054            "-ERR numkeys should be greater than 0\r\n"
6055        );
6056        assert_eq!(
6057            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
6058            "-ERR numkeys should be greater than 0\r\n"
6059        );
6060        assert_eq!(
6061            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
6062            "-ERR count should be greater than 0\r\n"
6063        );
6064        // A key count that eats the direction is a syntax error and not a
6065        // sentence about key counts, because the direction is simply not there.
6066        assert_eq!(
6067            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
6068            "-ERR syntax error\r\n"
6069        );
6070        assert_eq!(
6071            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
6072            "-ERR syntax error\r\n"
6073        );
6074        assert_eq!(
6075            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
6076            "-ERR syntax error\r\n"
6077        );
6078        assert_eq!(
6079            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
6080            "-ERR syntax error\r\n"
6081        );
6082        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
6083    }
6084
6085    #[test]
6086    fn every_list_command_says_wrongtype_and_writes_nothing() {
6087        let mut f = Fixture::new();
6088        f.run(&[b"SET", b"str", b"v"]);
6089        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6090        for cmd in [
6091            &[b"LPUSH".as_slice(), b"str", b"a"][..],
6092            &[b"RPUSH", b"str", b"a"],
6093            &[b"LPUSHX", b"str", b"a"],
6094            &[b"RPUSHX", b"str", b"a"],
6095            &[b"LPOP", b"str"],
6096            &[b"LPOP", b"str", b"2"],
6097            &[b"RPOP", b"str"],
6098            &[b"LLEN", b"str"],
6099            &[b"LRANGE", b"str", b"0", b"-1"],
6100            &[b"LINDEX", b"str", b"0"],
6101            &[b"LSET", b"str", b"0", b"a"],
6102            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
6103            &[b"LREM", b"str", b"0", b"a"],
6104            &[b"LTRIM", b"str", b"0", b"-1"],
6105            &[b"LPOS", b"str", b"a"],
6106            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
6107            &[b"RPOPLPUSH", b"str", b"d"],
6108            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
6109            &[b"LMPOP", b"1", b"str", b"LEFT"],
6110        ] {
6111            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
6112        }
6113        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
6114        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6115    }
6116
6117    /// A timeout is not an integer and it is not an ordinary float either: the
6118    /// three sentences it can answer with are its own, and which one a given
6119    /// argument gets is not what reading the code would suggest.
6120    #[test]
6121    fn a_timeout_has_three_ways_of_being_wrong() {
6122        let mut f = Fixture::new();
6123        let not_float = "-ERR timeout is not a float or out of range\r\n";
6124        let range = "-ERR timeout is out of range\r\n";
6125        for (bad, want) in [
6126            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
6127            (&[b"BLPOP", b"k", b"nan"], not_float),
6128            (&[b"BLPOP", b"k", b""], not_float),
6129            // Whitespace on either side, which `strtold` would take and Redis
6130            // does not.
6131            (&[b"BLPOP", b"k", b" 1"], not_float),
6132            (&[b"BLPOP", b"k", b"1 "], not_float),
6133            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
6134            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
6135            // These three parse, so they are not the not-a-float error, and all
6136            // three are further off than an i64 of milliseconds reaches.
6137            (&[b"BLPOP", b"k", b"1e400"], range),
6138            (&[b"BLPOP", b"k", b"inf"], range),
6139            (&[b"BLPOP", b"k", b"9999999999999999"], range),
6140            (&[b"BRPOP", b"k", b"abc"], not_float),
6141            (
6142                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
6143                not_float,
6144            ),
6145            (
6146                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
6147                "-ERR timeout is negative\r\n",
6148            ),
6149            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
6150        ] {
6151            assert_eq!(f.run(bad), want, "for {bad:?}");
6152        }
6153    }
6154
6155    /// A timeout of exactly zero means no timeout, and there are two ways of
6156    /// writing exactly zero.
6157    #[test]
6158    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
6159        let mut f = Fixture::new();
6160        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
6161            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
6162            assert_eq!(flow, Flow::Block, "for {timeout:?}");
6163            assert!(out.is_empty(), "for {timeout:?}");
6164        }
6165        // Positive, so it is a real deadline, and the deadline is this
6166        // millisecond. Nothing is written here either: the reply comes from the
6167        // sweep, which is the engine's and not this layer's.
6168        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
6169        assert_eq!(flow, Flow::Block);
6170        assert!(out.is_empty());
6171    }
6172
6173    #[test]
6174    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
6175        let mut f = Fixture::new();
6176        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
6177
6178        // The one difference from LPOP: the reply names the key that answered,
6179        // which is what makes BLPOP over several keys usable.
6180        assert_eq!(
6181            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
6182            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
6183        );
6184        assert_eq!(
6185            f.run(&[b"BRPOP", b"L", b"0"]),
6186            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
6187        );
6188        assert_eq!(
6189            f.run(&[
6190                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
6191            ]),
6192            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6193        );
6194        assert_eq!(
6195            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
6196            "$1\r\nd\r\n"
6197        );
6198        assert_eq!(
6199            f.run(&[b"EXISTS", b"L"]),
6200            ":0\r\n",
6201            "and the key went with it"
6202        );
6203        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
6204        // Onto itself, which is how a list is rotated and is a real thing to ask
6205        // a blocking move for.
6206        f.run(&[b"RPUSH", b"D", b"x"]);
6207        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
6208        assert_eq!(
6209            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
6210            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
6211        );
6212    }
6213
6214    #[test]
6215    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
6216        let mut f = Fixture::new();
6217        f.run(&[b"RPUSH", b"k", b"a"]);
6218        for (bad, want) in [
6219            (
6220                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
6221                "-ERR numkeys should be greater than 0\r\n",
6222            ),
6223            (
6224                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
6225                "-ERR numkeys should be greater than 0\r\n",
6226            ),
6227            // Two keys named and one given, so the word that should have been
6228            // the direction is a key and there is no direction left.
6229            (
6230                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
6231                "-ERR syntax error\r\n",
6232            ),
6233            (
6234                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
6235                "-ERR syntax error\r\n",
6236            ),
6237            (
6238                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
6239                "-ERR syntax error\r\n",
6240            ),
6241            (
6242                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
6243                "-ERR syntax error\r\n",
6244            ),
6245            // A count that is not a number at all gets the same sentence a zero
6246            // or a negative one gets, rather than the usual one about integers.
6247            (
6248                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
6249                "-ERR count should be greater than 0\r\n",
6250            ),
6251            (
6252                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
6253                "-ERR count should be greater than 0\r\n",
6254            ),
6255        ] {
6256            assert_eq!(f.run(bad), want, "for {bad:?}");
6257        }
6258        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
6259    }
6260
6261    #[test]
6262    fn a_blocking_move_reads_its_directions_before_its_timeout() {
6263        let mut f = Fixture::new();
6264        // Both are wrong. Redis checks the directions first, so this is the
6265        // syntax error and not a complaint about the timeout.
6266        assert_eq!(
6267            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
6268            "-ERR syntax error\r\n"
6269        );
6270        assert_eq!(
6271            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
6272            "-ERR syntax error\r\n"
6273        );
6274    }
6275
6276    /// The four ways a blocking command sees a key of another type, and the one
6277    /// way it does not.
6278    #[test]
6279    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
6280        let mut f = Fixture::new();
6281        f.run(&[b"SET", b"S", b"v"]);
6282        f.run(&[b"RPUSH", b"D", b"x"]);
6283        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6284
6285        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
6286        // Every key is checked even when an earlier one would have blocked, so
6287        // an empty key in front of a string does not hide it.
6288        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
6289        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
6290        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
6291        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
6292        // The destination, which is only reached because the source has
6293        // something in it.
6294        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
6295        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
6296
6297        // And the one that does not: an empty source means the destination is
6298        // never looked at, so this waits rather than erroring, and on a real
6299        // server it times out.
6300        assert_eq!(
6301            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
6302                .0,
6303            Flow::Block
6304        );
6305    }
6306
6307    /// The same churn the set and the string get, because a list that leaks a
6308    /// chunk per push looks exactly like one that does not until it has run for
6309    /// an afternoon.
6310    #[test]
6311    fn churning_lists_does_not_grow_the_server() {
6312        let mut f = Fixture::new();
6313        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
6314        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
6315            .into_iter()
6316            .chain(vals.iter().map(Vec::as_slice))
6317            .collect();
6318
6319        f.run(&args);
6320        f.run(&[b"DEL", b"k"]);
6321        f.server.compact_step();
6322        let after_first = f.server.memory_bytes();
6323
6324        for _ in 0..200 {
6325            f.run(&args);
6326            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
6327            f.server.compact_step();
6328        }
6329        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6330        assert!(
6331            f.server.memory_bytes() <= after_first * 2,
6332            "held {} after two hundred passes against {after_first} after one",
6333            f.server.memory_bytes()
6334        );
6335    }
6336
6337    // ------------------------------------------------------------ sorted set
6338
6339    #[test]
6340    fn a_sorted_set_takes_scores_and_gives_them_back() {
6341        let mut f = Fixture::new();
6342        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
6343        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
6344        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
6345        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
6346        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
6347        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
6348        assert_eq!(
6349            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
6350            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
6351        );
6352        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
6353        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
6354        // The key goes when the last member does.
6355        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
6356        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6357    }
6358
6359    #[test]
6360    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
6361        let mut f = Fixture::new();
6362        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
6363        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
6364        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
6365        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
6366
6367        f.out = Out::new(Proto::Resp3);
6368        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
6369        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
6370        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
6371        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
6372    }
6373
6374    #[test]
6375    fn the_zadd_options_gate_what_gets_written() {
6376        let mut f = Fixture::new();
6377        f.run(&[b"ZADD", b"z", b"5", b"a"]);
6378        // NX leaves a member that is there alone, XX will not create one.
6379        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
6380        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
6381        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
6382        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
6383        // GT and LT only move a score one way.
6384        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
6385        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
6386        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
6387        // CH counts a moved score and plain ZADD does not.
6388        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
6389        assert_eq!(
6390            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
6391            ":2\r\n"
6392        );
6393    }
6394
6395    #[test]
6396    fn zadd_incr_answers_a_score_or_nothing_at_all() {
6397        let mut f = Fixture::new();
6398        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
6399        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
6400        // A gate that refuses is the string nil, because the reply it stands in
6401        // for is a score.
6402        assert_eq!(
6403            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
6404            "$-1\r\n"
6405        );
6406        assert_eq!(
6407            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
6408            "$-1\r\n"
6409        );
6410        assert_eq!(
6411            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
6412            "$-1\r\n"
6413        );
6414        assert_eq!(
6415            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
6416            "$1\r\n8\r\n"
6417        );
6418        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
6419        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
6420    }
6421
6422    #[test]
6423    fn the_two_infinities_will_not_be_added_together() {
6424        let mut f = Fixture::new();
6425        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
6426        let nan = "-ERR resulting score is not a number (NaN)\r\n";
6427        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
6428        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
6429        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
6430        // And a key made for an increment that then fails does not stay behind.
6431        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
6432    }
6433
6434    #[test]
6435    fn zadd_says_its_mistakes_the_way_redis_says_them() {
6436        let mut f = Fixture::new();
6437        // The pairs are counted before the options are looked at, so this is a
6438        // syntax error about having none and not a complaint about NX and XX.
6439        assert_eq!(
6440            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
6441            "-ERR syntax error\r\n"
6442        );
6443        assert_eq!(
6444            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
6445            "-ERR XX and NX options at the same time are not compatible\r\n"
6446        );
6447        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
6448        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
6449        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
6450        assert_eq!(
6451            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
6452            "-ERR INCR option supports a single increment-element pair\r\n"
6453        );
6454        // An odd number of arguments after the options.
6455        assert_eq!(
6456            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
6457            "-ERR syntax error\r\n"
6458        );
6459        // Every score is read before the first is stored.
6460        assert_eq!(
6461            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
6462            "-ERR value is not a valid float\r\n"
6463        );
6464        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6465    }
6466
6467    #[test]
6468    fn a_rank_says_where_a_member_sits_from_either_end() {
6469        let mut f = Fixture::new();
6470        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6471        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
6472        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
6473        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
6474        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
6475        // WITHSCORE changes both shapes: the answer and the nothing.
6476        assert_eq!(
6477            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
6478            "*2\r\n:1\r\n$1\r\n2\r\n"
6479        );
6480        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
6481        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
6482        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
6483        // A bad option is a syntax error and one argument too many is an arity
6484        // error, which is Redis's split.
6485        assert_eq!(
6486            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
6487            "-ERR syntax error\r\n"
6488        );
6489        assert_eq!(
6490            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
6491            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
6492        );
6493    }
6494
6495    #[test]
6496    fn the_two_counts_read_their_two_kinds_of_bound() {
6497        let mut f = Fixture::new();
6498        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6499        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
6500        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
6501        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
6502        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
6503        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
6504        assert_eq!(
6505            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
6506            "-ERR min or max is not a float\r\n"
6507        );
6508
6509        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
6510        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
6511        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
6512        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
6513        // A bare member is not a bound, because a member can start with any
6514        // byte and there would be no way to say the bracket if it were optional.
6515        assert_eq!(
6516            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
6517            "-ERR min or max not valid string range item\r\n"
6518        );
6519    }
6520
6521    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
6522    ///
6523    /// Every byte in here was read off a real 8.10.1 rather than worked out,
6524    /// because the interesting part of this command is not what it selects, it
6525    /// is which of the two ends the client is expected to name first.
6526    #[test]
6527    fn one_range_command_selects_by_rank_or_score_or_name() {
6528        let mut f = Fixture::new();
6529        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6530        assert_eq!(
6531            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
6532            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6533        );
6534        assert_eq!(
6535            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
6536            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6537        );
6538        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
6539        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
6540        // REV over ranks reverses the walk and leaves the two arguments alone,
6541        // because a rank counts from the end the walk starts at.
6542        assert_eq!(
6543            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
6544            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
6545        );
6546        assert_eq!(
6547            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
6548            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6549        );
6550        // And REV over scores does swap them, since a bound does not count from
6551        // anywhere. This is the one line of the parse that tells the two apart.
6552        assert_eq!(
6553            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
6554            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
6555        );
6556        assert_eq!(
6557            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
6558            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6559        );
6560        assert_eq!(
6561            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
6562            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
6563        );
6564    }
6565
6566    /// The older spellings, which are the same six windows with the mode in the
6567    /// name and the high end named first on the three that go backwards.
6568    #[test]
6569    fn the_older_range_spellings_name_their_high_end_first() {
6570        let mut f = Fixture::new();
6571        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6572        assert_eq!(
6573            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
6574            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
6575        );
6576        assert_eq!(
6577            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
6578            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6579        );
6580        assert_eq!(
6581            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
6582            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6583        );
6584        assert_eq!(
6585            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
6586            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
6587        );
6588        // The two arguments the wrong way round is an empty answer and not an
6589        // error, which is what the swap being in the parse rather than in the
6590        // window buys.
6591        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
6592        assert_eq!(
6593            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
6594            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
6595        );
6596        assert_eq!(
6597            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
6598            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
6599        );
6600        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
6601        // way of spelling the mode, they are a syntax error.
6602        for cmd in [
6603            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
6604            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
6605            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
6606        ] {
6607            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
6608        }
6609    }
6610
6611    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
6612    /// only some of them accept.
6613    #[test]
6614    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
6615        let mut f = Fixture::new();
6616        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6617        assert_eq!(
6618            f.run(&[
6619                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
6620            ]),
6621            "*1\r\n$1\r\nb\r\n"
6622        );
6623        // A negative offset skips past everything, a negative count is no bound.
6624        assert_eq!(
6625            f.run(&[
6626                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
6627            ]),
6628            "*0\r\n"
6629        );
6630        assert_eq!(
6631            f.run(&[
6632                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
6633            ]),
6634            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6635        );
6636        // The two options in either order, which falls out of the parse loop.
6637        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";
6638        assert_eq!(
6639            f.run(&[
6640                b"ZRANGEBYSCORE",
6641                b"z",
6642                b"1",
6643                b"3",
6644                b"WITHSCORES",
6645                b"LIMIT",
6646                b"0",
6647                b"2"
6648            ]),
6649            both
6650        );
6651        assert_eq!(
6652            f.run(&[
6653                b"ZRANGEBYSCORE",
6654                b"z",
6655                b"1",
6656                b"3",
6657                b"LIMIT",
6658                b"0",
6659                b"2",
6660                b"WITHSCORES"
6661            ]),
6662            both
6663        );
6664        // LIMIT on a range by rank is refused after the whole option list has
6665        // been read, so this complains about LIMIT and not about WITHSCORES.
6666        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
6667        assert_eq!(
6668            f.run(&[
6669                b"ZREVRANGE",
6670                b"z",
6671                b"0",
6672                b"-1",
6673                b"WITHSCORES",
6674                b"LIMIT",
6675                b"0",
6676                b"1"
6677            ]),
6678            needs_by
6679        );
6680        assert_eq!(
6681            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
6682            needs_by
6683        );
6684        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
6685        assert_eq!(
6686            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
6687            not_bylex
6688        );
6689        assert_eq!(
6690            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
6691            not_bylex
6692        );
6693        // Two modes at once, an option nobody knows, a LIMIT missing its count,
6694        // and the three number errors, which are three different sentences.
6695        for cmd in [
6696            &[
6697                b"ZRANGE".as_slice(),
6698                b"z",
6699                b"0",
6700                b"-1",
6701                b"BYSCORE",
6702                b"BYLEX",
6703            ][..],
6704            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
6705            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
6706        ] {
6707            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
6708        }
6709        assert_eq!(
6710            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
6711            "-ERR min or max is not a float\r\n"
6712        );
6713        assert_eq!(
6714            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
6715            "-ERR min or max not valid string range item\r\n"
6716        );
6717        assert_eq!(
6718            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
6719            "-ERR value is not an integer or out of range\r\n"
6720        );
6721    }
6722
6723    /// `WITHSCORES` is the one place in this group where the two protocols
6724    /// disagree about the shape of the reply and not just the type of a value.
6725    #[test]
6726    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
6727        let mut f = Fixture::new();
6728        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6729        assert_eq!(
6730            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
6731            "*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"
6732        );
6733        f.out = Out::new(Proto::Resp3);
6734        assert_eq!(
6735            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
6736            "*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"
6737        );
6738        assert_eq!(
6739            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
6740            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
6741        );
6742    }
6743
6744    /// The store form, which is the same parse with the destination in front.
6745    #[test]
6746    fn a_range_store_writes_the_window_into_another_key() {
6747        let mut f = Fixture::new();
6748        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6749        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
6750        // A window that selects nothing deletes the destination rather than
6751        // leaving an empty sorted set, because an empty one does not exist.
6752        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
6753        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6754        assert_eq!(
6755            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
6756            ":2\r\n"
6757        );
6758        assert_eq!(
6759            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
6760            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6761        );
6762        // The destination is allowed to be the source, because the result is
6763        // built whole before anything is written over.
6764        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
6765        assert_eq!(
6766            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
6767            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6768        );
6769        // It takes every option ZRANGE takes except WITHSCORES, which is a
6770        // plain syntax error here and not the sentence about BYLEX.
6771        assert_eq!(
6772            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
6773            "-ERR syntax error\r\n"
6774        );
6775    }
6776
6777    /// The three removals, which are the read side's window with the walk
6778    /// turned into a removal and no options at all.
6779    #[test]
6780    fn the_three_removals_share_their_window_with_the_reads() {
6781        let mut f = Fixture::new();
6782        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6783        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
6784        assert_eq!(
6785            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
6786            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
6787        );
6788        assert_eq!(
6789            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
6790            ":1\r\n"
6791        );
6792        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
6793        // The last member going takes the key with it.
6794        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
6795        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6796        assert_eq!(
6797            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
6798            ":0\r\n"
6799        );
6800        assert_eq!(
6801            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
6802            "-ERR value is not an integer or out of range\r\n"
6803        );
6804    }
6805
6806    /// The algebra, which is one gather and three names for it.
6807    #[test]
6808    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
6809        let mut f = Fixture::new();
6810        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6811        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
6812        assert_eq!(
6813            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
6814            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
6815        );
6816        // The scores are added where a member is in both, and the answer comes
6817        // out in the order those combined scores put it in.
6818        assert_eq!(
6819            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
6820            "*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"
6821        );
6822        assert_eq!(
6823            f.run(&[
6824                b"ZUNION",
6825                b"2",
6826                b"z",
6827                b"y",
6828                b"WEIGHTS",
6829                b"2",
6830                b"3",
6831                b"WITHSCORES"
6832            ]),
6833            "*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"
6834        );
6835        assert_eq!(
6836            f.run(&[
6837                b"ZUNION",
6838                b"2",
6839                b"z",
6840                b"y",
6841                b"AGGREGATE",
6842                b"MIN",
6843                b"WITHSCORES"
6844            ]),
6845            "*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"
6846        );
6847        assert_eq!(
6848            f.run(&[
6849                b"ZUNION",
6850                b"2",
6851                b"z",
6852                b"y",
6853                b"AGGREGATE",
6854                b"MAX",
6855                b"WITHSCORES"
6856            ]),
6857            "*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"
6858        );
6859        assert_eq!(
6860            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
6861            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
6862        );
6863        assert_eq!(
6864            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
6865            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
6866        );
6867        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
6868        // A plain set is an input, and it behaves as a sorted set in which
6869        // every member scores one.
6870        f.run(&[b"SADD", b"p", b"a", b"d"]);
6871        assert_eq!(
6872            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
6873            "*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"
6874        );
6875        // A difference never combines two scores, so it has nothing for either
6876        // of the two options to do and refuses both.
6877        for cmd in [
6878            &[
6879                b"ZDIFF".as_slice(),
6880                b"2",
6881                b"z",
6882                b"y",
6883                b"WEIGHTS",
6884                b"1",
6885                b"1",
6886            ][..],
6887            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
6888        ] {
6889            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
6890        }
6891    }
6892
6893    /// The count of keys, which is what lets a key be named `WEIGHTS`.
6894    #[test]
6895    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
6896        let mut f = Fixture::new();
6897        f.run(&[b"ZADD", b"z", b"1", b"a"]);
6898        f.run(&[b"ZADD", b"y", b"2", b"b"]);
6899        // Redis names the command in this one, so each spelling says its own.
6900        assert_eq!(
6901            f.run(&[b"ZUNION", b"0", b"z"]),
6902            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
6903        );
6904        assert_eq!(
6905            f.run(&[b"ZUNION", b"-1", b"z"]),
6906            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
6907        );
6908        assert_eq!(
6909            f.run(&[b"ZINTERCARD", b"0", b"z"]),
6910            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
6911        );
6912        // A count bigger than the line is a plain syntax error, which reads
6913        // oddly and is what Redis says.
6914        assert_eq!(
6915            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
6916            "-ERR syntax error\r\n"
6917        );
6918        assert_eq!(
6919            f.run(&[b"ZUNION", b"x", b"z"]),
6920            "-ERR value is not an integer or out of range\r\n"
6921        );
6922        // A WEIGHTS list that is not one per key is a syntax error, and a
6923        // weight that is not a number gets a sentence of its own.
6924        assert_eq!(
6925            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
6926            "-ERR syntax error\r\n"
6927        );
6928        assert_eq!(
6929            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
6930            "-ERR weight value is not a float\r\n"
6931        );
6932        assert_eq!(
6933            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
6934            "-ERR syntax error\r\n"
6935        );
6936    }
6937
6938    /// The three store forms, which answer a count and take no WITHSCORES.
6939    #[test]
6940    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
6941        let mut f = Fixture::new();
6942        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6943        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
6944        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
6945        assert_eq!(
6946            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
6947            "*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"
6948        );
6949        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
6950        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
6951        // An empty result deletes the destination rather than leaving an empty
6952        // sorted set, because an empty one does not exist.
6953        assert_eq!(
6954            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
6955            ":0\r\n"
6956        );
6957        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
6958        // The destination is allowed to name its own source.
6959        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
6960        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
6961        for cmd in [
6962            &[
6963                b"ZUNIONSTORE".as_slice(),
6964                b"d",
6965                b"2",
6966                b"z",
6967                b"y",
6968                b"WITHSCORES",
6969            ][..],
6970            &[
6971                b"ZDIFFSTORE",
6972                b"d",
6973                b"2",
6974                b"z",
6975                b"y",
6976                b"WEIGHTS",
6977                b"1",
6978                b"1",
6979            ],
6980        ] {
6981            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
6982        }
6983    }
6984
6985    /// `ZINTERCARD`, which counts without building anything.
6986    #[test]
6987    fn intercard_counts_and_stops_at_its_limit() {
6988        let mut f = Fixture::new();
6989        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6990        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
6991        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
6992        // A limit of zero is no limit, which is Redis's reading of it.
6993        assert_eq!(
6994            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
6995            ":2\r\n"
6996        );
6997        assert_eq!(
6998            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
6999            ":1\r\n"
7000        );
7001        // A negative limit and a limit that is not a number at all get the same
7002        // sentence, which looks like a mistake in Redis and is copied as one.
7003        let bad = "-ERR LIMIT can't be negative\r\n";
7004        assert_eq!(
7005            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
7006            bad
7007        );
7008        assert_eq!(
7009            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
7010            bad
7011        );
7012        for cmd in [
7013            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
7014            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
7015            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
7016        ] {
7017            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
7018        }
7019    }
7020
7021    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
7022    #[test]
7023    fn a_draw_answers_one_member_or_an_array_of_them() {
7024        let mut f = Fixture::new();
7025        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7026        // No count is one member or a nil, a count is an array that may be
7027        // empty, and those are two reply types the client has to tell apart.
7028        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
7029        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
7030        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
7031        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
7032        // A positive count draws without replacement, so a count over the size
7033        // answers the whole set and never a member twice.
7034        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
7035        assert!(all.starts_with("*3\r\n"), "{all}");
7036        for m in ["a", "b", "c"] {
7037            assert!(all.contains(m), "{all}");
7038        }
7039        // A negative one draws with replacement and answers exactly as many as
7040        // it was asked for, whatever the size of the set.
7041        assert!(
7042            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
7043            "five draws with replacement"
7044        );
7045        assert!(
7046            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
7047                .starts_with("*4\r\n"),
7048            "two pairs, flat on RESP2"
7049        );
7050        f.out = Out::new(Proto::Resp3);
7051        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
7052        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
7053        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
7054        f.out = Out::new(Proto::Resp2);
7055        assert_eq!(
7056            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
7057            "-ERR syntax error\r\n"
7058        );
7059        assert_eq!(
7060            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
7061            "-ERR value is not an integer or out of range\r\n"
7062        );
7063    }
7064
7065    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
7066    #[test]
7067    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
7068        let mut f = Fixture::new();
7069        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7070        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";
7071        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
7072        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
7073        assert_eq!(
7074            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
7075            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
7076        );
7077        assert_eq!(
7078            f.run(&[b"ZSCAN", b"nokey", b"0"]),
7079            "*2\r\n$1\r\n0\r\n*0\r\n"
7080        );
7081        // A score stays a bulk string on RESP3, which is the one place the two
7082        // protocols agree about a score and everywhere else they do not.
7083        f.out = Out::new(Proto::Resp3);
7084        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
7085        f.out = Out::new(Proto::Resp2);
7086        assert_eq!(
7087            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
7088            "-ERR NOVALUES option can only be used in HSCAN\r\n"
7089        );
7090        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
7091        assert_eq!(
7092            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
7093            "-ERR syntax error\r\n"
7094        );
7095    }
7096
7097    /// The count is what decides the shape, and its value is not.
7098    #[test]
7099    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
7100        let mut f = Fixture::new();
7101        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7102        // No count, so one flat pair, and the score is a bulk string on RESP2.
7103        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
7104        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
7105        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
7106        // A count, so pairs, and on RESP2 they are flattened into one run.
7107        assert_eq!(
7108            f.run(&[b"ZPOPMIN", b"z", b"2"]),
7109            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
7110        );
7111        // An empty array rather than a null, which is where a sorted set pop and
7112        // a list pop part company, and the same answer a count of zero gives.
7113        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
7114        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
7115        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
7116        // The last member takes the key with it.
7117        assert_eq!(
7118            f.run(&[b"ZPOPMIN", b"z", b"9"]),
7119            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
7120        );
7121        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7122
7123        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
7124        f.out = Out::new(Proto::Resp3);
7125        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
7126        assert_eq!(
7127            f.run(&[b"ZPOPMIN", b"z", b"1"]),
7128            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
7129        );
7130        f.out = Out::new(Proto::Resp2);
7131        // Both of these are the range error rather than the usual sentence about
7132        // integers, which is the odd answer and so the one worth copying.
7133        let bad = "-ERR value is out of range, must be positive\r\n";
7134        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
7135        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
7136        assert_eq!(
7137            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
7138            "-ERR syntax error\r\n"
7139        );
7140    }
7141
7142    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
7143    #[test]
7144    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
7145        let mut f = Fixture::new();
7146        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7147        assert_eq!(
7148            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
7149            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
7150        );
7151        // Nested on RESP2 as well, because the key name is already in front of
7152        // the pairs and there is nothing left to flatten into.
7153        assert_eq!(
7154            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
7155            "*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"
7156        );
7157        // A null array and not a null, the same as LMPOP.
7158        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
7159        f.out = Out::new(Proto::Resp3);
7160        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
7161        f.out = Out::new(Proto::Resp2);
7162        let numkeys = "-ERR numkeys should be greater than 0\r\n";
7163        for bad in [
7164            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
7165            &[b"ZMPOP", b"-1", b"z", b"MIN"],
7166            &[b"ZMPOP", b"x", b"z", b"MIN"],
7167        ] {
7168            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
7169        }
7170        let count = "-ERR count should be greater than 0\r\n";
7171        for bad in [
7172            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
7173            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
7174            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
7175        ] {
7176            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
7177        }
7178        let syntax = "-ERR syntax error\r\n";
7179        for bad in [
7180            // Two keys named and one given, so the word that should have been
7181            // the direction is a key and there is no direction left.
7182            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
7183            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
7184            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
7185            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
7186        ] {
7187            assert_eq!(f.run(bad), syntax, "{bad:?}");
7188        }
7189    }
7190
7191    /// The three that wait, when there is something there and they do not have
7192    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
7193    #[test]
7194    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
7195        let mut f = Fixture::new();
7196        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
7197        assert_eq!(
7198            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
7199            (
7200                Flow::Continue,
7201                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
7202            )
7203        );
7204        assert_eq!(
7205            f.run(&[b"BZPOPMAX", b"z", b"0"]),
7206            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
7207        );
7208        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
7209        assert_eq!(
7210            f.run(&[
7211                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
7212            ]),
7213            "*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"
7214        );
7215        f.out = Out::new(Proto::Resp3);
7216        assert_eq!(
7217            f.run(&[b"BZPOPMIN", b"z", b"0"]),
7218            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
7219        );
7220        f.out = Out::new(Proto::Resp2);
7221        // Nothing to take, so the client is parked and nothing was written.
7222        assert_eq!(
7223            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
7224            (Flow::Block, String::new())
7225        );
7226        assert_eq!(
7227            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
7228            (Flow::Block, String::new())
7229        );
7230        // The timeout is read before the key count, so this complains about the
7231        // timeout and not about the count.
7232        assert_eq!(
7233            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
7234            "-ERR timeout is not a float or out of range\r\n"
7235        );
7236        assert_eq!(
7237            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
7238            "-ERR numkeys should be greater than 0\r\n"
7239        );
7240        assert_eq!(
7241            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
7242            "-ERR timeout is negative\r\n"
7243        );
7244    }
7245
7246    /// A parked sorted set client is served by whatever puts a member under one
7247    /// of its keys, and is not served by something of another type landing
7248    /// there.
7249    #[test]
7250    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
7251        let mut f = Fixture::new();
7252        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
7253        assert_eq!(f.server.waiters().len(), 1);
7254        // A string under the key is not what it asked for, so it stays parked
7255        // rather than being handed a WRONGTYPE on a command that was accepted.
7256        f.run(&[b"SET", b"z", b"v"]);
7257        let mut out = Out::new(Proto::Resp2);
7258        assert!(!f.server.serve_waiter(0, 0, &mut out));
7259        assert!(out.as_slice().is_empty());
7260        f.run(&[b"DEL", b"z"]);
7261        f.run(&[b"ZADD", b"z", b"5", b"m"]);
7262        assert!(f.server.serve_waiter(0, 0, &mut out));
7263        assert_eq!(
7264            core::str::from_utf8(out.as_slice()).expect("ascii"),
7265            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
7266        );
7267        // And the member is gone, which is what makes a queue of workers on a
7268        // sorted set work at all.
7269        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
7270    }
7271
7272    #[test]
7273    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
7274        let mut f = Fixture::new();
7275        f.run(&[b"SET", b"s", b"v"]);
7276        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
7277        for cmd in [
7278            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
7279            &[b"ZINCRBY", b"s", b"1", b"a"],
7280            &[b"ZCARD", b"s"],
7281            &[b"ZSCORE", b"s", b"a"],
7282            &[b"ZMSCORE", b"s", b"a"],
7283            &[b"ZREM", b"s", b"a"],
7284            &[b"ZRANK", b"s", b"a"],
7285            &[b"ZREVRANK", b"s", b"a"],
7286            &[b"ZCOUNT", b"s", b"1", b"2"],
7287            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
7288            &[b"ZRANGE", b"s", b"0", b"-1"],
7289            &[b"ZREVRANGE", b"s", b"0", b"-1"],
7290            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
7291            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
7292            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
7293            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
7294            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
7295            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
7296            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
7297            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
7298            &[b"ZUNION", b"1", b"s"],
7299            &[b"ZINTER", b"1", b"s"],
7300            &[b"ZDIFF", b"1", b"s"],
7301            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
7302            &[b"ZINTERSTORE", b"d", b"1", b"s"],
7303            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
7304            &[b"ZINTERCARD", b"1", b"s"],
7305            &[b"ZRANDMEMBER", b"s"],
7306            &[b"ZSCAN", b"s", b"0"],
7307            &[b"ZPOPMIN", b"s"],
7308            &[b"ZPOPMAX", b"s", b"2"],
7309            &[b"ZMPOP", b"1", b"s", b"MIN"],
7310            &[b"BZPOPMIN", b"s", b"0"],
7311            &[b"BZPOPMAX", b"s", b"0"],
7312            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
7313        ] {
7314            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
7315        }
7316        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
7317    }
7318
7319    /// The same churn the set, the string and the list get, because a sorted
7320    /// set that leaks a tree node per add looks exactly like one that does not
7321    /// until it has run for an afternoon.
7322    #[test]
7323    fn churning_sorted_sets_does_not_grow_the_server() {
7324        let mut f = Fixture::new();
7325        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
7326        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
7327        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
7328        for i in 0..200 {
7329            args.push(&scores[i]);
7330            args.push(&members[i]);
7331        }
7332
7333        f.run(&args);
7334        f.run(&[b"DEL", b"z"]);
7335        f.server.compact_step();
7336        let after_first = f.server.memory_bytes();
7337
7338        for _ in 0..200 {
7339            f.run(&args);
7340            f.run(&[b"DEL", b"z"]);
7341            f.server.compact_step();
7342        }
7343        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
7344        assert!(
7345            f.server.memory_bytes() <= after_first * 2,
7346            "held {} after two hundred passes against {after_first} after one",
7347            f.server.memory_bytes()
7348        );
7349    }
7350
7351    // ------------------------------------------------------------------- geo
7352
7353    /// The three places every Redis geo example uses, and one more.
7354    ///
7355    /// Every reply this section asserts on came off a running 8.10.1 with these
7356    /// three loaded, byte for byte, including the number of digits in a
7357    /// coordinate and the four places on a distance.
7358    fn sicily(f: &mut Fixture) {
7359        f.run(&[
7360            b"GEOADD",
7361            b"Sicily",
7362            b"13.361389",
7363            b"38.115556",
7364            b"Palermo",
7365            b"15.087269",
7366            b"37.502669",
7367            b"Catania",
7368        ]);
7369        f.run(&[
7370            b"GEOADD",
7371            b"Sicily",
7372            b"13.583333",
7373            b"37.316667",
7374            b"Agrigento",
7375        ]);
7376    }
7377
7378    #[test]
7379    fn places_go_in_as_scores_and_come_back_as_positions() {
7380        let mut f = Fixture::new();
7381        assert_eq!(
7382            f.run(&[
7383                b"GEOADD",
7384                b"Sicily",
7385                b"13.361389",
7386                b"38.115556",
7387                b"Palermo",
7388                b"15.087269",
7389                b"37.502669",
7390                b"Catania"
7391            ]),
7392            ":2\r\n"
7393        );
7394        // A geo key is a sorted set and says so, which is not an implementation
7395        // detail either: a client removes a place with ZREM and counts them
7396        // with ZCARD, and the score is the number a real server stores.
7397        assert_eq!(f.run(&[b"TYPE", b"Sicily"]), "+zset\r\n");
7398        assert_eq!(
7399            f.run(&[b"ZSCORE", b"Sicily", b"Palermo"]),
7400            "$16\r\n3479099956230698\r\n"
7401        );
7402        assert_eq!(
7403            f.run(&[b"GEOPOS", b"Sicily", b"Palermo", b"NonExisting"]),
7404            "*2\r\n*2\r\n$18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n*-1\r\n"
7405        );
7406        assert_eq!(
7407            f.run(&[
7408                b"GEOHASH",
7409                b"Sicily",
7410                b"Palermo",
7411                b"Catania",
7412                b"NonExisting"
7413            ]),
7414            "*3\r\n$11\r\nsqc8b49rny0\r\n$11\r\nsqdtr74hyu0\r\n$-1\r\n"
7415        );
7416        // A key that is not there is an empty one, and the two nulls are not
7417        // the same null: GEOPOS answers the array one and GEOHASH the string
7418        // one, which a RESP2 client can tell apart.
7419        assert_eq!(f.run(&[b"GEOPOS", b"nokey", b"a"]), "*1\r\n*-1\r\n");
7420        assert_eq!(f.run(&[b"GEOHASH", b"nokey", b"a"]), "*1\r\n$-1\r\n");
7421    }
7422
7423    #[test]
7424    fn a_distance_comes_back_with_four_places_in_whatever_unit_was_asked_for() {
7425        let mut f = Fixture::new();
7426        sicily(&mut f);
7427        assert_eq!(
7428            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania"]),
7429            "$11\r\n166274.1516\r\n"
7430        );
7431        assert_eq!(
7432            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"km"]),
7433            "$8\r\n166.2742\r\n"
7434        );
7435        assert_eq!(
7436            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Catania", b"mi"]),
7437            "$8\r\n103.3182\r\n"
7438        );
7439        // A member that is not there and a key that is not there are the same
7440        // nil, and the unit is read before the key is looked up, so a bad unit
7441        // on a missing key is still an error.
7442        assert_eq!(
7443            f.run(&[b"GEODIST", b"Sicily", b"Palermo", b"Foo"]),
7444            "$-1\r\n"
7445        );
7446        assert_eq!(f.run(&[b"GEODIST", b"nokey", b"a", b"b"]), "$-1\r\n");
7447        assert_eq!(
7448            f.run(&[b"GEODIST", b"nokey", b"a", b"b", b"parsecs"]),
7449            "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n"
7450        );
7451        assert_eq!(
7452            f.run(&[b"GEODIST", b"Sicily", b"a", b"b", b"km", b"extra"]),
7453            "-ERR syntax error\r\n"
7454        );
7455    }
7456
7457    #[test]
7458    fn a_search_finds_what_is_inside_it_nearest_first() {
7459        let mut f = Fixture::new();
7460        sicily(&mut f);
7461        let all = "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n";
7462        assert_eq!(
7463            f.run(&[
7464                b"GEOSEARCH",
7465                b"Sicily",
7466                b"FROMLONLAT",
7467                b"15",
7468                b"37",
7469                b"BYRADIUS",
7470                b"200",
7471                b"km",
7472                b"ASC"
7473            ]),
7474            all
7475        );
7476        // The older spelling of the same search, which is the same nine boxes
7477        // and the same order.
7478        assert_eq!(
7479            f.run(&[b"GEORADIUS", b"Sicily", b"15", b"37", b"200", b"km", b"ASC"]),
7480            all
7481        );
7482        assert_eq!(
7483            f.run(&[
7484                b"GEORADIUS_RO",
7485                b"Sicily",
7486                b"15",
7487                b"37",
7488                b"200",
7489                b"km",
7490                b"ASC"
7491            ]),
7492            all
7493        );
7494        // A count with no ordering means the nearest ones, so DESC has to be
7495        // asked for to get the far end.
7496        assert_eq!(
7497            f.run(&[
7498                b"GEORADIUS",
7499                b"Sicily",
7500                b"15",
7501                b"37",
7502                b"200",
7503                b"km",
7504                b"DESC",
7505                b"COUNT",
7506                b"1"
7507            ]),
7508            "*1\r\n$7\r\nPalermo\r\n"
7509        );
7510        assert_eq!(
7511            f.run(&[
7512                b"GEORADIUS",
7513                b"Sicily",
7514                b"15",
7515                b"37",
7516                b"200",
7517                b"km",
7518                b"COUNT",
7519                b"1"
7520            ]),
7521            "*1\r\n$7\r\nCatania\r\n"
7522        );
7523        // Nothing inside a kilometre of that point, and nothing in a key that
7524        // is not there, and both are the empty array rather than an error.
7525        let empty = "*0\r\n";
7526        assert_eq!(
7527            f.run(&[
7528                b"GEOSEARCH",
7529                b"Sicily",
7530                b"FROMLONLAT",
7531                b"15",
7532                b"37",
7533                b"BYRADIUS",
7534                b"1",
7535                b"km"
7536            ]),
7537            empty
7538        );
7539        assert_eq!(
7540            f.run(&[
7541                b"GEOSEARCH",
7542                b"nokey",
7543                b"FROMLONLAT",
7544                b"15",
7545                b"37",
7546                b"BYRADIUS",
7547                b"1",
7548                b"km"
7549            ]),
7550            empty
7551        );
7552        assert_eq!(
7553            f.run(&[b"GEORADIUSBYMEMBER", b"nokey", b"m", b"1", b"km"]),
7554            empty
7555        );
7556    }
7557
7558    #[test]
7559    fn a_search_centred_on_a_member_starts_from_where_that_member_is() {
7560        let mut f = Fixture::new();
7561        sicily(&mut f);
7562        assert_eq!(
7563            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Agrigento", b"100", b"km"]),
7564            "*2\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
7565        );
7566        // The member itself is nothing away from itself, which is where the
7567        // fixed point writer's zero shows up on the wire.
7568        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";
7569        assert_eq!(
7570            f.run(&[
7571                b"GEORADIUSBYMEMBER_RO",
7572                b"Sicily",
7573                b"Agrigento",
7574                b"100",
7575                b"km",
7576                b"WITHDIST"
7577            ]),
7578            with_dist
7579        );
7580        assert_eq!(
7581            f.run(&[
7582                b"GEOSEARCH",
7583                b"Sicily",
7584                b"FROMMEMBER",
7585                b"Agrigento",
7586                b"BYRADIUS",
7587                b"100",
7588                b"km",
7589                b"ASC",
7590                b"WITHDIST"
7591            ]),
7592            with_dist
7593        );
7594        assert_eq!(
7595            f.run(&[b"GEORADIUSBYMEMBER", b"Sicily", b"Nowhere", b"100", b"km"]),
7596            "-ERR could not decode requested zset member\r\n"
7597        );
7598    }
7599
7600    #[test]
7601    fn a_box_search_reports_the_distance_the_hash_and_the_coordinates() {
7602        let mut f = Fixture::new();
7603        sicily(&mut f);
7604        // Three options asked for, so each result is a four element array of
7605        // the member, the distance, the hash and a pair. The order of the three
7606        // is Redis's and not the order they were written in the command.
7607        assert_eq!(
7608            f.run(&[
7609                b"GEOSEARCH",
7610                b"Sicily",
7611                b"FROMLONLAT",
7612                b"15",
7613                b"37",
7614                b"BYBOX",
7615                b"400",
7616                b"400",
7617                b"km",
7618                b"ASC",
7619                b"WITHCOORD",
7620                b"WITHDIST",
7621                b"WITHHASH"
7622            ]),
7623            "*3\r\n*4\r\n$7\r\nCatania\r\n$7\r\n56.4413\r\n:3479447370796909\r\n*2\r\n\
7624             $18\r\n15.087267458438873\r\n$17\r\n37.50266842333162\r\n\
7625             *4\r\n$9\r\nAgrigento\r\n$8\r\n130.4235\r\n:3479030013248308\r\n*2\r\n\
7626             $18\r\n13.583331406116486\r\n$18\r\n37.316668049938166\r\n\
7627             *4\r\n$7\r\nPalermo\r\n$8\r\n190.4424\r\n:3479099956230698\r\n*2\r\n\
7628             $18\r\n13.361389338970184\r\n$16\r\n38.1155563954963\r\n"
7629        );
7630    }
7631
7632    #[test]
7633    fn a_store_writes_the_hashes_and_a_storedist_writes_the_distances() {
7634        let mut f = Fixture::new();
7635        sicily(&mut f);
7636        let hashes = "*6\r\n$9\r\nAgrigento\r\n$16\r\n3479030013248308\r\n\
7637                      $7\r\nPalermo\r\n$16\r\n3479099956230698\r\n\
7638                      $7\r\nCatania\r\n$16\r\n3479447370796909\r\n";
7639        assert_eq!(
7640            f.run(&[
7641                b"GEOSEARCHSTORE",
7642                b"dst",
7643                b"Sicily",
7644                b"FROMLONLAT",
7645                b"15",
7646                b"37",
7647                b"BYRADIUS",
7648                b"200",
7649                b"km",
7650                b"ASC"
7651            ]),
7652            ":3\r\n"
7653        );
7654        assert_eq!(
7655            f.run(&[b"ZRANGE", b"dst", b"0", b"-1", b"WITHSCORES"]),
7656            hashes
7657        );
7658        // The same again through the older spelling, which stores the same
7659        // scores, so a key written by either is a geo key.
7660        assert_eq!(
7661            f.run(&[
7662                b"GEORADIUS",
7663                b"Sicily",
7664                b"15",
7665                b"37",
7666                b"200",
7667                b"km",
7668                b"STORE",
7669                b"dst3"
7670            ]),
7671            ":3\r\n"
7672        );
7673        assert_eq!(
7674            f.run(&[b"ZRANGE", b"dst3", b"0", b"-1", b"WITHSCORES"]),
7675            hashes
7676        );
7677        // STOREDIST stores the distance in the search unit instead, and those
7678        // are full doubles rather than the four places WITHDIST writes. The
7679        // numbers on the right are what 8.10.1 stored for this search, and they
7680        // are compared with a tolerance rather than byte for byte because the
7681        // last bit of a haversine is the platform's sin, cos and asin: this
7682        // machine and that one disagree in the sixteenth digit, and so do two
7683        // Redis builds. Everything a client actually reads back is four places
7684        // and is asserted exactly above.
7685        assert_eq!(
7686            f.run(&[
7687                b"GEOSEARCHSTORE",
7688                b"dst2",
7689                b"Sicily",
7690                b"FROMLONLAT",
7691                b"15",
7692                b"37",
7693                b"BYRADIUS",
7694                b"200",
7695                b"km",
7696                b"ASC",
7697                b"STOREDIST"
7698            ]),
7699            ":3\r\n"
7700        );
7701        for (member, want) in [
7702            ("Catania", 56.441_257_870_158_19),
7703            ("Agrigento", 130.423_487_067_147_14),
7704            ("Palermo", 190.442_429_847_757_92),
7705        ] {
7706            let reply = f.run(&[b"ZSCORE", b"dst2", member.as_bytes()]);
7707            let got: f64 = reply
7708                .trim_start_matches(|c: char| c != '\n')
7709                .trim()
7710                .parse()
7711                .unwrap_or_else(|_| panic!("{member} scored {reply:?}"));
7712            assert!(
7713                (got - want).abs() < 1e-9,
7714                "{member} scored {got} not {want}"
7715            );
7716        }
7717        // The order they went in is the order the scores put them in, which is
7718        // the point of storing the distance rather than the hash.
7719        assert_eq!(
7720            f.run(&[b"ZRANGE", b"dst2", b"0", b"-1"]),
7721            "*3\r\n$7\r\nCatania\r\n$9\r\nAgrigento\r\n$7\r\nPalermo\r\n"
7722        );
7723        // A search that finds nothing takes the destination with it rather than
7724        // leaving what was there, and a source key that is not there is a
7725        // search that finds nothing.
7726        assert_eq!(
7727            f.run(&[
7728                b"GEOSEARCHSTORE",
7729                b"dst",
7730                b"nokey",
7731                b"FROMLONLAT",
7732                b"15",
7733                b"37",
7734                b"BYRADIUS",
7735                b"200",
7736                b"km"
7737            ]),
7738            ":0\r\n"
7739        );
7740        assert_eq!(f.run(&[b"EXISTS", b"dst"]), ":0\r\n");
7741    }
7742
7743    #[test]
7744    fn the_gates_on_geoadd_are_the_ones_zadd_has() {
7745        let mut f = Fixture::new();
7746        sicily(&mut f);
7747        // XX on a member that is already where it is changes nothing, and NX on
7748        // one that is there refuses to move it.
7749        assert_eq!(
7750            f.run(&[
7751                b"GEOADD",
7752                b"Sicily",
7753                b"XX",
7754                b"CH",
7755                b"13.361389",
7756                b"38.115556",
7757                b"Palermo"
7758            ]),
7759            ":0\r\n"
7760        );
7761        assert_eq!(
7762            f.run(&[
7763                b"GEOADD",
7764                b"Sicily",
7765                b"NX",
7766                b"13.361389",
7767                b"38.9",
7768                b"Palermo"
7769            ]),
7770            ":0\r\n"
7771        );
7772        assert_eq!(
7773            f.run(&[
7774                b"GEOADD",
7775                b"Sicily",
7776                b"CH",
7777                b"13.361389",
7778                b"38.9",
7779                b"Palermo"
7780            ]),
7781            ":1\r\n"
7782        );
7783        // Out of range, and nothing is stored: the whole call is refused rather
7784        // than the good pairs going in and the bad one stopping it.
7785        assert_eq!(
7786            f.run(&[
7787                b"GEOADD",
7788                b"new",
7789                b"13.361389",
7790                b"38.115556",
7791                b"here",
7792                b"181",
7793                b"38",
7794                b"there"
7795            ]),
7796            "-ERR invalid longitude,latitude pair 181.000000,38.000000\r\n"
7797        );
7798        assert_eq!(f.run(&[b"EXISTS", b"new"]), ":0\r\n");
7799        assert_eq!(
7800            f.run(&[b"GEOADD", b"new", b"x", b"38", b"here"]),
7801            "-ERR value is not a valid float\r\n"
7802        );
7803        // The count of triples is checked before the two gates are, and a call
7804        // with no triples at all reaches the same sentence.
7805        assert_eq!(
7806            f.run(&[b"GEOADD", b"new", b"13", b"38", b"here", b"and"]),
7807            "-ERR syntax error\r\n"
7808        );
7809        assert_eq!(
7810            f.run(&[b"GEOADD", b"new", b"NX", b"XX", b"CH"]),
7811            "-ERR syntax error\r\n"
7812        );
7813        assert_eq!(
7814            f.run(&[b"GEOADD", b"new", b"CH", b"CH", b"CH", b"CH"]),
7815            "-ERR syntax error\r\n"
7816        );
7817        assert_eq!(
7818            f.run(&[b"GEOADD", b"new", b"NX", b"CH"]),
7819            "-ERR wrong number of arguments for 'geoadd' command\r\n"
7820        );
7821    }
7822
7823    /// The sentences a search answers, which are its contract as much as the
7824    /// results are.
7825    #[test]
7826    fn every_way_a_search_can_be_written_wrong_has_its_own_sentence() {
7827        let mut f = Fixture::new();
7828        sicily(&mut f);
7829        let cases: &[(&[&[u8]], &str)] = &[
7830            (
7831                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"x", b"km"],
7832                "-ERR need numeric radius\r\n",
7833            ),
7834            (
7835                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"-1", b"km"],
7836                "-ERR radius cannot be negative\r\n",
7837            ),
7838            (
7839                &[b"GEORADIUS", b"Sicily", b"15", b"37", b"1", b"parsecs"],
7840                "-ERR unsupported unit provided. please use M, KM, FT, MI\r\n",
7841            ),
7842            (
7843                &[b"GEORADIUS", b"Sicily", b"181", b"37", b"1", b"km"],
7844                "-ERR invalid longitude,latitude pair 181.000000,37.000000\r\n",
7845            ),
7846            (
7847                &[
7848                    b"GEOSEARCH",
7849                    b"Sicily",
7850                    b"FROMLONLAT",
7851                    b"15",
7852                    b"37",
7853                    b"BYBOX",
7854                    b"x",
7855                    b"1",
7856                    b"km",
7857                ],
7858                "-ERR need numeric width\r\n",
7859            ),
7860            (
7861                &[
7862                    b"GEOSEARCH",
7863                    b"Sicily",
7864                    b"FROMLONLAT",
7865                    b"15",
7866                    b"37",
7867                    b"BYBOX",
7868                    b"1",
7869                    b"y",
7870                    b"km",
7871                ],
7872                "-ERR need numeric height\r\n",
7873            ),
7874            (
7875                &[
7876                    b"GEOSEARCH",
7877                    b"Sicily",
7878                    b"FROMLONLAT",
7879                    b"15",
7880                    b"37",
7881                    b"BYBOX",
7882                    b"-1",
7883                    b"1",
7884                    b"km",
7885                ],
7886                "-ERR height or width cannot be negative\r\n",
7887            ),
7888            (
7889                &[
7890                    b"GEOSEARCH",
7891                    b"Sicily",
7892                    b"FROMLONLAT",
7893                    b"15",
7894                    b"37",
7895                    b"BYRADIUS",
7896                    b"1",
7897                    b"km",
7898                    b"ANY",
7899                ],
7900                "-ERR the ANY argument requires COUNT argument\r\n",
7901            ),
7902            (
7903                &[
7904                    b"GEOSEARCH",
7905                    b"Sicily",
7906                    b"FROMLONLAT",
7907                    b"15",
7908                    b"37",
7909                    b"BYRADIUS",
7910                    b"1",
7911                    b"km",
7912                    b"COUNT",
7913                    b"0",
7914                ],
7915                "-ERR COUNT must be > 0\r\n",
7916            ),
7917            (
7918                &[
7919                    b"GEOSEARCH",
7920                    b"Sicily",
7921                    b"BYRADIUS",
7922                    b"1",
7923                    b"km",
7924                    b"BYBOX",
7925                    b"1",
7926                    b"1",
7927                    b"km",
7928                ],
7929                "-ERR syntax error\r\n",
7930            ),
7931            (
7932                &[
7933                    b"GEOSEARCH",
7934                    b"Sicily",
7935                    b"FROMMEMBER",
7936                    b"Palermo",
7937                    b"FROMLONLAT",
7938                    b"1",
7939                    b"2",
7940                    b"BYRADIUS",
7941                    b"1",
7942                    b"km",
7943                ],
7944                "-ERR syntax error\r\n",
7945            ),
7946            // The two options a GEOSEARCH cannot leave out, each with its own
7947            // sentence, and the command quoted the way the client spelled it.
7948            (
7949                &[
7950                    b"geosearch",
7951                    b"Sicily",
7952                    b"BYRADIUS",
7953                    b"1",
7954                    b"km",
7955                    b"ASC",
7956                    b"WITHDIST",
7957                ],
7958                "-ERR exactly one of FROMMEMBER or FROMLONLAT can be specified for geosearch\r\n",
7959            ),
7960            (
7961                &[
7962                    b"GEOSEARCH",
7963                    b"Sicily",
7964                    b"FROMLONLAT",
7965                    b"15",
7966                    b"37",
7967                    b"ASC",
7968                    b"WITHDIST",
7969                ],
7970                "-ERR exactly one of BYRADIUS and BYBOX can be specified for GEOSEARCH\r\n",
7971            ),
7972            // A store cannot also be asked for the distance, and the two
7973            // families name themselves differently in the same sentence.
7974            (
7975                &[
7976                    b"GEOSEARCHSTORE",
7977                    b"d",
7978                    b"Sicily",
7979                    b"FROMLONLAT",
7980                    b"15",
7981                    b"37",
7982                    b"BYRADIUS",
7983                    b"1",
7984                    b"km",
7985                    b"WITHCOORD",
7986                ],
7987                "-ERR GEOSEARCHSTORE is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
7988            ),
7989            (
7990                &[
7991                    b"GEORADIUS",
7992                    b"Sicily",
7993                    b"15",
7994                    b"37",
7995                    b"1",
7996                    b"km",
7997                    b"WITHDIST",
7998                    b"STORE",
7999                    b"d",
8000                ],
8001                "-ERR STORE option in GEORADIUS is not compatible with WITHDIST, WITHHASH and WITHCOORD options\r\n",
8002            ),
8003            // The read only forms have no store at all, so the word is a stray
8004            // one, and GEOSEARCH's STOREDIST is only a GEOSEARCHSTORE option.
8005            (
8006                &[
8007                    b"GEORADIUS_RO",
8008                    b"Sicily",
8009                    b"15",
8010                    b"37",
8011                    b"1",
8012                    b"km",
8013                    b"STORE",
8014                    b"d",
8015                ],
8016                "-ERR syntax error\r\n",
8017            ),
8018            (
8019                &[
8020                    b"GEOSEARCH",
8021                    b"Sicily",
8022                    b"FROMLONLAT",
8023                    b"15",
8024                    b"37",
8025                    b"BYRADIUS",
8026                    b"1",
8027                    b"km",
8028                    b"STOREDIST",
8029                ],
8030                "-ERR syntax error\r\n",
8031            ),
8032        ];
8033        for (parts, want) in cases {
8034            assert_eq!(&f.run(parts), want, "{:?}", parts[0]);
8035        }
8036    }
8037
8038    /// A wrong type wins over a bad argument, because the key is looked up
8039    /// first, and every one of the ten says the same thing about it.
8040    #[test]
8041    fn every_geo_command_says_wrongtype() {
8042        let mut f = Fixture::new();
8043        f.run(&[b"SET", b"s", b"v"]);
8044        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8045        let cases: &[&[&[u8]]] = &[
8046            &[b"GEOADD", b"s", b"13", b"38", b"m"],
8047            &[b"GEOPOS", b"s", b"m"],
8048            &[b"GEOHASH", b"s", b"m"],
8049            &[b"GEODIST", b"s", b"a", b"b"],
8050            &[
8051                b"GEOSEARCH",
8052                b"s",
8053                b"FROMLONLAT",
8054                b"15",
8055                b"37",
8056                b"BYRADIUS",
8057                b"1",
8058                b"km",
8059            ],
8060            &[
8061                b"GEOSEARCHSTORE",
8062                b"d",
8063                b"s",
8064                b"FROMLONLAT",
8065                b"15",
8066                b"37",
8067                b"BYRADIUS",
8068                b"1",
8069                b"km",
8070            ],
8071            &[b"GEORADIUS", b"s", b"15", b"37", b"1", b"km"],
8072            &[b"GEORADIUS_RO", b"s", b"15", b"37", b"1", b"km"],
8073            &[b"GEORADIUSBYMEMBER", b"s", b"m", b"1", b"km"],
8074            &[b"GEORADIUSBYMEMBER_RO", b"s", b"m", b"1", b"km"],
8075        ];
8076        for case in cases {
8077            assert_eq!(f.run(case), wrong, "{:?}", case[0]);
8078        }
8079        // And it wins over an argument that will not parse, which is the whole
8080        // reason the lookup comes first.
8081        assert_eq!(
8082            f.run(&[b"GEORADIUS", b"s", b"15", b"37", b"x", b"km"]),
8083            wrong
8084        );
8085    }
8086
8087    // ----------------------------------------------------------------- array
8088
8089    #[test]
8090    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
8091        let mut f = Fixture::new();
8092        // Three consecutive positions from a high index, and the reply is how
8093        // many of them were empty before rather than how many were written.
8094        assert_eq!(
8095            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
8096            ":3\r\n"
8097        );
8098        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
8099        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
8100        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
8101        // A hole and a key that is not there are the same answer.
8102        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
8103        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
8104        assert_eq!(
8105            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
8106            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
8107        );
8108        // Scattered pairs in one command, last write wins within it.
8109        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
8110        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
8111    }
8112
8113    /// The two numbers an array reports are not the same number, and one of
8114    /// them does not fit a signed integer.
8115    #[test]
8116    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
8117        let mut f = Fixture::new();
8118        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
8119        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
8120        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
8121        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
8122        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
8123        // Deleting in the middle leaves the high water mark where it was.
8124        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
8125        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
8126        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
8127
8128        // The top of the space is addressable, and its length is a number with
8129        // bit sixty three set, so the reply has to be unsigned or it comes back
8130        // negative.
8131        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
8132        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
8133        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
8134        // And one past it does not exist, so a write that would reach it fails
8135        // before any of it lands.
8136        assert_eq!(
8137            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
8138            "-ERR array index overflow\r\n"
8139        );
8140        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
8141    }
8142
8143    /// One reply per position and not one per element, which is the whole
8144    /// reason the range is capped.
8145    #[test]
8146    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
8147        let mut f = Fixture::new();
8148        f.run(&[b"ARSET", b"a", b"1", b"x"]);
8149        assert_eq!(
8150            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
8151            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
8152        );
8153        // The two ends may come in either order, and the answer is reversed
8154        // rather than empty.
8155        assert_eq!(
8156            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
8157            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
8158        );
8159        // A key that is not there reads like an array of nothing but holes.
8160        assert_eq!(
8161            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
8162            "*2\r\n$-1\r\n$-1\r\n"
8163        );
8164        // A range wider than a million positions is refused and not trimmed,
8165        // because against a missing key it is a request for as many nulls as
8166        // the range is wide.
8167        assert_eq!(
8168            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
8169            "-ERR range exceeds maximum of 1000000 items\r\n"
8170        );
8171    }
8172
8173    /// Every index in the argument list is read before the key is touched, so
8174    /// a bad one at the end leaves nothing half written.
8175    #[test]
8176    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
8177        let mut f = Fixture::new();
8178        assert_eq!(
8179            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
8180            "-ERR invalid array index\r\n"
8181        );
8182        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
8183        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
8184        assert_eq!(
8185            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
8186            "-ERR invalid array index\r\n"
8187        );
8188        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
8189        // An index is unsigned here, so the numbers a list would take are not
8190        // the last element, they are errors.
8191        assert_eq!(
8192            f.run(&[b"ARGET", b"a", b"-1"]),
8193            "-ERR invalid array index\r\n"
8194        );
8195        // And a pair list with an odd tail is an arity error rather than a
8196        // syntax one.
8197        assert_eq!(
8198            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
8199            "-ERR wrong number of arguments for 'armset' command\r\n"
8200        );
8201        assert_eq!(
8202            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
8203            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
8204        );
8205    }
8206
8207    #[test]
8208    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
8209        let mut f = Fixture::new();
8210        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
8211        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
8212        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
8213        // Two ranges in one command, and the second one covers the whole space
8214        // without walking it.
8215        assert_eq!(
8216            f.run(&[
8217                b"ARDELRANGE",
8218                b"a",
8219                b"100",
8220                b"200",
8221                b"0",
8222                b"18446744073709551614"
8223            ]),
8224            ":2\r\n"
8225        );
8226        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
8227        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
8228        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
8229    }
8230
8231    /// A value goes out as the bytes it came in as, whichever of the three ways
8232    /// the array found to store it.
8233    #[test]
8234    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
8235        let mut f = Fixture::new();
8236        let long = vec![b'v'; 200];
8237        f.run(&[
8238            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
8239            b"short", b"5", &long, b"6", b"-0",
8240        ]);
8241        // 42 is an integer, 007 is not one because it does not print back the
8242        // same, 3.5 survives a double and 3.14 does not, and the last two are a
8243        // word packed string and a blob.
8244        assert_eq!(
8245            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
8246            format!(
8247                "*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",
8248                String::from_utf8_lossy(&long)
8249            )
8250        );
8251    }
8252
8253    #[test]
8254    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
8255        let mut f = Fixture::new();
8256        f.run(&[b"ARSET", b"a", b"0", b"x"]);
8257        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
8258        assert_eq!(
8259            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
8260            "$12\r\nsliced-array\r\n"
8261        );
8262        // And it is a body like any other, so the key commands work on it.
8263        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
8264        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
8265        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
8266        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
8267        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
8268        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
8269    }
8270
8271    #[test]
8272    fn every_array_command_refuses_a_key_holding_something_else() {
8273        let mut f = Fixture::new();
8274        f.run(&[b"SET", b"s", b"v"]);
8275        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8276        for cmd in [
8277            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
8278            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
8279            &[b"ARGET".as_ref(), b"s", b"0"][..],
8280            &[b"ARMGET".as_ref(), b"s", b"0"][..],
8281            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
8282            &[b"ARLEN".as_ref(), b"s"][..],
8283            &[b"ARCOUNT".as_ref(), b"s"][..],
8284            &[b"ARDEL".as_ref(), b"s", b"0"][..],
8285            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
8286            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
8287            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
8288            &[b"ARNEXT".as_ref(), b"s"][..],
8289            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
8290            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
8291            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
8292            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
8293            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
8294            &[b"ARINFO".as_ref(), b"s"][..],
8295        ] {
8296            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
8297        }
8298    }
8299
8300    /// Two of the array commands look the key up before they read the index and
8301    /// the rest read the index first, so the same broken argument gets two
8302    /// different errors depending on which command it went to.
8303    #[test]
8304    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
8305        let mut f = Fixture::new();
8306        f.run(&[b"SET", b"s", b"v"]);
8307        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
8308        let bad = "-ERR invalid array index\r\n";
8309        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
8310        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
8311        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
8312        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
8313        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
8314        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
8315        // And on a key that is an array the index is just an index.
8316        f.run(&[b"ARSET", b"a", b"0", b"x"]);
8317        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
8318        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
8319    }
8320
8321    #[test]
8322    fn an_append_follows_a_cursor_the_client_can_move() {
8323        let mut f = Fixture::new();
8324        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
8325        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
8326        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
8327        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
8328        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
8329
8330        // A seek says where the next one goes, and a missing key has no cursor
8331        // to move and is not created by the asking.
8332        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
8333        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
8334        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
8335        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
8336        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
8337        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
8338        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
8339
8340        // The top of the space is the one index only ARSEEK will take, and it
8341        // leaves the cursor with nowhere to go.
8342        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
8343        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
8344        assert_eq!(
8345            f.run(&[b"ARINSERT", b"a", b"x"]),
8346            "-ERR insert index overflow\r\n"
8347        );
8348        assert_eq!(
8349            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
8350            "-ERR invalid array index\r\n"
8351        );
8352    }
8353
8354    #[test]
8355    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
8356        let mut f = Fixture::new();
8357        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
8358        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
8359        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
8360        assert_eq!(
8361            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
8362            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
8363        );
8364        // Growing it after it has wrapped puts the survivors back in the order
8365        // they arrived, which is the whole point of paying for the rebuild.
8366        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
8367        assert_eq!(
8368            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
8369            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
8370        );
8371        // The size is read before the key, so a bad one is a bad size wherever
8372        // it is sent.
8373        assert_eq!(
8374            f.run(&[b"ARRING", b"r", b"0", b"x"]),
8375            "-ERR size must be positive\r\n"
8376        );
8377        assert_eq!(
8378            f.run(&[b"ARRING", b"r", b"big", b"x"]),
8379            "-ERR invalid size\r\n"
8380        );
8381    }
8382
8383    #[test]
8384    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
8385        let mut f = Fixture::new();
8386        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
8387        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
8388        assert_eq!(
8389            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
8390            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
8391        );
8392        assert_eq!(
8393            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
8394            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
8395        );
8396        assert_eq!(
8397            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
8398            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
8399            "more than there is gets what there is"
8400        );
8401        // Nothing asked for is an empty reply, and Redis answers that before it
8402        // has read the option or looked at the key.
8403        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
8404        assert_eq!(
8405            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
8406            "-ERR syntax error\r\n"
8407        );
8408        assert_eq!(
8409            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
8410            "-ERR invalid COUNT\r\n"
8411        );
8412
8413        // With no cursor the tail of the array is the anchor, and a hole inside
8414        // the window is reported as one.
8415        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
8416        assert_eq!(
8417            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
8418            "*2\r\n$-1\r\n$1\r\nz\r\n"
8419        );
8420    }
8421
8422    #[test]
8423    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
8424        let mut f = Fixture::new();
8425        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
8426        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
8427        // The whole index space, which ARGETRANGE refuses and this one answers
8428        // in three visits because holes cost nothing.
8429        assert_eq!(
8430            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
8431            "*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"
8432        );
8433        assert_eq!(
8434            f.run(&[
8435                b"ARSCAN",
8436                b"a",
8437                b"18446744073709551614",
8438                b"0",
8439                b"LIMIT",
8440                b"1"
8441            ]),
8442            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
8443        );
8444        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
8445        assert_eq!(
8446            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
8447            "-ERR LIMIT must be positive\r\n"
8448        );
8449        assert_eq!(
8450            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
8451            "-ERR syntax error\r\n"
8452        );
8453        assert_eq!(
8454            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
8455            "-ERR wrong number of arguments for 'arscan' command\r\n"
8456        );
8457    }
8458
8459    #[test]
8460    fn a_grep_answers_the_indexes_whose_elements_match() {
8461        let mut f = Fixture::new();
8462        assert_eq!(
8463            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
8464            "*0\r\n"
8465        );
8466        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
8467
8468        // The two bounds take the ends of the array as well as an index, and a
8469        // reversed range is walked backwards the way ARSCAN walks one.
8470        assert_eq!(
8471            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
8472            "*3\r\n:0\r\n:1\r\n:2\r\n"
8473        );
8474        assert_eq!(
8475            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
8476            "*3\r\n:2\r\n:1\r\n:0\r\n"
8477        );
8478        assert_eq!(
8479            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
8480            "*2\r\n:1\r\n:2\r\n"
8481        );
8482
8483        // One test each. NOCASE reaches all four of them and it may be written
8484        // after the pattern it applies to.
8485        assert_eq!(
8486            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
8487            "*1\r\n:0\r\n"
8488        );
8489        assert_eq!(
8490            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
8491            "*2\r\n:0\r\n:3\r\n"
8492        );
8493        assert_eq!(
8494            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
8495            "*1\r\n:2\r\n"
8496        );
8497        assert_eq!(
8498            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
8499            "*2\r\n:1\r\n:2\r\n"
8500        );
8501
8502        // OR is the default and AND has to be asked for, and either way the
8503        // last of a repeated option wins.
8504        let both: &[&[u8]] = &[
8505            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
8506        ];
8507        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
8508        assert_eq!(
8509            f.run(&[
8510                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
8511            ]),
8512            "*0\r\n"
8513        );
8514        assert_eq!(
8515            f.run(&[
8516                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
8517            ]),
8518            "*2\r\n:0\r\n:1\r\n"
8519        );
8520
8521        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
8522        // not the positions it had to look at.
8523        assert_eq!(
8524            f.run(&[
8525                b"ARGREP",
8526                b"a",
8527                b"-",
8528                b"+",
8529                b"MATCH",
8530                b"a",
8531                b"WITHVALUES",
8532                b"LIMIT",
8533                b"2"
8534            ]),
8535            "*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"
8536        );
8537        assert_eq!(
8538            f.run(&[
8539                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
8540            ]),
8541            "*1\r\n:3\r\n"
8542        );
8543    }
8544
8545    /// Everything ARGREP refuses, in the order it refuses it.
8546    #[test]
8547    fn a_grep_reports_a_broken_command_the_way_redis_does() {
8548        let mut f = Fixture::new();
8549        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
8550        let syntax = "-ERR syntax error\r\n";
8551
8552        // The bounds are read before the plan, so a bad index beats a bad
8553        // predicate whichever way round the two are written.
8554        assert_eq!(
8555            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
8556            "-ERR invalid array index\r\n"
8557        );
8558        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
8559        // A keyword with nothing after it, and a command that asks for nothing.
8560        assert_eq!(
8561            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
8562            syntax
8563        );
8564        assert_eq!(
8565            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
8566            syntax
8567        );
8568        assert_eq!(
8569            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
8570            syntax,
8571            "a command with no predicate in it at all"
8572        );
8573        assert_eq!(
8574            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
8575            "-ERR LIMIT must be positive\r\n"
8576        );
8577        assert_eq!(
8578            f.run(&[
8579                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
8580            ]),
8581            "-ERR value is not an integer or out of range\r\n"
8582        );
8583        assert_eq!(
8584            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
8585            "-ERR regular expression is empty\r\n"
8586        );
8587        assert_eq!(
8588            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
8589            "-ERR invalid regular expression: Missing ')'\r\n"
8590        );
8591        assert_eq!(
8592            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
8593            "-ERR regular expression backreferences are not supported\r\n"
8594        );
8595        // The arity is minus six, so a predicate keyword with no pattern after
8596        // it is short by one and never reaches the parser.
8597        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
8598        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
8599        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
8600    }
8601
8602    #[test]
8603    fn an_op_reduces_a_range_to_one_number() {
8604        let mut f = Fixture::new();
8605        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
8606        assert_eq!(
8607            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
8608            "$4\r\n-0.5\r\n"
8609        );
8610        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
8611        assert_eq!(
8612            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
8613            "$3\r\n2.5\r\n"
8614        );
8615        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
8616        assert_eq!(
8617            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
8618            ":1\r\n"
8619        );
8620        // An aggregate is written with seventeen significant digits, which is
8621        // Redis's own choice and not what a score comes back as.
8622        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
8623        assert_eq!(
8624            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
8625            "$19\r\n0.30000000000000004\r\n"
8626        );
8627        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
8628        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
8629
8630        // Nothing to work with is a null, and a missing key is a null for the
8631        // aggregates and a zero for the two that count.
8632        f.run(&[b"ARSET", b"w", b"0", b"word"]);
8633        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
8634        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
8635        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
8636
8637        assert_eq!(
8638            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
8639            "-ERR unknown operation\r\n"
8640        );
8641        assert_eq!(
8642            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
8643            "-ERR MATCH requires a value argument\r\n"
8644        );
8645        assert_eq!(
8646            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
8647            "-ERR wrong number of arguments for 'arop' command\r\n"
8648        );
8649    }
8650
8651    #[test]
8652    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
8653        let mut f = Fixture::new();
8654        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
8655        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
8656        let short = f.run(&[b"ARINFO", b"a"]);
8657        assert!(
8658            short.starts_with("*14\r\n"),
8659            "seven pairs on RESP2: {short}"
8660        );
8661        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
8662        assert!(
8663            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
8664            "{short}"
8665        );
8666        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
8667        let full = f.run(&[b"ARINFO", b"a", b"full"]);
8668        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
8669        // Two values one apart are held sparsely, so the dense count is zero and
8670        // the two dense averages have nothing to average.
8671        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
8672        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
8673        assert!(
8674            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
8675            "{full}"
8676        );
8677        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
8678
8679        // On RESP3 the same reply is a map and the averages are doubles.
8680        let mut g = Fixture::new();
8681        g.run(&[b"HELLO", b"3"]);
8682        g.run(&[b"ARINSERT", b"a", b"x"]);
8683        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
8684        assert!(map.starts_with("%12\r\n"), "{map}");
8685        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
8686        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
8687    }
8688
8689    #[test]
8690    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
8691        let mut f = Fixture::new();
8692        // Whole numbers up to two to the sixty second come back as integers,
8693        // and past that the digit generator takes over and uses an exponent.
8694        for (score, want) in [
8695            ("3", "3"),
8696            ("3.5", "3.5"),
8697            ("0.3", "0.3"),
8698            ("1e30", "1e+30"),
8699            ("1e19", "1e+19"),
8700            ("1e-7", "1e-7"),
8701            ("0.000001", "0.000001"),
8702            ("4611686018427387904", "4611686018427387904"),
8703            ("-0", "-0"),
8704        ] {
8705            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
8706            assert_eq!(
8707                f.run(&[b"ZSCORE", b"z", b"m"]),
8708                format!("${}\r\n{want}\r\n", want.len()),
8709                "score {score}"
8710            );
8711        }
8712
8713        // The same bytes on RESP3, where the reply is a double rather than a
8714        // bulk string.
8715        let mut g = Fixture::new();
8716        g.run(&[b"HELLO", b"3"]);
8717        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
8718        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
8719        // The two float increments are not this printer. They go through
8720        // ld2string in its human mode, which is a fixed point conversion with
8721        // the trailing zeros taken off, so they never write an exponent, and
8722        // they reply with a bulk string on both protocols.
8723        assert_eq!(
8724            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
8725            "$31\r\n1000000000000000000000000000000\r\n"
8726        );
8727        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
8728        assert_eq!(
8729            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
8730            "$20\r\n10000000000000000000\r\n"
8731        );
8732    }
8733
8734    // ----------------------------------------------------------------- graph
8735
8736    #[test]
8737    fn a_node_comes_back_with_the_fields_it_went_in_with() {
8738        let mut f = Fixture::new();
8739        assert_eq!(
8740            f.run(&[
8741                b"G.NADD", b"social", b"ada", b"name", b"Ada", b"born", b"1815"
8742            ]),
8743            ":1\r\n"
8744        );
8745        // The year comes back as the four bytes that were sent and not as a
8746        // number, because every property is text and there is nothing on the
8747        // wire that says which of `1815` and `"1815"` the client meant. The
8748        // fields are in the document's order, which is sorted by name, because
8749        // that is what makes a field lookup a binary search.
8750        assert_eq!(
8751            f.run(&[b"G.NGET", b"social", b"ada"]),
8752            "*4\r\n$4\r\nborn\r\n$4\r\n1815\r\n$4\r\nname\r\n$3\r\nAda\r\n"
8753        );
8754        // A second write to the same id replaces the document and says so with
8755        // a zero, so an ingest can count what it created.
8756        assert_eq!(
8757            f.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada Lovelace"]),
8758            ":0\r\n"
8759        );
8760        assert_eq!(
8761            f.run(&[b"G.NGET", b"social", b"ada"]),
8762            "*2\r\n$4\r\nname\r\n$12\r\nAda Lovelace\r\n"
8763        );
8764        // A node with no properties is an empty map and not a null, which is
8765        // how a client tells an isolated node from one that is not there.
8766        assert_eq!(f.run(&[b"G.NADD", b"social", b"grace"]), ":1\r\n");
8767        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
8768        assert_eq!(f.run(&[b"G.NGET", b"social", b"nobody"]), "$-1\r\n");
8769        assert_eq!(f.run(&[b"G.NGET", b"nokey", b"ada"]), "$-1\r\n");
8770
8771        // A field with no value creates nothing, because the pairs are checked
8772        // before the key is touched.
8773        assert_eq!(
8774            f.run(&[b"G.NADD", b"fresh", b"n", b"lonely"]),
8775            "-ERR syntax error\r\n"
8776        );
8777        assert_eq!(f.run(&[b"EXISTS", b"fresh"]), ":0\r\n");
8778
8779        // On RESP3 the same reply is a map.
8780        let mut g = Fixture::new();
8781        g.run(&[b"HELLO", b"3"]);
8782        g.run(&[b"G.NADD", b"social", b"ada", b"name", b"Ada"]);
8783        assert_eq!(
8784            g.run(&[b"G.NGET", b"social", b"ada"]),
8785            "%1\r\n$4\r\nname\r\n$3\r\nAda\r\n"
8786        );
8787    }
8788
8789    #[test]
8790    fn an_edge_creates_the_ends_it_needs() {
8791        let mut f = Fixture::new();
8792        assert_eq!(
8793            f.run(&[
8794                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1843"
8795            ]),
8796            ":1\r\n"
8797        );
8798        // Neither end was written first and both are there, as empty nodes.
8799        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "*0\r\n");
8800        assert_eq!(f.run(&[b"G.NGET", b"social", b"grace"]), "*0\r\n");
8801        assert_eq!(
8802            f.run(&[b"G.OUT", b"social", b"ada", b"FOLLOWS"]),
8803            "*2\r\n$1\r\n0\r\n*1\r\n$5\r\ngrace\r\n"
8804        );
8805        assert_eq!(
8806            f.run(&[b"G.IN", b"social", b"grace", b"FOLLOWS"]),
8807            "*2\r\n$1\r\n0\r\n*1\r\n$3\r\nada\r\n"
8808        );
8809        // The same pair under the same label again updates the edge rather than
8810        // making a second one.
8811        assert_eq!(
8812            f.run(&[
8813                b"G.EADD", b"social", b"ada", b"grace", b"FOLLOWS", b"since", b"1844"
8814            ]),
8815            ":0\r\n"
8816        );
8817        assert_eq!(f.run(&[b"G.DEG", b"social", b"ada", b"FOLLOWS"]), ":1\r\n");
8818        // A different label between the same pair is a different edge.
8819        assert_eq!(
8820            f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"WORKS_WITH"]),
8821            ":1\r\n"
8822        );
8823        assert_eq!(
8824            f.run(&[b"G.DEG", b"social", b"ada", b"WORKS_WITH"]),
8825            ":1\r\n"
8826        );
8827
8828        assert_eq!(
8829            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
8830            ":1\r\n"
8831        );
8832        assert_eq!(
8833            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"FOLLOWS"]),
8834            ":0\r\n"
8835        );
8836        // A label nothing has used, an end that is not there, and a key that is
8837        // not there are all a zero rather than an error.
8838        assert_eq!(
8839            f.run(&[b"G.EDEL", b"social", b"ada", b"grace", b"NEVER"]),
8840            ":0\r\n"
8841        );
8842        assert_eq!(
8843            f.run(&[b"G.EDEL", b"social", b"ada", b"nobody", b"FOLLOWS"]),
8844            ":0\r\n"
8845        );
8846        assert_eq!(
8847            f.run(&[b"G.EDEL", b"nokey", b"ada", b"grace", b"FOLLOWS"]),
8848            ":0\r\n"
8849        );
8850    }
8851
8852    /// A run is paged the way `SCAN` is paged, so a client that can walk one
8853    /// can walk the other.
8854    #[test]
8855    fn a_hop_answers_a_cursor_and_a_page() {
8856        let mut f = Fixture::new();
8857        for i in 0..25u32 {
8858            let dst = format!("n{i}");
8859            f.run(&[b"G.EADD", b"social", b"hub", dst.as_bytes(), b"FOLLOWS"]);
8860        }
8861        // Ten without being asked, and the cursor is where to carry on from.
8862        let first = f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS"]);
8863        assert!(first.starts_with("*2\r\n$2\r\n10\r\n*10\r\n"), "{first}");
8864
8865        let mut seen = 0;
8866        let mut cursor = String::from("0");
8867        loop {
8868            let page = f.run(&[
8869                b"G.OUT",
8870                b"social",
8871                b"hub",
8872                b"FOLLOWS",
8873                b"COUNT",
8874                b"7",
8875                b"CURSOR",
8876                cursor.as_bytes(),
8877            ]);
8878            let (head, rest) = page.split_once("\r\n*").expect("a cursor and a page");
8879            cursor = head
8880                .rsplit("\r\n")
8881                .next()
8882                .expect("the cursor line")
8883                .to_string();
8884            seen += rest
8885                .split_once("\r\n")
8886                .expect("the page length")
8887                .0
8888                .parse::<usize>()
8889                .expect("a length");
8890            if cursor == "0" {
8891                break;
8892            }
8893        }
8894        assert_eq!(seen, 25, "every neighbour once across the pages");
8895
8896        // A cursor past the end is an empty page and not an error, and so is a
8897        // key or a label that is not there.
8898        assert_eq!(
8899            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"CURSOR", b"900"]),
8900            "*2\r\n$1\r\n0\r\n*0\r\n"
8901        );
8902        assert_eq!(
8903            f.run(&[b"G.OUT", b"social", b"hub", b"NEVER"]),
8904            "*2\r\n$1\r\n0\r\n*0\r\n"
8905        );
8906        assert_eq!(
8907            f.run(&[b"G.OUT", b"nokey", b"hub", b"FOLLOWS"]),
8908            "*2\r\n$1\r\n0\r\n*0\r\n"
8909        );
8910        assert_eq!(
8911            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"COUNT", b"0"]),
8912            "-ERR COUNT must be a positive integer\r\n"
8913        );
8914        assert_eq!(
8915            f.run(&[b"G.OUT", b"social", b"hub", b"FOLLOWS", b"NOPE", b"1"]),
8916            "-ERR syntax error\r\n"
8917        );
8918    }
8919
8920    #[test]
8921    fn a_degree_counts_one_way_or_both() {
8922        let mut f = Fixture::new();
8923        f.run(&[b"G.EADD", b"social", b"a", b"b", b"F"]);
8924        f.run(&[b"G.EADD", b"social", b"a", b"c", b"F"]);
8925        f.run(&[b"G.EADD", b"social", b"d", b"a", b"F"]);
8926        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F"]), ":2\r\n");
8927        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"OUT"]), ":2\r\n");
8928        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"IN"]), ":1\r\n");
8929        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"F", b"BOTH"]), ":3\r\n");
8930        assert_eq!(f.run(&[b"G.DEG", b"social", b"nobody", b"F"]), ":0\r\n");
8931        assert_eq!(f.run(&[b"G.DEG", b"social", b"a", b"NEVER"]), ":0\r\n");
8932        assert_eq!(f.run(&[b"G.DEG", b"nokey", b"a", b"F"]), ":0\r\n");
8933        assert_eq!(
8934            f.run(&[b"G.DEG", b"social", b"a", b"F", b"SIDEWAYS"]),
8935            "-ERR syntax error\r\n"
8936        );
8937    }
8938
8939    /// A walk answers which nodes it can reach and not by how many routes, so a
8940    /// node two ways out is in the frontier once.
8941    #[test]
8942    fn a_walk_reaches_each_node_once_however_many_ways_there_are() {
8943        let mut f = Fixture::new();
8944        for (src, dst) in [
8945            ("ada", "grace"),
8946            ("ada", "alan"),
8947            ("grace", "edsger"),
8948            ("alan", "edsger"),
8949            ("edsger", "barbara"),
8950        ] {
8951            f.run(&[b"G.EADD", b"social", src.as_bytes(), dst.as_bytes(), b"F"]);
8952        }
8953        // Two hops without being asked, the start left out, and edsger once
8954        // even though both of the first hop's nodes point at it.
8955        assert_eq!(
8956            f.run(&[b"G.NEIGH", b"social", b"ada", b"F"]),
8957            "*3\r\n$5\r\ngrace\r\n$4\r\nalan\r\n$6\r\nedsger\r\n"
8958        );
8959        assert_eq!(
8960            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"1"]),
8961            "*2\r\n$5\r\ngrace\r\n$4\r\nalan\r\n"
8962        );
8963        let deep = f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"9"]);
8964        assert!(deep.starts_with("*4\r\n"), "the whole component: {deep}");
8965        assert!(deep.contains("$7\r\nbarbara\r\n"), "{deep}");
8966        // COUNT stops the walk rather than trimming what it found.
8967        assert_eq!(
8968            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"COUNT", b"1"]),
8969            "*1\r\n$5\r\ngrace\r\n"
8970        );
8971        // A node nothing leaves is an empty array and not an error.
8972        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"barbara", b"F"]), "*0\r\n");
8973        assert_eq!(f.run(&[b"G.NEIGH", b"social", b"ada", b"NEVER"]), "*0\r\n");
8974        assert_eq!(f.run(&[b"G.NEIGH", b"nokey", b"ada", b"F"]), "*0\r\n");
8975        assert_eq!(
8976            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"DEPTH", b"0"]),
8977            "-ERR DEPTH must be a positive integer\r\n"
8978        );
8979        assert_eq!(
8980            f.run(&[b"G.NEIGH", b"social", b"ada", b"F", b"NOPE", b"1"]),
8981            "-ERR syntax error\r\n"
8982        );
8983    }
8984
8985    /// The two sided search, which is the whole reason `G.PATH` is a command
8986    /// and not something a client builds out of `G.OUT`.
8987    #[test]
8988    fn a_path_is_the_shortest_one_and_goes_over_any_label() {
8989        let mut f = Fixture::new();
8990        // A chain of six, and a shortcut that makes a shorter way round under a
8991        // second label so the search has to take either kind of hop.
8992        for i in 0..6u32 {
8993            let src = format!("n{i}");
8994            let dst = format!("n{}", i + 1);
8995            f.run(&[b"G.EADD", b"road", src.as_bytes(), dst.as_bytes(), b"STEP"]);
8996        }
8997        assert_eq!(
8998            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
8999            "*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"
9000        );
9001        f.run(&[b"G.EADD", b"road", b"n0", b"n5", b"JUMP"]);
9002        assert_eq!(
9003            f.run(&[b"G.PATH", b"road", b"n0", b"n6"]),
9004            "*3\r\n$2\r\nn0\r\n$2\r\nn5\r\n$2\r\nn6\r\n"
9005        );
9006        // A node to itself is a path of one, and a depth too short to reach is
9007        // no path at all.
9008        assert_eq!(
9009            f.run(&[b"G.PATH", b"road", b"n2", b"n2"]),
9010            "*1\r\n$2\r\nn2\r\n"
9011        );
9012        assert_eq!(
9013            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"MAXDEPTH", b"1"]),
9014            "*0\r\n"
9015        );
9016        // Direction counts: the chain only goes one way.
9017        assert_eq!(f.run(&[b"G.PATH", b"road", b"n6", b"n0"]), "*0\r\n");
9018        // An unreachable node, a node that is not there, and a key that is not
9019        // there are the same empty answer.
9020        f.run(&[b"G.NADD", b"road", b"island"]);
9021        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"island"]), "*0\r\n");
9022        assert_eq!(f.run(&[b"G.PATH", b"road", b"n0", b"nobody"]), "*0\r\n");
9023        assert_eq!(f.run(&[b"G.PATH", b"nokey", b"n0", b"n6"]), "*0\r\n");
9024        assert_eq!(
9025            f.run(&[b"G.PATH", b"road", b"n0", b"n6", b"NOPE", b"3"]),
9026            "-ERR syntax error\r\n"
9027        );
9028    }
9029
9030    /// The point of the escape in the record tag: the keyspace owns a graph key
9031    /// the way it owns every other key, and none of these commands know a graph
9032    /// exists.
9033    #[test]
9034    fn the_keyspace_sees_a_graph_key_like_any_other() {
9035        let mut f = Fixture::new();
9036        f.run(&[b"G.EADD", b"social", b"ada", b"grace", b"F"]);
9037        assert_eq!(f.run(&[b"TYPE", b"social"]), "+graph\r\n");
9038        assert_eq!(
9039            f.run(&[b"OBJECT", b"ENCODING", b"social"]),
9040            "$9\r\nadjacency\r\n"
9041        );
9042        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
9043        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
9044        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$6\r\nsocial\r\n");
9045        // A graph is counted against the server the way every other body is,
9046        // which is what `maxmemory` will read when this key is a million nodes.
9047        // There is no `MEMORY USAGE` command yet, so this asks the server.
9048        let held = f.server.memory_bytes();
9049        for i in 0..200u32 {
9050            let dst = format!("n{i}");
9051            f.run(&[b"G.EADD", b"big", b"hub", dst.as_bytes(), b"F"]);
9052        }
9053        assert!(
9054            f.server.memory_bytes() > held,
9055            "two hundred edges cost something: {held} then {}",
9056            f.server.memory_bytes()
9057        );
9058        f.run(&[b"DEL", b"big"]);
9059
9060        // An expiry, then a rename, then a move to another database, all of
9061        // which are the keyspace moving a record it cannot look inside.
9062        assert_eq!(f.run(&[b"EXPIRE", b"social", b"100"]), ":1\r\n");
9063        assert_eq!(f.run(&[b"PERSIST", b"social"]), ":1\r\n");
9064        assert_eq!(f.run(&[b"RENAME", b"social", b"net"]), "+OK\r\n");
9065        assert_eq!(f.run(&[b"MOVE", b"net", b"1"]), ":1\r\n");
9066        assert_eq!(f.run(&[b"EXISTS", b"net"]), ":0\r\n");
9067        f.run(&[b"SELECT", b"1"]);
9068        assert_eq!(f.run(&[b"G.DEG", b"net", b"ada", b"F"]), ":1\r\n");
9069
9070        assert_eq!(f.run(&[b"DEL", b"net"]), ":1\r\n");
9071        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9072        f.run(&[b"G.NADD", b"g", b"n"]);
9073        assert_eq!(f.run(&[b"FLUSHDB"]), "+OK\r\n");
9074        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9075    }
9076
9077    /// Neither `COPY` nor `DUMP` has a byte shape for a graph, so both say so
9078    /// rather than answering the way they answer for a key that is not there.
9079    #[test]
9080    fn a_graph_cannot_be_copied_or_dumped() {
9081        let mut f = Fixture::new();
9082        f.run(&[b"G.NADD", b"social", b"ada"]);
9083        assert_eq!(
9084            f.run(&[b"COPY", b"social", b"other"]),
9085            "-ERR COPY is not supported for a graph\r\n"
9086        );
9087        assert_eq!(
9088            f.run(&[b"COPY", b"social", b"other", b"DB", b"1"]),
9089            "-ERR COPY is not supported for a graph\r\n"
9090        );
9091        assert_eq!(
9092            f.run(&[b"DUMP", b"social"]),
9093            "-ERR DUMP is not supported for a graph\r\n"
9094        );
9095        // A refused copy leaves both keys exactly as they were.
9096        assert_eq!(f.run(&[b"EXISTS", b"social", b"other"]), ":1\r\n");
9097    }
9098
9099    /// A graph key is a key, so the commands for the other types refuse it and
9100    /// the graph commands refuse theirs.
9101    #[test]
9102    fn a_graph_and_a_string_are_the_wrong_type_for_each_other() {
9103        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
9104        let mut f = Fixture::new();
9105        f.run(&[b"G.NADD", b"social", b"ada"]);
9106        assert_eq!(f.run(&[b"GET", b"social"]), wrong);
9107        assert_eq!(f.run(&[b"LPUSH", b"social", b"x"]), wrong);
9108        assert_eq!(f.run(&[b"SADD", b"social", b"x"]), wrong);
9109
9110        f.run(&[b"SET", b"str", b"v"]);
9111        for cmd in [
9112            vec![b"G.NADD".as_ref(), b"str", b"n"],
9113            vec![b"G.NGET".as_ref(), b"str", b"n"],
9114            vec![b"G.NDEL".as_ref(), b"str", b"n"],
9115            vec![b"G.EADD".as_ref(), b"str", b"a", b"b", b"F"],
9116            vec![b"G.EDEL".as_ref(), b"str", b"a", b"b", b"F"],
9117            vec![b"G.OUT".as_ref(), b"str", b"a", b"F"],
9118            vec![b"G.IN".as_ref(), b"str", b"a", b"F"],
9119            vec![b"G.DEG".as_ref(), b"str", b"a", b"F"],
9120            vec![b"G.NEIGH".as_ref(), b"str", b"a", b"F"],
9121            vec![b"G.PATH".as_ref(), b"str", b"a", b"b"],
9122        ] {
9123            assert_eq!(f.run(&cmd), wrong, "{:?}", cmd[0]);
9124        }
9125    }
9126
9127    /// Every other collection here takes its key with it when its last member
9128    /// goes, and a graph is no different.
9129    #[test]
9130    fn a_graph_goes_when_its_last_node_does() {
9131        let mut f = Fixture::new();
9132        f.run(&[
9133            b"G.EADD", b"social", b"ada", b"grace", b"F", b"since", b"1843",
9134        ]);
9135        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":1\r\n");
9136        // The node and the edges that hung off it are both gone.
9137        assert_eq!(f.run(&[b"G.NGET", b"social", b"ada"]), "$-1\r\n");
9138        assert_eq!(
9139            f.run(&[b"G.DEG", b"social", b"grace", b"F", b"IN"]),
9140            ":0\r\n"
9141        );
9142        assert_eq!(f.run(&[b"G.NDEL", b"social", b"ada"]), ":0\r\n");
9143        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":1\r\n");
9144
9145        assert_eq!(f.run(&[b"G.NDEL", b"social", b"grace"]), ":1\r\n");
9146        assert_eq!(f.run(&[b"EXISTS", b"social"]), ":0\r\n");
9147        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
9148        assert_eq!(f.run(&[b"G.NDEL", b"nokey", b"ada"]), ":0\r\n");
9149
9150        // The id the removed node had is not handed out again, so a client
9151        // holding an id from an earlier reply cannot have it mean another node.
9152        f.run(&[b"G.NADD", b"social", b"first"]);
9153        f.run(&[b"G.NADD", b"social", b"second"]);
9154        f.run(&[b"G.NDEL", b"social", b"first"]);
9155        f.run(&[b"G.EADD", b"social", b"third", b"second", b"F"]);
9156        assert_eq!(
9157            f.run(&[b"G.OUT", b"social", b"third", b"F"]),
9158            "*2\r\n$1\r\n0\r\n*1\r\n$6\r\nsecond\r\n"
9159        );
9160    }
9161
9162    /// The three shapes an `XADD` id can take, and the one rule behind all of
9163    /// them.
9164    #[test]
9165    fn xadd_ids_only_ever_go_up() {
9166        let mut f = Fixture::new();
9167        // A bare millisecond is that millisecond and sequence zero.
9168        assert_eq!(f.run(&[b"XADD", b"s", b"5", b"a", b"1"]), "$3\r\n5-0\r\n");
9169        // And `5-*` is the next free sequence inside it.
9170        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"2"]), "$3\r\n5-1\r\n");
9171        assert_eq!(f.run(&[b"XADD", b"s", b"5-*", b"a", b"3"]), "$3\r\n5-2\r\n");
9172        assert_eq!(f.run(&[b"XADD", b"s", b"6-9", b"a", b"4"]), "$3\r\n6-9\r\n");
9173        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
9174
9175        assert!(
9176            f.run(&[b"XADD", b"s", b"6-9", b"a", b"5"])
9177                .contains("equal or smaller")
9178        );
9179        assert!(
9180            f.run(&[b"XADD", b"s", b"0-0", b"a", b"5"])
9181                .contains("must be greater than 0-0")
9182        );
9183        assert!(
9184            f.run(&[b"XADD", b"s", b"nonsense", b"a", b"5"])
9185                .contains("Invalid stream ID")
9186        );
9187        // The pairs have to be pairs, and Redis calls an odd one an arity error
9188        // rather than a syntax error even though the table has already passed.
9189        assert!(
9190            f.run(&[b"XADD", b"s", b"*", b"a"])
9191                .contains("wrong number of arguments")
9192        );
9193
9194        // `NOMKSTREAM` on a key that is not there is a null and not a zero, so a
9195        // producer can tell nobody is consuming this yet from the write landed.
9196        assert_eq!(
9197            f.run(&[b"XADD", b"gone", b"NOMKSTREAM", b"*", b"a", b"1"]),
9198            "$-1\r\n"
9199        );
9200        assert_eq!(f.run(&[b"EXISTS", b"gone"]), ":0\r\n");
9201        assert_eq!(f.run(&[b"TYPE", b"s"]), "+stream\r\n");
9202        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$6\r\nstream\r\n");
9203    }
9204
9205    /// The trim options, which are three keywords that disagree about how many
9206    /// arguments they take.
9207    #[test]
9208    fn trimming_reads_its_options_the_way_redis_does() {
9209        let mut f = Fixture::new();
9210        for i in 1..=10u32 {
9211            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
9212        }
9213        assert_eq!(f.run(&[b"XTRIM", b"s", b"MAXLEN", b"4"]), ":6\r\n");
9214        assert_eq!(f.run(&[b"XLEN", b"s"]), ":4\r\n");
9215        assert_eq!(f.run(&[b"XTRIM", b"s", b"MINID", b"9"]), ":2\r\n");
9216        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9217
9218        // One argument after the keyword and the `~` is read as the threshold,
9219        // which is what a real server does and is the reason this is a number
9220        // complaint and not a syntax one.
9221        assert!(
9222            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"~"])
9223                .contains("not an integer")
9224        );
9225        assert!(
9226            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"-1"])
9227                .contains("MAXLEN argument must be >= 0")
9228        );
9229        // The strategy check runs before the approximation check, so a LIMIT
9230        // with neither is told about the missing strategy.
9231        assert!(
9232            f.run(&[b"XTRIM", b"s", b"LIMIT", b"5"])
9233                .contains("without specifying a trimming strategy")
9234        );
9235        assert!(
9236            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"LIMIT", b"5"])
9237                .contains("without the special ~ option")
9238        );
9239        assert!(
9240            f.run(&[b"XTRIM", b"s", b"MAXLEN", b"5", b"MINID", b"5"])
9241                .contains("at the same time are not compatible")
9242        );
9243        // NOMKSTREAM is XADD's and XTRIM does not take it.
9244        assert!(
9245            f.run(&[b"XTRIM", b"s", b"NOMKSTREAM", b"MAXLEN", b"5"])
9246                .contains("syntax error")
9247        );
9248        assert_eq!(f.run(&[b"XTRIM", b"missing", b"MAXLEN", b"5"]), ":0\r\n");
9249    }
9250
9251    /// `XRANGE`, whose two kinds of nothing are the thing worth pinning.
9252    #[test]
9253    fn xrange_looks_the_key_up_before_it_reads_the_count() {
9254        let mut f = Fixture::new();
9255        f.run(&[b"XADD", b"s", b"5-1", b"a", b"1"]);
9256        f.run(&[b"XADD", b"s", b"6-1", b"b", b"2"]);
9257
9258        assert_eq!(
9259            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
9260            "*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\
9261             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
9262        );
9263        assert_eq!(
9264            f.run(&[b"XREVRANGE", b"s", b"+", b"-", b"COUNT", b"1"]),
9265            "*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"
9266        );
9267        // The exclusive bound is stepped after the missing sequence is filled
9268        // in, so `(6` is `6-` and the largest sequence there is, minus one, and
9269        // `6-1` is still in the range.
9270        assert_eq!(
9271            f.run(&[b"XRANGE", b"s", b"-", b"(6"]),
9272            "*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\
9273             *2\r\n$3\r\n6-1\r\n*2\r\n$1\r\nb\r\n$1\r\n2\r\n"
9274        );
9275        assert_eq!(
9276            f.run(&[b"XRANGE", b"s", b"(5-1", b"+"]),
9277            "*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"
9278        );
9279        assert!(
9280            f.run(&[b"XRANGE", b"s", b"(-", b"+"])
9281                .contains("Invalid stream ID")
9282        );
9283
9284        // The two kinds of nothing. A key that is not there is an empty array
9285        // and a key that is there with a count of zero is a null array, because
9286        // the lookup happens first.
9287        assert_eq!(
9288            f.run(&[b"XRANGE", b"missing", b"-", b"+", b"COUNT", b"0"]),
9289            "*0\r\n"
9290        );
9291        assert_eq!(
9292            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"0"]),
9293            "*-1\r\n"
9294        );
9295        f.run(&[b"SET", b"str", b"v"]);
9296        assert!(
9297            f.run(&[b"XRANGE", b"str", b"-", b"+", b"COUNT", b"0"])
9298                .starts_with("-WRONGTYPE")
9299        );
9300        // The count is read in a loop, so the last one wins.
9301        assert_eq!(
9302            f.run(&[b"XRANGE", b"s", b"-", b"+", b"COUNT", b"2", b"COUNT", b"1"]),
9303            "*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"
9304        );
9305    }
9306
9307    /// `XDEL` and `XACK` check every id before they touch any of them.
9308    #[test]
9309    fn a_bad_id_late_in_the_list_stops_the_whole_command() {
9310        let mut f = Fixture::new();
9311        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9312        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9313        assert!(
9314            f.run(&[b"XDEL", b"s", b"1-1", b"nonsense"])
9315                .contains("Invalid stream ID")
9316        );
9317        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9318        assert_eq!(f.run(&[b"XDEL", b"s", b"1-1", b"9-9"]), ":1\r\n");
9319        assert_eq!(f.run(&[b"XLEN", b"s"]), ":1\r\n");
9320        assert_eq!(f.run(&[b"XDEL", b"missing", b"1-1"]), ":0\r\n");
9321        assert_eq!(f.run(&[b"XACK", b"missing", b"g", b"1-1"]), ":0\r\n");
9322    }
9323
9324    /// `XGROUP`, and the two different complaints it makes about arguments.
9325    #[test]
9326    fn xgroup_has_an_arity_per_subcommand() {
9327        let mut f = Fixture::new();
9328        assert!(
9329            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
9330                .contains("requires the key")
9331        );
9332        assert_eq!(
9333            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$", b"MKSTREAM"]),
9334            "+OK\r\n"
9335        );
9336        // A second CREATE is BUSYGROUP and not an ordinary error, because a
9337        // client racing another one to make a group branches on the prefix.
9338        assert!(
9339            f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"])
9340                .starts_with("-BUSYGROUP")
9341        );
9342        assert_eq!(
9343            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
9344            ":1\r\n"
9345        );
9346        assert_eq!(
9347            f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c"]),
9348            ":0\r\n"
9349        );
9350        assert_eq!(
9351            f.run(&[b"XGROUP", b"DELCONSUMER", b"s", b"g", b"c"]),
9352            ":0\r\n"
9353        );
9354
9355        // Below the subcommand's own arity is an arity error naming the pair.
9356        let short = f.run(&[b"XGROUP", b"DESTROY", b"s"]);
9357        assert!(
9358            short.contains("wrong number of arguments for 'xgroup|destroy' command"),
9359            "{short}"
9360        );
9361        // At or above it in a shape the handler will not take is the other one.
9362        let odd = f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0", b"ENTRIESREAD"]);
9363        assert!(
9364            odd.contains("unknown subcommand or wrong number of arguments for 'SETID'"),
9365            "{odd}"
9366        );
9367        assert!(
9368            f.run(&[b"XGROUP", b"NOSUCH", b"s"])
9369                .contains("Try XGROUP HELP")
9370        );
9371
9372        assert_eq!(f.run(&[b"XGROUP", b"SETID", b"s", b"g", b"0"]), "+OK\r\n");
9373        assert!(
9374            f.run(&[b"XGROUP", b"SETID", b"s", b"nogroup", b"0"])
9375                .starts_with("-NOGROUP")
9376        );
9377        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":1\r\n");
9378        assert_eq!(f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]), ":0\r\n");
9379        assert!(
9380            f.run(&[b"XGROUP", b"DESTROY", b"missing", b"g"])
9381                .contains("requires the key")
9382        );
9383    }
9384
9385    /// A group read, an acknowledgement, and what is left in between.
9386    #[test]
9387    fn xreadgroup_hands_out_and_xack_takes_back() {
9388        let mut f = Fixture::new();
9389        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9390        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9391        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9392
9393        let first = f.run(&[
9394            b"XREADGROUP",
9395            b"GROUP",
9396            b"g",
9397            b"c1",
9398            b"COUNT",
9399            b"1",
9400            b"STREAMS",
9401            b"s",
9402            b">",
9403        ]);
9404        assert_eq!(
9405            first,
9406            "*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"
9407        );
9408        // A history read names its stream even with nothing to show, which is
9409        // the difference between it and a `>` read that found nothing.
9410        assert_eq!(
9411            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b"0"]),
9412            "*1\r\n*2\r\n$1\r\ns\r\n*0\r\n"
9413        );
9414        assert_eq!(
9415            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
9416            "*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"
9417        );
9418
9419        assert_eq!(
9420            f.run(&[b"XPENDING", b"s", b"g"]),
9421            "*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"
9422        );
9423        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":1\r\n");
9424        assert_eq!(f.run(&[b"XACK", b"s", b"g", b"1-1"]), ":0\r\n");
9425        // Empty is four nulls and not a zero with three empty things.
9426        assert_eq!(
9427            f.run(&[b"XPENDING", b"s", b"g"]),
9428            "*4\r\n:0\r\n$-1\r\n$-1\r\n*-1\r\n"
9429        );
9430
9431        // A history read of an entry that has since been deleted is the id with
9432        // a null beside it, so the consumer can still acknowledge it.
9433        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9434        f.run(&[b"XDEL", b"s", b"2-1"]);
9435        assert_eq!(
9436            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b"0"]),
9437            "*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"
9438        );
9439
9440        // The group lookup runs before the id parse, so a `+` at a stream with
9441        // no such group is told about the group and not about the id.
9442        assert!(
9443            f.run(&[
9444                b"XREADGROUP",
9445                b"GROUP",
9446                b"nope",
9447                b"c",
9448                b"STREAMS",
9449                b"s",
9450                b"+"
9451            ])
9452            .starts_with("-NOGROUP")
9453        );
9454        assert!(
9455            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"$"])
9456                .contains("meaningless in the context of XREADGROUP")
9457        );
9458        assert!(
9459            f.run(&[b"XREAD", b"GROUP", b"g", b"c", b"STREAMS", b"s", b"0"])
9460                .contains("only supported by XREADGROUP")
9461        );
9462        assert!(
9463            f.run(&[
9464                b"XREADGROUP",
9465                b"GROUP",
9466                b"g",
9467                b"c",
9468                b"STREAMS",
9469                b"s",
9470                b"a",
9471                b"b"
9472            ])
9473            .contains("Unbalanced 'xreadgroup' list of streams")
9474        );
9475    }
9476
9477    /// `XREAD` without `BLOCK`, which answers now and takes nothing for an
9478    /// answer.
9479    #[test]
9480    fn xread_with_no_block_writes_the_null_itself() {
9481        let mut f = Fixture::new();
9482        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9483        assert_eq!(
9484            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
9485            "*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"
9486        );
9487        // Nothing new is a null array and not an empty one, and a stream with
9488        // nothing new is left out rather than sent with an empty list.
9489        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "*-1\r\n");
9490        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"missing", b"0"]), "*-1\r\n");
9491        f.run(&[b"XADD", b"other", b"1-1", b"b", b"2"]);
9492        assert_eq!(
9493            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"1-1", b"0"]),
9494            "*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"
9495        );
9496        // `$` is the last id, so nothing that is already there comes back.
9497        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"$"]), "*-1\r\n");
9498        // And `+` is the last entry, whatever COUNT says.
9499        assert_eq!(
9500            f.run(&[b"XREAD", b"COUNT", b"5", b"STREAMS", b"s", b"+"]),
9501            "*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"
9502        );
9503        // A count of zero means unlimited here, which is the opposite of what it
9504        // means to XRANGE.
9505        assert_eq!(
9506            f.run(&[b"XREAD", b"COUNT", b"0", b"STREAMS", b"s", b"0"]),
9507            "*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"
9508        );
9509        // Milliseconds as a whole number, where BLPOP takes seconds as a float.
9510        assert!(
9511            f.run(&[b"XREAD", b"BLOCK", b"0.5", b"STREAMS", b"s", b"$"])
9512                .contains("not an integer")
9513        );
9514        assert!(
9515            f.run(&[b"XREAD", b"BLOCK", b"-1", b"STREAMS", b"s", b"$"])
9516                .contains("timeout is negative")
9517        );
9518        assert!(
9519            f.run(&[b"XREAD", b"STREAMS", b"s", b"other", b"0"])
9520                .contains("Unbalanced 'xread' list of streams")
9521        );
9522    }
9523
9524    /// A blocked reader, and the two ways it stops being blocked.
9525    #[test]
9526    fn a_blocked_xread_wakes_on_the_next_entry() {
9527        let mut f = Fixture::new();
9528        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9529        let (flow, reply) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
9530        assert_eq!(flow, Flow::Block);
9531        assert!(reply.is_empty());
9532
9533        // Everybody parked on the stream gets the entry, because a read takes
9534        // nothing away. That is the difference between this and BLPOP.
9535        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"0", b"STREAMS", b"s", b"$"]);
9536        assert_eq!(flow, Flow::Block);
9537        assert_eq!(f.server.waiters().len(), 2);
9538
9539        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9540        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";
9541        for at in 0..2 {
9542            let mut out = Out::new(Proto::Resp2);
9543            assert!(f.server.serve_waiter(at, 0, &mut out));
9544            assert_eq!(core::str::from_utf8(out.as_slice()).expect("ascii"), want);
9545        }
9546
9547        // And a deadline that runs out is a null array, the same as a plain
9548        // XREAD that found nothing.
9549        f.server.waiters_mut().forget(7);
9550        let (flow, _) = f.flow(&[b"XREAD", b"BLOCK", b"50", b"STREAMS", b"s", b"$"]);
9551        assert_eq!(flow, Flow::Block);
9552        let mut out = Out::new(Proto::Resp2);
9553        assert!(!f.server.serve_waiter(0, 0, &mut out));
9554        assert!(out.as_slice().is_empty());
9555        assert!(f.server.serve_waiter(0, u64::MAX, &mut out));
9556        assert_eq!(
9557            core::str::from_utf8(out.as_slice()).expect("ascii"),
9558            "*-1\r\n"
9559        );
9560    }
9561
9562    /// A blocked group reader whose group is destroyed under it.
9563    #[test]
9564    fn losing_a_group_while_blocked_is_the_ordinary_sentence() {
9565        let mut f = Fixture::new();
9566        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9567        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"$"]);
9568        let (flow, _) = f.flow(&[
9569            b"XREADGROUP",
9570            b"GROUP",
9571            b"g",
9572            b"c",
9573            b"BLOCK",
9574            b"0",
9575            b"STREAMS",
9576            b"s",
9577            b">",
9578        ]);
9579        assert_eq!(flow, Flow::Block);
9580
9581        f.run(&[b"XGROUP", b"DESTROY", b"s", b"g"]);
9582        let mut out = Out::new(Proto::Resp2);
9583        assert!(f.server.serve_waiter(0, 0, &mut out));
9584        // The ordinary sentence and not a special one about having been parked,
9585        // which is what a running 8.10 sends.
9586        assert_eq!(
9587            core::str::from_utf8(out.as_slice()).expect("ascii"),
9588            "-NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option\r\n"
9589        );
9590    }
9591
9592    /// `XCLAIM`, whose argument shape is the odd one in the group.
9593    #[test]
9594    fn xclaim_reads_ids_until_one_will_not_parse() {
9595        let mut f = Fixture::new();
9596        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9597        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9598        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9599        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9600
9601        // Everything after the first argument that is not an id is an option, so
9602        // a `-` is an unrecognised option and not a bad id.
9603        assert!(
9604            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"-"])
9605                .contains("Unrecognized XCLAIM option '-'")
9606        );
9607        assert_eq!(
9608            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"1-1", b"JUSTID"]),
9609            "*1\r\n$3\r\n1-1\r\n"
9610        );
9611        // An id that is pending but whose entry has gone is an empty answer, and
9612        // it leaves the pending list on the way past.
9613        f.run(&[b"XDEL", b"s", b"2-1"]);
9614        assert_eq!(
9615            f.run(&[b"XCLAIM", b"s", b"g", b"c2", b"0", b"2-1"]),
9616            "*0\r\n"
9617        );
9618        assert!(
9619            f.run(&[b"XPENDING", b"s", b"g"])
9620                .starts_with("*4\r\n:1\r\n")
9621        );
9622        assert!(
9623            f.run(&[b"XCLAIM", b"s", b"nope", b"c", b"0", b"1-1"])
9624                .starts_with("-NOGROUP")
9625        );
9626        assert!(
9627            f.run(&[b"XCLAIM", b"s", b"g", b"c", b"nan", b"1-1"])
9628                .contains("Invalid min-idle-time argument for XCLAIM")
9629        );
9630    }
9631
9632    /// `XAUTOCLAIM`, and the third value nobody expects.
9633    #[test]
9634    fn xautoclaim_reports_what_it_dropped() {
9635        let mut f = Fixture::new();
9636        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9637        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9638        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9639        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9640        f.run(&[b"XDEL", b"s", b"1-1"]);
9641
9642        // The cursor, what was claimed, and what was dropped for no longer being
9643        // in the stream. The third one is what makes a sweep converge.
9644        assert_eq!(
9645            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"JUSTID"]),
9646            "*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"
9647        );
9648        assert!(
9649            f.run(&[b"XAUTOCLAIM", b"s", b"g", b"c2", b"0", b"-", b"COUNT", b"0"])
9650                .contains("COUNT must be > 0")
9651        );
9652        assert!(
9653            f.run(&[b"XAUTOCLAIM", b"s", b"nope", b"c", b"0", b"-"])
9654                .starts_with("-NOGROUP")
9655        );
9656    }
9657
9658    /// `XDELEX`, which is `XDEL` with a say in what the groups keep.
9659    #[test]
9660    fn xdelex_answers_one_integer_an_id() {
9661        let mut f = Fixture::new();
9662        for i in 1..=4 {
9663            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
9664        }
9665        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9666        f.run(&[
9667            b"XREADGROUP",
9668            b"GROUP",
9669            b"g",
9670            b"c",
9671            b"COUNT",
9672            b"2",
9673            b"STREAMS",
9674            b"s",
9675            b">",
9676        ]);
9677
9678        // One means gone and minus one means it was not there to start with.
9679        assert_eq!(
9680            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1", b"9-9"]),
9681            "*2\r\n:1\r\n:-1\r\n"
9682        );
9683        // `KEEPREF` leaves the pending entry behind, so the group still counts
9684        // the one it was handed even though the entry has gone.
9685        assert!(
9686            f.run(&[b"XPENDING", b"s", b"g"])
9687                .starts_with("*4\r\n:2\r\n")
9688        );
9689        // `DELREF` takes it out of every pending list on the way past.
9690        assert_eq!(
9691            f.run(&[b"XDELEX", b"s", b"DELREF", b"IDS", b"1", b"2-1"]),
9692            "*1\r\n:1\r\n"
9693        );
9694        // `1-1` is still in the list, because the delete before it said KEEPREF.
9695        assert_eq!(
9696            f.run(&[b"XPENDING", b"s", b"g"]),
9697            "*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"
9698        );
9699
9700        // Two means somebody still wants it, and the question is wider than the
9701        // name: the group's bookmark is at `2-1`, so `4-1` is above it and is
9702        // refused even though no consumer has ever been handed it.
9703        assert_eq!(
9704            f.run(&[b"XDELEX", b"s", b"ACKED", b"IDS", b"2", b"3-1", b"4-1"]),
9705            "*2\r\n:2\r\n:2\r\n"
9706        );
9707
9708        // A key that is not there answers minus ones without reading the IDs.
9709        assert_eq!(
9710            f.run(&[b"XDELEX", b"nope", b"IDS", b"2", b"bad", b"worse"]),
9711            "*2\r\n:-1\r\n:-1\r\n"
9712        );
9713        // A key that is there validates every ID before deleting any of them.
9714        assert!(
9715            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"3-1", b"bad"])
9716                .starts_with("-ERR Invalid stream ID")
9717        );
9718        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9719
9720        assert!(
9721            f.run(&[b"XDELEX", b"s", b"IDS", b"0", b"1-1"])
9722                .contains("Number of IDs must be a positive integer")
9723        );
9724        assert!(
9725            f.run(&[b"XDELEX", b"s", b"IDS", b"2", b"1-1"])
9726                .contains("The `numids` parameter must match the number of arguments")
9727        );
9728        // The condition is one word, so a second one is a syntax error, and so
9729        // is one ID more than the count promised.
9730        assert!(
9731            f.run(&[b"XDELEX", b"s", b"KEEPREF", b"DELREF", b"IDS", b"1", b"1-1"])
9732                .starts_with("-ERR syntax error")
9733        );
9734        assert!(
9735            f.run(&[b"XDELEX", b"s", b"IDS", b"1", b"1-1", b"2-1"])
9736                .starts_with("-ERR syntax error")
9737        );
9738        // The key is looked up first, so the wrong type beats the syntax.
9739        f.run(&[b"SET", b"str", b"v"]);
9740        assert!(
9741            f.run(&[b"XDELEX", b"str", b"BOGUS", b"IDS", b"0", b"1-1"])
9742                .starts_with("-WRONGTYPE")
9743        );
9744    }
9745
9746    /// `XACKDEL`, whose reply is about the pending list and not about the log.
9747    #[test]
9748    fn xackdel_reports_what_the_group_was_holding() {
9749        let mut f = Fixture::new();
9750        for i in 1..=3 {
9751            f.run(&[b"XADD", b"s", format!("{i}-1").as_bytes(), b"a", b"1"]);
9752        }
9753        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9754        f.run(&[
9755            b"XREADGROUP",
9756            b"GROUP",
9757            b"g",
9758            b"c",
9759            b"COUNT",
9760            b"1",
9761            b"STREAMS",
9762            b"s",
9763            b">",
9764        ]);
9765
9766        // Minus one is not about the stream: `2-1` is sitting there unread and
9767        // still answers minus one, because the group was not holding it. It also
9768        // stays, since only an ID that was acknowledged is deleted.
9769        assert_eq!(
9770            f.run(&[b"XACKDEL", b"s", b"g", b"IDS", b"2", b"1-1", b"2-1"]),
9771            "*2\r\n:1\r\n:-1\r\n"
9772        );
9773        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9774
9775        // A missing group is minus one an ID and not a NOGROUP.
9776        assert_eq!(
9777            f.run(&[b"XACKDEL", b"s", b"nope", b"IDS", b"1", b"2-1"]),
9778            "*1\r\n:-1\r\n"
9779        );
9780        assert_eq!(
9781            f.run(&[b"XACKDEL", b"nope", b"g", b"IDS", b"1", b"2-1"]),
9782            "*1\r\n:-1\r\n"
9783        );
9784
9785        // The acknowledgement happens whatever the condition says, so an ACKED
9786        // that answers two has still emptied the pending list.
9787        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c", b"STREAMS", b"s", b">"]);
9788        f.run(&[b"XGROUP", b"CREATE", b"s", b"g2", b"0"]);
9789        assert_eq!(
9790            f.run(&[b"XACKDEL", b"s", b"g", b"ACKED", b"IDS", b"1", b"2-1"]),
9791            "*1\r\n:2\r\n"
9792        );
9793        assert_eq!(
9794            f.run(&[b"XPENDING", b"s", b"g"]),
9795            "*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"
9796        );
9797        assert_eq!(f.run(&[b"XLEN", b"s"]), ":2\r\n");
9798    }
9799
9800    /// `XNACK`, which hands an entry back to nobody.
9801    #[test]
9802    fn xnack_releases_an_entry_for_the_next_claim() {
9803        let mut f = Fixture::new();
9804        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9805        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9806        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9807        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
9808        // Twice, so the delivery count is two and the words have something to
9809        // do with it.
9810        f.run(&[b"XCLAIM", b"s", b"g", b"c1", b"0", b"1-1", b"2-1"]);
9811
9812        assert_eq!(
9813            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1"]),
9814            ":1\r\n"
9815        );
9816        // No owner, no idle time, and the count left where it was. A released
9817        // entry reads as idle for longer than any min-idle-time, which is what
9818        // puts it at the front of the next claim.
9819        assert_eq!(
9820            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]),
9821            "*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"
9822        );
9823        // The consumer no longer holds it, so a filtered XPENDING skips it.
9824        assert_eq!(
9825            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
9826            "*1\r\n*4\r\n$3\r\n2-1\r\n$2\r\nc1\r\n:0\r\n:2\r\n"
9827        );
9828        // The bookmark did not move, so a `>` read will not hand it out again.
9829        assert_eq!(
9830            f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c2", b"STREAMS", b"s", b">"]),
9831            "*-1\r\n"
9832        );
9833        // A claim at any min-idle-time takes it.
9834        assert_eq!(
9835            f.run(&[
9836                b"XAUTOCLAIM",
9837                b"s",
9838                b"g",
9839                b"c2",
9840                b"99999999",
9841                b"-",
9842                b"JUSTID"
9843            ]),
9844            "*3\r\n$3\r\n0-0\r\n*1\r\n$3\r\n1-1\r\n*0\r\n"
9845        );
9846
9847        // `SILENT` takes one off the count rather than putting it back to zero,
9848        // which only shows on an entry that has been handed out more than once.
9849        // It was delivered and then claimed, so it is on two and goes to one.
9850        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
9851        assert!(
9852            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9853                .contains(":-1\r\n:1\r\n")
9854        );
9855        // And it stops at zero rather than wrapping.
9856        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
9857        f.run(&[b"XNACK", b"s", b"g", b"SILENT", b"IDS", b"1", b"1-1"]);
9858        assert!(
9859            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9860                .contains(":-1\r\n:0\r\n")
9861        );
9862        // `FATAL` puts it at the ceiling, and `RETRYCOUNT` wins over the word.
9863        f.run(&[b"XNACK", b"s", b"g", b"FATAL", b"IDS", b"1", b"1-1"]);
9864        assert!(
9865            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9866                .contains(":9223372036854775807\r\n")
9867        );
9868        f.run(&[
9869            b"XNACK",
9870            b"s",
9871            b"g",
9872            b"FATAL",
9873            b"IDS",
9874            b"1",
9875            b"1-1",
9876            b"RETRYCOUNT",
9877            b"3",
9878        ]);
9879        assert!(
9880            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9881                .contains(":-1\r\n:3\r\n")
9882        );
9883
9884        // Releasing something the group is not holding is zero, and `FORCE`
9885        // makes the pending entry rather than answering zero. A forced entry
9886        // starts at zero, since there was no earlier count to keep.
9887        f.run(&[b"XACK", b"s", b"g", b"2-1"]);
9888        assert_eq!(
9889            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1"]),
9890            ":0\r\n"
9891        );
9892        assert_eq!(
9893            f.run(&[
9894                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"2-1", b"FORCE"
9895            ]),
9896            ":1\r\n"
9897        );
9898        assert!(
9899            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"])
9900                .contains(":-1\r\n:0\r\n")
9901        );
9902        // `FORCE` on an ID the stream does not have is still zero.
9903        assert_eq!(
9904            f.run(&[
9905                b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"9-9", b"FORCE"
9906            ]),
9907            ":0\r\n"
9908        );
9909
9910        // The group is looked up before the mode word, and it raises rather
9911        // than answering per ID the way the two delete commands do.
9912        assert_eq!(
9913            f.run(&[b"XNACK", b"s", b"nope", b"BOGUS", b"IDS", b"1", b"1-1"]),
9914            "-NOGROUP No such key 's' or consumer group 'nope'\r\n"
9915        );
9916        assert!(
9917            f.run(&[b"XNACK", b"s", b"g", b"BOGUS", b"IDS", b"1", b"1-1"])
9918                .starts_with("-ERR")
9919        );
9920        // Its own sentences, which are not the ones XDELEX uses.
9921        assert!(
9922            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"0", b"1-1"])
9923                .contains("numids must be a positive integer")
9924        );
9925        assert!(
9926            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"2", b"1-1"])
9927                .contains("number of IDs doesn't match numids")
9928        );
9929        // Everything past the counted IDs is an option, so one too many is an
9930        // option nobody recognises and not a count that does not add up.
9931        assert!(
9932            f.run(&[b"XNACK", b"s", b"g", b"FAIL", b"IDS", b"1", b"1-1", b"2-1"])
9933                .contains("Unrecognized XNACK option '2-1'")
9934        );
9935    }
9936
9937    /// `XINFO`, which is where the shape of the storage shows through.
9938    #[test]
9939    fn xinfo_reports_the_stream_the_groups_and_the_consumers() {
9940        let mut f = Fixture::new();
9941        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
9942        f.run(&[b"XADD", b"s", b"2-1", b"a", b"2"]);
9943        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
9944        f.run(&[
9945            b"XREADGROUP",
9946            b"GROUP",
9947            b"g",
9948            b"c1",
9949            b"COUNT",
9950            b"1",
9951            b"STREAMS",
9952            b"s",
9953            b">",
9954        ]);
9955
9956        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
9957        // Ten pairs, since the six idempotency fields have nothing behind them
9958        // here and a zero would claim they had. That is D-27.
9959        assert!(info.starts_with("*20\r\n"), "{info}");
9960        assert!(info.contains("$6\r\nlength\r\n:2\r\n"), "{info}");
9961        assert!(
9962            info.contains("$17\r\nlast-generated-id\r\n$3\r\n2-1\r\n"),
9963            "{info}"
9964        );
9965        assert!(info.contains("$13\r\nentries-added\r\n:2\r\n"), "{info}");
9966        assert!(info.contains("$6\r\ngroups\r\n:1\r\n"), "{info}");
9967
9968        let groups = f.run(&[b"XINFO", b"GROUPS", b"s"]);
9969        assert!(groups.starts_with("*1\r\n*12\r\n"), "{groups}");
9970        assert!(groups.contains("$9\r\nconsumers\r\n:1\r\n"), "{groups}");
9971        assert!(groups.contains("$7\r\npending\r\n:1\r\n"), "{groups}");
9972        assert!(groups.contains("$3\r\nlag\r\n:1\r\n"), "{groups}");
9973
9974        // A consumer that has never been given anything reports minus one for
9975        // inactive rather than the moment it turned up, which is what tells a
9976        // worker that is stuck from one that has nothing to do.
9977        f.run(&[b"XGROUP", b"CREATECONSUMER", b"s", b"g", b"c2"]);
9978        let consumers = f.run(&[b"XINFO", b"CONSUMERS", b"s", b"g"]);
9979        assert!(consumers.starts_with("*2\r\n"), "{consumers}");
9980        assert!(
9981            consumers.contains("$8\r\ninactive\r\n:-1\r\n"),
9982            "{consumers}"
9983        );
9984        // And in name order, which the storage does not hold them in.
9985        let c1 = consumers.find("c1").unwrap();
9986        let c2 = consumers.find("c2").unwrap();
9987        assert!(c1 < c2, "{consumers}");
9988
9989        let full = f.run(&[b"XINFO", b"STREAM", b"s", b"FULL"]);
9990        assert!(full.starts_with("*18\r\n"), "{full}");
9991        assert!(full.contains("$12\r\nnacked-count\r\n:0\r\n"), "{full}");
9992        assert!(full.contains("$11\r\nactive-time\r\n"), "{full}");
9993
9994        assert!(
9995            f.run(&[b"XINFO", b"STREAM", b"missing"])
9996                .contains("no such key")
9997        );
9998        assert!(
9999            f.run(&[b"XINFO", b"GROUPS", b"missing"])
10000                .contains("no such key")
10001        );
10002        assert!(
10003            f.run(&[b"XINFO", b"CONSUMERS", b"s", b"nope"])
10004                .starts_with("-NOGROUP")
10005        );
10006        assert!(
10007            f.run(&[b"XINFO", b"NOSUCH", b"s"])
10008                .contains("Try XINFO HELP")
10009        );
10010        assert!(f.run(&[b"XINFO", b"HELP"]).contains("XINFO <subcommand>"));
10011        assert!(f.run(&[b"XGROUP", b"HELP"]).contains("XGROUP <subcommand>"));
10012    }
10013
10014    /// `XPENDING`'s long form, which reads its arguments by counting them.
10015    #[test]
10016    fn xpending_takes_the_consumer_only_when_the_count_comes_out_right() {
10017        let mut f = Fixture::new();
10018        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
10019        f.run(&[b"XGROUP", b"CREATE", b"s", b"g", b"0"]);
10020        f.run(&[b"XREADGROUP", b"GROUP", b"g", b"c1", b"STREAMS", b"s", b">"]);
10021
10022        let list = f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10"]);
10023        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");
10024        assert_eq!(
10025            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"c1"]),
10026            "*1\r\n*4\r\n$3\r\n1-1\r\n$2\r\nc1\r\n:0\r\n:1\r\n"
10027        );
10028        // A consumer nobody has heard of holds nothing rather than erroring.
10029        assert_eq!(
10030            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"10", b"nope"]),
10031            "*0\r\n"
10032        );
10033        assert_eq!(
10034            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0", b"-", b"+", b"10"]),
10035            list
10036        );
10037        // IDLE is only read at position three.
10038        assert!(
10039            f.run(&[b"XPENDING", b"s", b"g", b"IDLE", b"0"])
10040                .contains("syntax error")
10041        );
10042        assert!(
10043            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+"])
10044                .contains("syntax error")
10045        );
10046        assert_eq!(
10047            f.run(&[b"XPENDING", b"s", b"g", b"-", b"+", b"-1"]),
10048            "*0\r\n"
10049        );
10050        assert!(
10051            f.run(&[b"XPENDING", b"missing", b"g"])
10052                .starts_with("-NOGROUP")
10053        );
10054    }
10055
10056    /// `XSETID`, which is three counters and two refusals.
10057    #[test]
10058    fn xsetid_will_not_go_below_what_is_there() {
10059        let mut f = Fixture::new();
10060        f.run(&[b"XADD", b"s", b"5-5", b"a", b"1"]);
10061        assert_eq!(f.run(&[b"XSETID", b"s", b"9-9"]), "+OK\r\n");
10062        assert_eq!(
10063            f.run(&[
10064                b"XSETID",
10065                b"s",
10066                b"10-1",
10067                b"ENTRIESADDED",
10068                b"7",
10069                b"MAXDELETEDID",
10070                b"9-1"
10071            ]),
10072            "+OK\r\n"
10073        );
10074        let info = f.run(&[b"XINFO", b"STREAM", b"s"]);
10075        assert!(info.contains("$13\r\nentries-added\r\n:7\r\n"), "{info}");
10076        assert!(
10077            info.contains("$20\r\nmax-deleted-entry-id\r\n$3\r\n9-1\r\n"),
10078            "{info}"
10079        );
10080
10081        assert!(
10082            f.run(&[b"XSETID", b"s", b"1-1"])
10083                .contains("smaller than the target stream top item")
10084        );
10085        assert!(
10086            f.run(&[b"XSETID", b"s", b"10-1", b"ENTRIESADDED", b"-1"])
10087                .contains("entries_added must be positive")
10088        );
10089        assert!(
10090            f.run(&[b"XSETID", b"missing", b"1-1"])
10091                .contains("no such key")
10092        );
10093    }
10094
10095    /// RESP3, where the two reads answer a map and the entries stay an array.
10096    #[test]
10097    fn xread_answers_a_map_on_resp3_and_the_fields_stay_flat() {
10098        let mut f = Fixture::new();
10099        f.run(&[b"HELLO", b"3"]);
10100        f.run(&[b"XADD", b"s", b"1-1", b"a", b"1"]);
10101        // A map header and then the key and the entries side by side, with no
10102        // two element array wrapping the pair.
10103        assert_eq!(
10104            f.run(&[b"XREAD", b"STREAMS", b"s", b"0"]),
10105            "%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"
10106        );
10107        // The fields are still one flat array and not a map, which is Redis's
10108        // shape and is what every consumer written before RESP3 expects.
10109        assert_eq!(
10110            f.run(&[b"XRANGE", b"s", b"-", b"+"]),
10111            "*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"
10112        );
10113        assert_eq!(f.run(&[b"XREAD", b"STREAMS", b"s", b"1-1"]), "_\r\n");
10114    }
10115
10116    /// A store to migrate values into, so a test can watch the inversion.
10117    ///
10118    /// A vector rather than a file for the same reason the tier's own tests use
10119    /// one: the file work has not attached a real store yet, and what this is
10120    /// checking is the policy above the store rather than the store.
10121    struct Mem {
10122        blobs: Vec<Vec<u8>>,
10123    }
10124
10125    impl yo_kv::cold::Blocks for Mem {
10126        fn put(&mut self, bytes: &[u8]) -> yo_common::Result<yo_common::Addr> {
10127            self.blobs.push(bytes.to_vec());
10128            Ok(yo_common::Addr::new(
10129                yo_common::Space::Log,
10130                (self.blobs.len() - 1) as u64,
10131            ))
10132        }
10133
10134        fn get(&self, at: yo_common::Addr) -> yo_common::Result<&[u8]> {
10135            self.blobs
10136                .get(at.offset() as usize)
10137                .map(Vec::as_slice)
10138                .ok_or_else(|| {
10139                    yo_common::Error::new(yo_common::Code::Corrupt, "no chunk at that address")
10140                })
10141        }
10142
10143        fn bytes(&self) -> u64 {
10144            self.blobs.iter().map(|b| b.len() as u64).sum()
10145        }
10146    }
10147
10148    /// A server holding several segments of strings, with somewhere to put them.
10149    ///
10150    /// Answers the fixture and what it was holding when it stopped filling.
10151    fn filled(attach: bool) -> (Fixture, usize) {
10152        let mut f = Fixture::new();
10153        if attach {
10154            f.server.db(0).attach(Box::new(Mem { blobs: Vec::new() }));
10155        }
10156        let val = vec![b'v'; 256];
10157        for i in 0..24000u32 {
10158            let k = format!("key:{i:08}");
10159            f.run(&[b"SET", k.as_bytes(), &val]);
10160        }
10161        let full = f.server.memory_bytes();
10162        assert!(full > 3 * 1024 * 1024, "the arena is several segments");
10163        (f, full)
10164    }
10165
10166    /// Write until the server is under `limit` or the writes run out.
10167    ///
10168    /// The same shape the eviction test uses. A memory limit is enforced in
10169    /// front of a command, so nothing happens until something is written, and
10170    /// the budget means one command does not do the whole job.
10171    fn press(f: &mut Fixture, limit: usize) {
10172        let val = vec![b'v'; 256];
10173        for i in 0..3000u32 {
10174            let k = format!("new:{i:08}");
10175            assert_eq!(
10176                f.run(&[b"SET", k.as_bytes(), &val]),
10177                "+OK\r\n",
10178                "write {i} was refused"
10179            );
10180            f.server.refresh_memory();
10181            if f.server.memory_bytes() <= limit {
10182                return;
10183            }
10184        }
10185        panic!(
10186            "it never got under: {} against {limit}",
10187            f.server.memory_bytes()
10188        );
10189    }
10190
10191    #[test]
10192    fn the_storage_limit_reads_back_and_minus_one_is_no_limit() {
10193        let mut f = Fixture::new();
10194        assert_eq!(
10195            f.run(&[b"CONFIG", b"GET", b"maxstore"]),
10196            "*2\r\n$8\r\nmaxstore\r\n$2\r\n-1\r\n",
10197            "no limit is the default"
10198        );
10199        // The same memory value parser `maxmemory` uses, and the same trap in
10200        // it, plus the one spelling that means no limit at all.
10201        for (typed, bytes) in [
10202            (&b"0"[..], "0"),
10203            (b"1024", "1024"),
10204            (b"1k", "1000"),
10205            (b"1gb", "1073741824"),
10206            (b"-1", "-1"),
10207        ] {
10208            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxstore", typed]), "+OK\r\n");
10209            assert_eq!(
10210                f.run(&[b"CONFIG", b"GET", b"maxstore"]),
10211                format!("*2\r\n$8\r\nmaxstore\r\n${}\r\n{bytes}\r\n", bytes.len()),
10212                "set {}",
10213                String::from_utf8_lossy(typed)
10214            );
10215        }
10216        for bad in [&b"1tb"[..], b"-2", b"", b"lots"] {
10217            assert_eq!(
10218                f.run(&[b"CONFIG", b"SET", b"maxstore", bad]),
10219                "-ERR CONFIG SET failed (possibly related to argument 'maxstore') - argument must be a memory value or -1\r\n",
10220                "refused {}",
10221                String::from_utf8_lossy(bad)
10222            );
10223        }
10224        // Nothing is attached, so the answer to a memory limit is still Redis's.
10225        let info = f.run(&[b"INFO", b"memory"]);
10226        assert!(info.contains("maxstore:-1"), "{info}");
10227        assert!(info.contains("yo_memory_regime:evict"), "{info}");
10228        assert!(info.contains("yo_store_bytes:0"), "{info}");
10229    }
10230
10231    #[test]
10232    fn a_memory_limit_moves_values_to_the_file_instead_of_dropping_keys() {
10233        // The inversion. The same pressure that makes a Redis server throw keys
10234        // away makes this one move values to the file, and afterwards every key
10235        // is still there and still answers with what was stored in it.
10236        let (mut f, full) = filled(true);
10237        let keys = f.run(&[b"DBSIZE"]);
10238        assert!(
10239            f.run(&[b"INFO", b"memory"])
10240                .contains("yo_memory_regime:migrate"),
10241            "a database with somewhere to put values migrates"
10242        );
10243
10244        let limit = full - 2 * 1024 * 1024;
10245        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
10246        f.run(&[
10247            b"CONFIG",
10248            b"SET",
10249            b"maxmemory",
10250            limit.to_string().as_bytes(),
10251        ]);
10252        press(&mut f, limit);
10253
10254        assert!(
10255            f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
10256            "nothing was thrown away"
10257        );
10258        let after: usize = f.run(&[b"DBSIZE"])[1..]
10259            .trim_end()
10260            .parse()
10261            .expect("a count");
10262        let before: usize = keys[1..].trim_end().parse().expect("a count");
10263        assert!(after > before, "the keys that came in are all still here");
10264        assert!(
10265            f.server.store_bytes() > 0,
10266            "and what came out of memory went to the file"
10267        );
10268        // And the values read back, which is the part that makes it a migration
10269        // rather than a loss.
10270        let val = format!("$256\r\n{}\r\n", "v".repeat(256));
10271        assert_eq!(f.run(&[b"GET", b"key:00000000"]), val);
10272        assert_eq!(f.run(&[b"GET", b"key:00023999"]), val);
10273    }
10274
10275    #[test]
10276    fn a_storage_limit_of_zero_restores_redis_behaviour_exactly() {
10277        // The documented setting for a drop in cache. A file that may hold
10278        // nothing cannot be migrated to, so eviction is all that is left, and
10279        // the server behaves exactly as it did before any of this existed.
10280        let (mut f, full) = filled(true);
10281        f.run(&[b"CONFIG", b"SET", b"maxstore", b"0"]);
10282        assert!(
10283            f.run(&[b"INFO", b"memory"])
10284                .contains("yo_memory_regime:evict"),
10285            "nothing may go to the file"
10286        );
10287
10288        let limit = full - 2 * 1024 * 1024;
10289        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
10290        f.run(&[
10291            b"CONFIG",
10292            b"SET",
10293            b"maxmemory",
10294            limit.to_string().as_bytes(),
10295        ]);
10296        press(&mut f, limit);
10297
10298        assert!(
10299            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
10300            "keys were thrown away, which is what was asked for"
10301        );
10302        assert_eq!(f.server.store_bytes(), 0, "and the file was never written");
10303    }
10304
10305    #[test]
10306    fn a_full_file_goes_back_to_evicting() {
10307        // A storage limit reached is a storage limit, and eviction is the right
10308        // answer to one. The budget here is a few kilobytes, so the first round
10309        // of migration fills it and everything after that is evicted.
10310        let (mut f, full) = filled(true);
10311        f.run(&[b"CONFIG", b"SET", b"maxstore", b"64kb"]);
10312        let limit = full - 2 * 1024 * 1024;
10313        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
10314        f.run(&[
10315            b"CONFIG",
10316            b"SET",
10317            b"maxmemory",
10318            limit.to_string().as_bytes(),
10319        ]);
10320        press(&mut f, limit);
10321
10322        assert!(f.server.store_bytes() >= 64 * 1024, "the file filled up");
10323        assert!(
10324            !f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"),
10325            "and then it started evicting"
10326        );
10327        assert!(
10328            f.run(&[b"INFO", b"memory"])
10329                .contains("yo_memory_regime:evict"),
10330            "and it says so"
10331        );
10332    }
10333}