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 blocking;
57mod cpu;
58mod hashes;
59mod keyspace;
60mod lists;
61mod scan;
62mod scripting;
63mod server;
64mod sets;
65mod strings;
66pub mod table;
67mod zsets;
68
69pub use args::Args;
70pub use blocking::{Parked, Waiters};
71pub use table::{COMMANDS, Spec, arity_ok, lookup};
72
73use crate::reply::Out;
74use yo_common::{Code, Error};
75use yo_kv::{Clock, Keyspace};
76
77/// How many databases a server has.
78///
79/// Redis's default is sixteen and its `databases` setting can change it. Ours
80/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
81/// constant. Nothing in the design needs the number to be fixed; nothing yet
82/// needs it not to be.
83pub const DATABASES: usize = 16;
84
85/// Every database's bit in [`Server::dirty`], which is what a fresh server
86/// starts on so that the first maintenance turn asks all of them.
87///
88/// A `u64` holds sixteen bits with room to spare, and the assertion below is
89/// what turns raising [`DATABASES`] past sixty four into a build failure rather
90/// than a shift that silently drops the databases past the end.
91const ALL_DATABASES: u64 = if DATABASES == 64 {
92    u64::MAX
93} else {
94    (1u64 << DATABASES) - 1
95};
96const _: () = assert!(DATABASES <= 64);
97
98/// How many keys one command throws away before it leaves the rest to the next.
99///
100/// A bound and not a loop to the end, because this runs in front of a client
101/// that is waiting for its reply, and a server a long way over its limit would
102/// otherwise hold that client for as long as it took to walk all the way back
103/// under. Sixty four is a batch's worth of commands, so a server that went over
104/// by what one batch allocated comes back under in one command, and a server
105/// whose limit was just cut in half works through it over the next few thousand
106/// rather than in one long stall. Redis bounds the same loop by a time slice
107/// instead of a count and hands the rest to a timer; there is no timer here, so
108/// the rest goes to the next command that runs.
109const EVICT_BUDGET: usize = 64;
110
111/// What a server says to a command that would allocate when it has no room.
112///
113/// Redis's `shared.oomerr`, word for word including the full stop, because
114/// clients match on the `OOM` prefix and people match on the sentence.
115const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
116
117/// What the connection should do after a command.
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum Flow {
120    /// Read the next command.
121    Continue,
122    /// Write what is buffered and then close, which is what `QUIT` asks for.
123    Close,
124    /// Nothing was written and nothing is owed yet.
125    ///
126    /// The client is on the waiter list and its reply comes when a key it named
127    /// has something in it or when its deadline passes, whichever happens first.
128    /// Until then the connection stops reading commands, because a client that
129    /// is waiting for an answer is not a client that has sent another question.
130    Block,
131}
132
133/// The numbers `INFO` reports that this layer cannot see for itself.
134///
135/// The reactor owns the sockets, so the reactor is what knows how many clients
136/// there are. It writes these directly and nothing here does anything with them
137/// except report them.
138#[derive(Debug, Clone, Copy, Default)]
139pub struct Stats {
140    /// Connections open right now.
141    pub clients: u64,
142    /// Connections accepted since the server started.
143    pub connections: u64,
144    /// Commands run since the server started, which this layer counts itself.
145    pub commands: u64,
146}
147
148/// Everything a server holds.
149///
150/// One of these per shard thread, not one per process: the databases inside are
151/// not `Sync` and are reached by sending their thread a command. What makes
152/// this a server rather than a shard is that it is the whole of what a
153/// connection can address.
154pub struct Server {
155    dbs: Vec<Keyspace>,
156    clock: Clock,
157    started_ms: u64,
158    /// Where the next maintenance turn starts looking, so that a database
159    /// under constant write load cannot hold the other fifteen's space.
160    next_db: usize,
161    /// One bit per database, set when a command ran against it.
162    ///
163    /// The maintenance turn after every batch used to ask all sixteen
164    /// databases whether they had anything to collect, and asking costs a load
165    /// and a store in each one. Fifteen of those are cold lines on a server
166    /// where every client is on database zero, which is every server, and the
167    /// answer is no every time. This is the cheap half of the question: a
168    /// database nobody has touched since it last said no cannot have started
169    /// saying yes.
170    dirty: u64,
171    /// What the connections are holding, kept by the engine.
172    conn_bytes: usize,
173    /// The `maxmemory` limit in bytes, zero when there is not one.
174    ///
175    /// Zero is the default and it is the whole reason the check in front of
176    /// every write is one comparison against a field that is already warm.
177    maxmemory: u64,
178    /// What [`Server::memory_bytes`] said at the last maintenance turn.
179    ///
180    /// The reading is a walk over every collection in every database and cannot
181    /// go on a command path, so the command path reads this instead and is at
182    /// most one batch behind. What that costs is overshoot: a server can end a
183    /// batch holding one batch's worth of allocation more than its limit before
184    /// anything notices. A batch is 64 commands, so that is bounded by what 64
185    /// commands can allocate and not by how long the server runs.
186    ///
187    /// Only kept up to date when there is a limit to judge it against. A server
188    /// with no `maxmemory` never reads it and never pays for it.
189    used: usize,
190    /// Which database the next eviction draws from.
191    ///
192    /// Its own cursor and not [`Server::next_db`], because eviction and
193    /// compaction move at different rates and sharing one would make the
194    /// database that gets compacted depend on how many keys were evicted.
195    evict_db: usize,
196    /// Clients parked on a blocking command.
197    waiters: Waiters,
198    /// The numbers the reactor keeps for `INFO`.
199    pub stats: Stats,
200}
201
202impl Server {
203    /// A server with [`DATABASES`] empty databases on the system clock.
204    #[must_use]
205    pub fn new() -> Server {
206        let clock = Clock::system();
207        Server {
208            dbs: (0..DATABASES)
209                .map(|_| Keyspace::with_clock(clock))
210                .collect(),
211            clock,
212            started_ms: clock.now_ms(),
213            next_db: 0,
214            dirty: ALL_DATABASES,
215            conn_bytes: 0,
216            maxmemory: 0,
217            used: 0,
218            evict_db: 0,
219            waiters: Waiters::default(),
220            stats: Stats::default(),
221        }
222    }
223
224    /// A server on a clock the caller moves by hand, for tests.
225    #[must_use]
226    pub fn with_clock(clock: Clock) -> Server {
227        Server {
228            dbs: (0..DATABASES)
229                .map(|_| Keyspace::with_clock(clock))
230                .collect(),
231            clock,
232            started_ms: clock.now_ms(),
233            next_db: 0,
234            dirty: ALL_DATABASES,
235            conn_bytes: 0,
236            maxmemory: 0,
237            used: 0,
238            evict_db: 0,
239            waiters: Waiters::default(),
240            stats: Stats::default(),
241        }
242    }
243
244    /// One database, by index.
245    ///
246    /// # Panics
247    ///
248    /// If `i` is not a database. `SELECT` is the only way a client changes the
249    /// index and it checks, so an index that is out of range here is a bug in
250    /// the caller and not something a client can ask for.
251    pub fn db(&mut self, i: usize) -> &mut Keyspace {
252        // The borrow is mutable, so assume it is used. Anything that only reads
253        // has [`Server::db_ref`] and does not come through here.
254        self.dirty |= 1u64 << i;
255        &mut self.dbs[i]
256    }
257
258    /// One database, by index, without taking it mutably.
259    ///
260    /// What the prefetch stage needs. It runs for all 64 commands in a batch
261    /// before any of them executes, so it cannot hold the mutable borrow `run`
262    /// is about to want, and it does not need one: warming a cache line reads
263    /// nothing and changes nothing.
264    ///
265    /// # Panics
266    ///
267    /// As [`Server::db`].
268    #[must_use]
269    pub fn db_ref(&self, i: usize) -> &Keyspace {
270        &self.dbs[i]
271    }
272
273    /// Take a new clock reading and give it to every database.
274    ///
275    /// Once per turn of the event loop, which is the only place time moves. A
276    /// command asking what the time is gets the answer the whole batch got, so
277    /// two keys written by the same batch expire together (`04` section 3).
278    pub fn refresh_clock(&mut self) {
279        self.clock.refresh();
280        let now = self.clock.now_ms();
281        for db in &mut self.dbs {
282            db.clock_mut().set(now);
283        }
284    }
285
286    /// Move every clock here to `ms` by hand, for tests about expiry.
287    ///
288    /// A test cannot wait a hundred seconds and a test that waits a hundred
289    /// milliseconds is a test that fails on a loaded machine, so time moves on
290    /// request. The system clock underneath will overwrite this on the next
291    /// [`Server::refresh_clock`], which is why this is only useful in a test
292    /// that drives commands directly rather than through the event loop.
293    pub fn set_clock_ms(&mut self, ms: u64) {
294        self.clock.set(ms);
295        for db in &mut self.dbs {
296            db.clock_mut().set(ms);
297        }
298    }
299
300    /// Seconds since this server was built.
301    #[must_use]
302    pub fn uptime_secs(&self) -> u64 {
303        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
304    }
305
306    /// Bytes held by every database's index and arena, plus the read and reply
307    /// buffers of every connection.
308    ///
309    /// The buffers are in here because they are real and because Redis counts
310    /// its own, so leaving them out would make the one number people compare
311    /// flattering rather than true. They are not a database, so nothing in the
312    /// keyspace can change them and the engine has to say when they move.
313    #[must_use]
314    pub fn memory_bytes(&self) -> usize {
315        self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
316    }
317
318    /// What the keyspace itself is holding, live records only.
319    ///
320    /// `used_memory` minus this is what the store costs to run: the index, the
321    /// space dead records are sitting in until compaction gets to them, and the
322    /// connections' buffers.
323    #[must_use]
324    pub fn dataset_bytes(&self) -> usize {
325        self.dbs
326            .iter()
327            .map(|db| db.map().arena().live_bytes() as usize)
328            .sum()
329    }
330
331    /// Bytes the arenas are holding, live and dead together.
332    #[must_use]
333    pub fn arena_bytes(&self) -> usize {
334        self.dbs
335            .iter()
336            .map(|db| db.map().arena().reserved_bytes() as usize)
337            .sum()
338    }
339
340    /// Bytes the indexes are holding.
341    #[must_use]
342    pub fn index_bytes(&self) -> usize {
343        self.dbs
344            .iter()
345            .map(|db| db.map().index().memory_bytes())
346            .sum()
347    }
348
349    /// Arena segments whose pages are real, across every database.
350    #[must_use]
351    pub fn segment_count(&self) -> usize {
352        self.dbs
353            .iter()
354            .map(|db| db.map().arena().resident_segments())
355            .sum()
356    }
357
358    /// What the connections' read and reply buffers are holding.
359    #[must_use]
360    pub const fn conn_bytes(&self) -> usize {
361        self.conn_bytes
362    }
363
364    /// Note that the connections are holding `delta` bytes more than they were,
365    /// or fewer when it is negative.
366    ///
367    /// A delta and not a total because the alternative is a walk over every
368    /// connection, and the walk would have to happen on a turn of the loop
369    /// rather than when `INFO` asks, which puts the cost of a report on the
370    /// command path of a server nobody is asking.
371    pub fn note_conn_bytes(&mut self, delta: isize) {
372        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
373    }
374
375    /// Keys reclaimed by running into them after their deadline.
376    #[must_use]
377    pub fn expired_keys(&self) -> u64 {
378        self.dbs.iter().map(Keyspace::expired_keys).sum()
379    }
380
381    /// Keys thrown away to make room, which is the other number entirely.
382    #[must_use]
383    pub fn evicted_keys(&self) -> u64 {
384        self.dbs.iter().map(Keyspace::evicted_keys).sum()
385    }
386
387    /// The `maxmemory` limit in bytes, zero when there is not one.
388    #[must_use]
389    pub const fn maxmemory(&self) -> u64 {
390        self.maxmemory
391    }
392
393    /// Set the limit, and take a reading straight away.
394    ///
395    /// The reading is here rather than left to the next maintenance turn because
396    /// a client that sets the limit and sends a write in the same batch expects
397    /// the write to be judged against the limit it just set, and because the
398    /// cached number is meaningless until the first time there is a limit to
399    /// compare it with.
400    ///
401    /// Turning the limit on also turns on the running total every slab keeps of
402    /// what its collections hold, and turning it off turns that back off, so a
403    /// server with no limit is not paying to count something nobody reads. The
404    /// first reading after switching it on is the walk that the total starts
405    /// from, and it is the only walk.
406    pub fn set_maxmemory(&mut self, bytes: u64) {
407        self.maxmemory = bytes;
408        for db in &mut self.dbs {
409            db.track_memory(bytes != 0);
410        }
411        self.used = self.settled_memory();
412    }
413
414    /// Take a fresh memory reading, which the maintenance turn does once a batch.
415    ///
416    /// Nothing at all when there is no limit, which is the default and is every
417    /// server that has not asked for one.
418    pub fn refresh_memory(&mut self) {
419        if self.maxmemory != 0 {
420            self.used = self.settled_memory();
421        }
422    }
423
424    /// [`Server::memory_bytes`], asked the cheap way.
425    ///
426    /// The same number. The difference is that this asks each database only
427    /// about the collections that could have moved since the last time, which is
428    /// what a batch touched rather than what the server holds, so it can be
429    /// asked once a batch and again on every command that is over the limit.
430    fn settled_memory(&mut self) -> usize {
431        self.dbs
432            .iter_mut()
433            .map(Keyspace::settled_memory_bytes)
434            .sum::<usize>()
435            + self.conn_bytes
436    }
437
438    /// Make room under the `maxmemory` limit, throwing keys away if that is what
439    /// it takes. Answers whether there is anything left it could throw away.
440    ///
441    /// Redis runs the same thing from `processCommand` before every command and
442    /// so does this: a client that writes has to be judged at the moment it
443    /// writes, not a batch later, or the limit is a suggestion.
444    ///
445    /// Three things happen in the loop and all three are needed. Eviction picks
446    /// a key and drops it. Compaction gives the pages back, because dropping a
447    /// key marks its record dead and returns nothing on its own, so a loop that
448    /// only evicted would throw the whole keyspace away and watch the number
449    /// stay where it was. The reading is taken again each time round, because
450    /// the two of them together are the only thing that moves it.
451    ///
452    /// # Why running out of budget is not a no
453    ///
454    /// `false` means there was nothing left to evict, which is `noeviction`, or
455    /// a `volatile` policy on a database where nothing has a deadline, or a
456    /// keyspace that is already empty. It does not mean the server is still over
457    /// its limit, and that difference is Redis's: `performEvictions` answers
458    /// `EVICT_FAIL` only when it has run out of things to delete, and
459    /// `processCommand` refuses the client on that and on nothing else. Running
460    /// out of time part way through a job it is doing well comes back as
461    /// `EVICT_RUNNING` and the command goes through, because a server that is
462    /// evicting steadily and refusing every write while it does it is worse for
463    /// the client than a little overshoot.
464    ///
465    /// # What the limit is worth
466    ///
467    /// Space comes back a segment at a time and a segment is two megabytes, so
468    /// this holds a server to its limit give or take a segment. A `maxmemory` of
469    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
470    /// megabytes is asking for a precision this store does not have.
471    pub fn make_room(&mut self) -> bool {
472        if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
473            return true;
474        }
475        // The cached reading is a batch old and the batch may have compacted
476        // since, so take a fresh one before throwing anything away. It is the
477        // settled reading and not the walk, so what this costs is the handful of
478        // collections the last batch touched and not the whole database.
479        self.used = self.settled_memory();
480        let mut budget = EVICT_BUDGET;
481        while self.used as u64 > self.maxmemory {
482            if !self.evict_step() {
483                return false;
484            }
485            self.compact_hard_step();
486            self.used = self.settled_memory();
487            budget -= 1;
488            if budget == 0 {
489                break;
490            }
491        }
492        true
493    }
494
495    /// Throw one key away, from whichever database has one to give.
496    ///
497    /// Round robin from a cursor rather than always starting at database zero,
498    /// so a server using more than one of them does not empty the first before
499    /// touching the second. Almost every server is on database zero only, where
500    /// this is one call that answers and fifteen that say the map is empty.
501    fn evict_step(&mut self) -> bool {
502        for turn in 0..self.dbs.len() {
503            let i = (self.evict_db + turn) % self.dbs.len();
504            if self.dbs[i].evict_one() {
505                self.evict_db = (i + 1) % self.dbs.len();
506                self.dirty |= 1u64 << i;
507                return true;
508            }
509        }
510        false
511    }
512
513    /// One slice of compaction for a server that is over its limit.
514    ///
515    /// Takes the databases in the same order [`Server::compact_step`] does and
516    /// stops at the first one that had something to move, and it asks with the
517    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
518    fn compact_hard_step(&mut self) -> Option<usize> {
519        for turn in 0..self.dbs.len() {
520            let i = (self.next_db + turn) % self.dbs.len();
521            if let Some(moved) = self.dbs[i].compact_hard() {
522                self.next_db = (i + 1) % self.dbs.len();
523                return Some(moved);
524            }
525        }
526        None
527    }
528
529    /// Give one database's dead space back, if any database has enough of it to
530    /// be worth the move. `None` when no database had a candidate.
531    ///
532    /// Once per batch, next to the clock. Overwriting a key writes a new record
533    /// and counts the old one dead, so without this a server holds everything
534    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
535    /// a key against Redis at 144 for the same load, and the whole difference
536    /// was dead records nothing ever came back for.
537    ///
538    /// At most one segment moves per call and the search starts one database
539    /// further along each time, so the cost of asking is a comparison per
540    /// database and the cost of acting is bounded by a segment.
541    pub fn compact_step(&mut self) -> Option<usize> {
542        for turn in 0..self.dbs.len() {
543            let i = (self.next_db + turn) % self.dbs.len();
544            // Nothing has run against this database since it last said it had
545            // nothing to collect, so it still has nothing to collect and the
546            // line it lives on stays where it is.
547            if self.dirty & (1 << i) == 0 {
548                continue;
549            }
550            if let Some(moved) = self.dbs[i].compact_step() {
551                self.next_db = (i + 1) % self.dbs.len();
552                return Some(moved);
553            }
554            self.dirty &= !(1u64 << i);
555        }
556        None
557    }
558}
559
560impl Default for Server {
561    fn default() -> Server {
562        Server::new()
563    }
564}
565
566/// What one connection has chosen.
567pub struct Session {
568    db: usize,
569    id: u64,
570    name: Vec<u8>,
571}
572
573impl Session {
574    /// A new connection, on database zero with no name.
575    #[must_use]
576    pub fn new(id: u64) -> Session {
577        Session {
578            db: 0,
579            id,
580            name: Vec::new(),
581        }
582    }
583
584    /// The connection id, which `HELLO` reports and `CLIENT` will.
585    #[must_use]
586    pub const fn id(&self) -> u64 {
587        self.id
588    }
589
590    /// Which database this connection is working in.
591    #[must_use]
592    pub const fn db(&self) -> usize {
593        self.db
594    }
595
596    /// The name the client gave itself, empty if it gave none.
597    #[must_use]
598    pub fn name(&self) -> &[u8] {
599        &self.name
600    }
601
602    /// Put everything back the way it was when the connection was opened.
603    ///
604    /// The protocol is not here because it is not here: it lives in the reply
605    /// buffer, and `RESET` sets it back there.
606    pub fn reset(&mut self) {
607        self.db = 0;
608        self.name.clear();
609    }
610
611    /// Record the name from `HELLO ... SETNAME`.
612    fn set_name(&mut self, name: &[u8]) {
613        yo_alloc::allow(|| {
614            self.name.clear();
615            self.name.extend_from_slice(name);
616        });
617    }
618}
619
620/// Run one command and write its reply.
621///
622/// The name is looked up and the arity is checked here, once, so that no body
623/// has to. Everything after that is the command's own.
624pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
625    // The decoder never produces a command with no name. If one ever arrives,
626    // it is not something to answer.
627    if args.is_empty() {
628        return Flow::Continue;
629    }
630    server.stats.commands += 1;
631
632    let Some(spec) = lookup(args.name()) else {
633        write_error(out, &args::unknown_command(args));
634        return Flow::Continue;
635    };
636    if !arity_ok(spec, args.len()) {
637        write_error(out, &args::wrong_arity(spec.name));
638        return Flow::Continue;
639    }
640
641    // The limit first, so a server with no `maxmemory`, which is the default and
642    // is nearly all of them, pays one comparison against a field that is already
643    // warm. Every command and not only the writes, because that is where Redis
644    // puts it: making room is the server's job whatever the client asked for,
645    // and the flag only decides who gets told no when there is no room to make.
646    //
647    // The flag is Redis's own `denyoom` and the list of commands carrying it is
648    // Redis's list, so a command that only frees is let through with nothing
649    // left, which is what lets a client dig itself out with `DEL`.
650    if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
651        out.error_line(b"OOM ", OOM);
652        return Flow::Continue;
653    }
654
655    // Which databases the maintenance turn after this batch has to ask. Marked
656    // for every command and not only for the writes, because a read can make
657    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
658    // record it dropped is exactly the kind of thing the collector is for.
659    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
660    // two groups that hold them mark all of them rather than the session's.
661    server.dirty |= match spec.group {
662        "string" | "set" | "hash" | "list" | "zset" | "array" => 1u64 << session.db,
663        _ => ALL_DATABASES,
664    };
665
666    let mark = out.len();
667    // Before the group, because the five that block are list commands and would
668    // otherwise land in `lists`, which is handed one database and nothing that
669    // could park a client. The flag is the right thing to branch on rather than
670    // a list of names: it is what `COMMAND INFO` reports about exactly these
671    // commands, and the sorted set and stream ones that arrive later carry it
672    // too.
673    let done = if spec.flags.contains(&"blocking") {
674        blocking::execute(server, session, spec, args, out)
675    } else {
676        match spec.group {
677            "string" => {
678                let db = session.db;
679                strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
680            }
681            "set" => {
682                let db = session.db;
683                sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
684            }
685            "hash" => {
686                let db = session.db;
687                hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
688            }
689            "list" => {
690                let db = session.db;
691                lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
692            }
693            "zset" => {
694                let db = session.db;
695                zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
696            }
697            "array" => {
698                let db = session.db;
699                arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
700            }
701            // Every database and not the one the session is on, because `COPY` takes
702            // a `DB n` and writes into a database nobody selected.
703            "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
704                .map(|()| Flow::Continue),
705            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
706            _ => server::execute(server, session, spec, args, out),
707        }
708    };
709    match done {
710        Ok(flow) => flow,
711        Err(e) => {
712            out.truncate(mark);
713            write_error(out, &e);
714            Flow::Continue
715        }
716    }
717}
718
719/// The error line for an error value.
720///
721/// The prefix is what a client branches on, and there are only two of them in
722/// this milestone: `WRONGTYPE` for a command sent at the wrong kind of value,
723/// and `ERR` for everything else. The three errors that need a different one,
724/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
725/// than routed through here. `OOM` is not a [`Code`] of its own because
726/// [`Code::Full`] already covers the string that is too long for
727/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
728fn write_error(out: &mut Out, e: &Error) {
729    let prefix: &[u8] = match e.code() {
730        Code::WrongType => b"WRONGTYPE ",
731        _ => b"ERR ",
732    };
733    out.error_line(prefix, e.message().as_bytes());
734}
735
736#[cfg(test)]
737mod tests {
738    use super::*;
739    use crate::proto::{Limits, Proto};
740    use crate::request::Argv;
741
742    /// Build the wire bytes for a command.
743    ///
744    /// Tests go through the codec rather than around it, so an argument in a
745    /// test is the same borrowed slice a connection produces.
746    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
747        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
748        for p in parts {
749            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
750            wire.extend_from_slice(p);
751            wire.extend_from_slice(b"\r\n");
752        }
753        wire
754    }
755
756    /// A server, a connection and a buffer, driven the way the reactor will.
757    struct Fixture {
758        server: Server,
759        session: Session,
760        argv: Argv,
761        out: Out,
762    }
763
764    impl Fixture {
765        fn new() -> Fixture {
766            Fixture {
767                server: Server::new(),
768                session: Session::new(7),
769                argv: Argv::new(),
770                out: Out::new(Proto::Resp2),
771            }
772        }
773
774        /// Run one command and answer with the bytes it wrote.
775        fn run(&mut self, parts: &[&[u8]]) -> String {
776            self.flow(parts).1
777        }
778
779        /// The same, with what the connection should do next.
780        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
781            let wire = encode(parts);
782            self.argv.decode(&wire, &Limits::default()).unwrap();
783            self.out.clear();
784            let flow = execute(
785                &mut self.server,
786                &mut self.session,
787                Args::new(&self.argv, &wire),
788                &mut self.out,
789            );
790            (
791                flow,
792                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
793            )
794        }
795    }
796
797    /// What a client does all day: write the same keys again and again. Every
798    /// one of those writes leaves the previous record behind, so a server that
799    /// never compacts holds every version of every key it has ever been sent.
800    #[test]
801    fn rewriting_the_same_keys_does_not_grow_the_server() {
802        let mut f = Fixture::new();
803        let val = vec![b'v'; 1024];
804        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
805
806        for k in &keys {
807            f.run(&[b"SET", k, &val]);
808        }
809        f.server.compact_step();
810        let after_first = f.server.memory_bytes();
811
812        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
813        // of it. Thirty two megabytes written to hold sixty four kilobytes,
814        // which is the shape of a real workload and is enough churn to fill
815        // sixteen segments if nothing ever comes back.
816        for _ in 0..500 {
817            for k in &keys {
818                f.run(&[b"SET", k, &val]);
819            }
820            f.server.compact_step();
821        }
822
823        assert!(
824            f.server.memory_bytes() <= after_first * 2,
825            "held {} after five hundred passes against {after_first} after one",
826            f.server.memory_bytes()
827        );
828        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
829        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
830    }
831
832    /// The same churn on a database nobody starts on, either side of a quiet
833    /// spell long enough for the maintenance turn to stop asking about it.
834    ///
835    /// The turn after each batch skips a database that has already said it has
836    /// nothing to collect and has not been touched since, which is what keeps a
837    /// server whose clients are all on database zero from loading and storing
838    /// in the other fifteen every batch to be told no. Two things could go
839    /// wrong with that. A database might never be marked at all, so this uses
840    /// database nine, which nothing marks by accident. And a database whose
841    /// mark was cleared might never get it back, so this drains the collector
842    /// until it says there is nothing left, checks the mark really is gone, and
843    /// then writes another thirty two megabytes through the same sixty four
844    /// keys. If either went wrong the server would hold all of it.
845    #[test]
846    fn a_database_nobody_started_on_is_still_collected() {
847        let mut f = Fixture::new();
848        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
849        let val = vec![b'v'; 1024];
850        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
851
852        for k in &keys {
853            f.run(&[b"SET", k, &val]);
854        }
855        while f.server.compact_step().is_some() {}
856        assert_eq!(
857            f.server.dirty & (1 << 9),
858            0,
859            "database nine was drained and should not be asked again until it is written to"
860        );
861        let after_first = f.server.memory_bytes();
862
863        for _ in 0..500 {
864            for k in &keys {
865                f.run(&[b"SET", k, &val]);
866            }
867            f.server.compact_step();
868        }
869
870        assert!(
871            f.server.memory_bytes() <= after_first * 2,
872            "held {} after five hundred passes against {after_first} after one",
873            f.server.memory_bytes()
874        );
875        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
876        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
877        // And nothing landed anywhere else on the way.
878        f.run(&[b"SELECT", b"0"]);
879        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
880    }
881
882    #[test]
883    fn a_command_goes_from_bytes_to_bytes() {
884        let mut f = Fixture::new();
885        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
886        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
887        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
888        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
889        // The name is matched whatever case it came in, and so are the options.
890        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
891        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
892    }
893
894    #[test]
895    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
896        let mut f = Fixture::new();
897        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
898        // A key named twice exists twice and can only be deleted once, and both
899        // of those are Redis's answers rather than tidier ones.
900        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
901        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
902        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
903        // UNLINK is the same body and reports the same way.
904        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
905        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
906    }
907
908    #[test]
909    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
910        let mut f = Fixture::new();
911        f.run(&[b"SET", b"k", b"v"]);
912        // A simple string on both protocols, which is unusual: most replies
913        // that carry a word are bulk strings.
914        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
915        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
916    }
917
918    #[test]
919    fn touch_counts_the_way_exists_counts() {
920        let mut f = Fixture::new();
921        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
922        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
923        assert_eq!(
924            f.run(&[b"TOUCH", b"a", b"a"]),
925            ":2\r\n",
926            "twice counts twice"
927        );
928        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
929        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
930    }
931
932    #[test]
933    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
934        let mut f = Fixture::new();
935        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
936        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
937
938        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
939        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
940        assert_eq!(
941            f.run(&[b"TTL", b"b"]),
942            ":100\r\n",
943            "the source's and not b's"
944        );
945        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
946    }
947
948    #[test]
949    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
950        let mut f = Fixture::new();
951        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
952        // The source is checked before the destination, so this is the error
953        // and not the zero RENAMENX would otherwise answer for a taken name.
954        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
955    }
956
957    #[test]
958    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
959        let mut f = Fixture::new();
960        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
961
962        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
963        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
964        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
965        // one call the two disagree about and neither does any work for.
966        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
967        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
968        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
969        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
970    }
971
972    #[test]
973    fn renaming_a_set_does_not_touch_a_member() {
974        let mut f = Fixture::new();
975        for i in 0..300 {
976            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
977        }
978        let before = f.server.memory_bytes();
979
980        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
981        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
982        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
983        assert!(
984            f.server.memory_bytes().abs_diff(before) < 256,
985            "the members were copied: {} against {before}",
986            f.server.memory_bytes()
987        );
988    }
989
990    #[test]
991    fn a_copy_is_a_second_value_and_not_a_second_name() {
992        let mut f = Fixture::new();
993        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
994
995        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
996        f.run(&[b"SADD", b"t", b"m3"]);
997        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
998        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
999    }
1000
1001    /// Every type a key can hold, copied, because two of them used to panic.
1002    ///
1003    /// `COPY` reads the value out of the source through one match on the type
1004    /// tag, and that match had a catch all at the bottom from back when a set
1005    /// and a hash were the only bodies. The list and the sorted set landed after
1006    /// it and nobody came back, so `COPY mylist other` took the shard down. It
1007    /// is an ordinary command against a type the server supports everywhere
1008    /// else, so this walks all five rather than the two that were broken: the
1009    /// point is that the next type cannot land the same way.
1010    #[test]
1011    fn every_type_can_be_copied() {
1012        let mut f = Fixture::new();
1013        f.run(&[b"SET", b"str", b"v1"]);
1014        f.run(&[b"SADD", b"set", b"m1"]);
1015        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1016        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1017        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1018
1019        for name in [
1020            &b"str"[..],
1021            &b"set"[..],
1022            &b"hash"[..],
1023            &b"list"[..],
1024            &b"zset"[..],
1025        ] {
1026            let dst = [name, b":copy"].concat();
1027            assert_eq!(
1028                f.run(&[b"COPY", name, &dst]),
1029                ":1\r\n",
1030                "copying {}",
1031                String::from_utf8_lossy(name)
1032            );
1033            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1034        }
1035
1036        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1037            let mut want = String::from("*2\r\n");
1038            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1039            want
1040        });
1041        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1042
1043        // And the copy is its own value, not a second name for the source.
1044        f.run(&[b"RPUSH", b"list:copy", b"c"]);
1045        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1046        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1047    }
1048
1049    #[test]
1050    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1051        let mut f = Fixture::new();
1052        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1053        f.run(&[b"SET", b"b", b"v2"]);
1054
1055        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1056        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1057        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1058        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1059        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1060        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1061    }
1062
1063    #[test]
1064    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1065        let mut f = Fixture::new();
1066        f.run(&[b"SET", b"a", b"v1"]);
1067
1068        // Same key, different database, so this is not the same object and is
1069        // an ordinary copy. Same key in the same database is the error below.
1070        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1071        f.run(&[b"SELECT", b"1"]);
1072        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1073        assert_eq!(
1074            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1075            ":0\r\n",
1076            "taken"
1077        );
1078        assert_eq!(
1079            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1080            ":1\r\n"
1081        );
1082    }
1083
1084    #[test]
1085    fn copy_checks_its_options_before_it_looks_for_anything() {
1086        let mut f = Fixture::new();
1087        // No key exists at all, and every one of these is still the option
1088        // complaint rather than a zero, which is the order a real server uses.
1089        assert_eq!(
1090            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
1091            "-ERR DB index is out of range\r\n"
1092        );
1093        assert_eq!(
1094            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
1095            "-ERR DB index is out of range\r\n"
1096        );
1097        assert_eq!(
1098            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
1099            "-ERR value is not an integer or out of range\r\n"
1100        );
1101        assert_eq!(
1102            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
1103            "-ERR syntax error\r\n"
1104        );
1105        assert_eq!(
1106            f.run(&[b"COPY", b"a", b"a"]),
1107            "-ERR source and destination objects are the same\r\n"
1108        );
1109        // Repeated, reordered and lowercased, and the last DB wins.
1110        assert_eq!(
1111            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
1112            ":0\r\n"
1113        );
1114    }
1115
1116    #[test]
1117    fn time_is_two_bulk_strings_and_moves() {
1118        let mut f = Fixture::new();
1119        let first = f.run(&[b"TIME"]);
1120        assert!(first.starts_with("*2\r\n$"), "got {first}");
1121        let parts: Vec<&str> = first.split("\r\n").collect();
1122        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
1123        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
1124        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
1125        assert!((0..1_000_000).contains(&micros), "got {micros}");
1126        // The coarse clock the keyspace uses is a cached millisecond that a
1127        // background tick refreshes, so a TIME built on it would answer the
1128        // same microsecond twice in a row here.
1129        assert_ne!(first, f.run(&[b"TIME"]));
1130    }
1131
1132    #[test]
1133    fn a_keyspace_scan_walks_every_key_once() {
1134        let mut f = Fixture::new();
1135        for i in 0..500 {
1136            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
1137        }
1138
1139        let mut seen: Vec<String> = Vec::new();
1140        let mut cursor = "0".to_owned();
1141        let mut calls = 0;
1142        loop {
1143            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
1144            seen.extend(keys);
1145            cursor = next;
1146            calls += 1;
1147            assert!(calls < 10_000, "the cursor is not advancing");
1148            if cursor == "0" {
1149                break;
1150            }
1151        }
1152
1153        seen.sort();
1154        seen.dedup();
1155        assert_eq!(seen.len(), 500, "every key once and only once");
1156        // And more than one call to get them, or the COUNT is being ignored and
1157        // the loop above proved nothing about resuming.
1158        assert!(calls > 1, "500 keys came back in one batch");
1159    }
1160
1161    #[test]
1162    fn a_scan_narrows_by_pattern_and_by_type() {
1163        let mut f = Fixture::new();
1164        f.run(&[b"SET", b"str", b"v"]);
1165        f.run(&[b"SADD", b"members", b"a"]);
1166        f.run(&[b"HSET", b"fields", b"f", b"v"]);
1167
1168        let all = |f: &mut Fixture, args: &[&[u8]]| {
1169            let mut out: Vec<String> = Vec::new();
1170            let mut cursor = "0".to_owned();
1171            loop {
1172                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
1173                line.extend_from_slice(args);
1174                let (next, keys) = scan_reply(&f.run(&line));
1175                out.extend(keys);
1176                cursor = next;
1177                if cursor == "0" {
1178                    break;
1179                }
1180            }
1181            out.sort();
1182            out
1183        };
1184
1185        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
1186        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
1187        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
1188        // Case insensitive, the same as Redis's own comparison.
1189        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
1190        // A type nothing can hold is not an error, it just matches nothing.
1191        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
1192        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
1193        // Both filters at once, and they are an and rather than an or.
1194        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
1195    }
1196
1197    #[test]
1198    fn a_scan_says_what_is_wrong_with_it() {
1199        let mut f = Fixture::new();
1200        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
1201        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
1202        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
1203        assert_eq!(
1204            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
1205            "-ERR syntax error\r\n"
1206        );
1207        assert_eq!(
1208            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
1209            "-ERR value is not an integer or out of range\r\n"
1210        );
1211        assert_eq!(
1212            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
1213            "-ERR syntax error\r\n"
1214        );
1215        // A cursor the client made up is a cursor. It resumes somewhere
1216        // arbitrary and answers whatever is there, which is what Redis does and
1217        // is the only behaviour that does not need the server to remember every
1218        // cursor it has handed out.
1219        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
1220    }
1221
1222    #[test]
1223    fn keys_and_randomkey_look_at_the_whole_database() {
1224        let mut f = Fixture::new();
1225        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
1226        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
1227
1228        for name in ["one", "two", "three"] {
1229            f.run(&[b"SET", name.as_bytes(), b"v"]);
1230        }
1231        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
1232        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
1233        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
1234
1235        for _ in 0..50 {
1236            let got = f.run(&[b"RANDOMKEY"]);
1237            assert!(
1238                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
1239                "got {got}"
1240            );
1241        }
1242    }
1243
1244    #[test]
1245    fn a_walk_does_not_answer_keys_that_have_expired() {
1246        let mut f = Fixture::new();
1247        f.run(&[b"SET", b"alive", b"v"]);
1248        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
1249        f.server.db(0).clock_mut().advance(2);
1250        assert_eq!(
1251            f.run(&[b"DBSIZE"]),
1252            ":2\r\n",
1253            "nothing has collected it yet"
1254        );
1255
1256        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
1257        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
1258        assert_eq!(keys, ["alive"]);
1259        for _ in 0..20 {
1260            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
1261        }
1262        // The walk collected it on the way past, which is what makes DBSIZE
1263        // here answer what Redis answers once its own cycle has been round.
1264        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1265    }
1266
1267    #[test]
1268    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
1269        let mut f = Fixture::new();
1270        f.run(&[b"SET", b"k", b"v"]);
1271        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
1272        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
1273
1274        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
1275        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1276        let ms = int(&f.run(&[b"PTTL", b"k"]));
1277        assert!((99_000..=100_000).contains(&ms), "got {ms}");
1278
1279        // The absolute pair, derived from the same one number the store kept.
1280        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
1281        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1282        assert_eq!(at, (at_ms + 500) / 1000);
1283        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
1284
1285        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
1286        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
1287        assert_eq!(
1288            f.run(&[b"PERSIST", b"k"]),
1289            ":0\r\n",
1290            "nothing to take off the second time"
1291        );
1292        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
1293        assert_eq!(
1294            f.run(&[b"GET", b"k"]),
1295            "$1\r\nv\r\n",
1296            "and the value went through all of that untouched"
1297        );
1298    }
1299
1300    #[test]
1301    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
1302        let mut f = Fixture::new();
1303        f.run(&[b"SET", b"str", b"v"]);
1304        f.run(&[b"SADD", b"set", b"a", b"b"]);
1305        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1306
1307        for key in [b"str".as_slice(), b"set", b"hash"] {
1308            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
1309            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
1310        }
1311        // The body is not touched by any of that, which is the whole reason the
1312        // deadline lives in the record and the body lives somewhere else.
1313        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
1314        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
1315        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
1316    }
1317
1318    #[test]
1319    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
1320        let mut f = Fixture::new();
1321        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
1322            f.run(&[b"SET", key, b"v"]);
1323        }
1324        // Four ways of naming a moment that has passed, and all four are a
1325        // delete answering 1 rather than an error. Zero is a moment, minus one
1326        // is a moment, and the hash field commands refuse the negative one.
1327        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
1328        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
1329        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
1330        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
1331        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1332        assert_eq!(
1333            f.run(&[b"EXPIRE", b"a", b"100"]),
1334            ":0\r\n",
1335            "and the key really went, so there is nothing to put a deadline on"
1336        );
1337    }
1338
1339    #[test]
1340    fn the_four_conditions_decide_whether_the_deadline_moves() {
1341        let mut f = Fixture::new();
1342        f.run(&[b"SET", b"k", b"v"]);
1343
1344        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
1345        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
1346        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
1347        assert_eq!(
1348            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
1349            ":1\r\n",
1350            "no deadline reads as infinitely far away, so LT passes where GT fails"
1351        );
1352
1353        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
1354        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
1355        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1356        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
1357        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
1358        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1359
1360        // The condition is answered before the past check, so this is a 0 and
1361        // the key survives. The other order would delete it.
1362        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
1363        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
1364        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
1365        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
1366    }
1367
1368    #[test]
1369    fn the_conditions_are_a_set_and_not_a_keyword() {
1370        let mut f = Fixture::new();
1371        f.run(&[b"SET", b"k", b"v"]);
1372
1373        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
1374        assert_eq!(
1375            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
1376            ":0\r\n",
1377            "the same keyword twice means it once, and NX now has a deadline to fail on"
1378        );
1379
1380        // XX with LT is the one pair that is not either of them on its own: LT
1381        // alone would accept a key with no deadline and this does not.
1382        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
1383        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1384        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
1385        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
1386        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1387        f.run(&[b"PERSIST", b"k"]);
1388        assert_eq!(
1389            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
1390            ":0\r\n",
1391            "where LT on its own would have taken it"
1392        );
1393        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
1394    }
1395
1396    #[test]
1397    fn a_key_is_gone_once_its_moment_passes() {
1398        let mut f = Fixture::new();
1399        f.run(&[b"SET", b"k", b"v"]);
1400        f.run(&[b"EXPIRE", b"k", b"100"]);
1401
1402        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1403        f.server.set_clock_ms(at as u64 + 1);
1404        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1405        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
1406        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
1407        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1408    }
1409
1410    #[test]
1411    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
1412        let mut f = Fixture::new();
1413        f.run(&[b"SET", b"k", b"v"]);
1414        for (bad, want) in [
1415            (
1416                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
1417                "-ERR value is not an integer or out of range\r\n",
1418            ),
1419            (
1420                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
1421                "-ERR Unsupported option MAYBE\r\n",
1422            ),
1423            (
1424                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
1425                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1426            ),
1427            (
1428                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
1429                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1430            ),
1431            (
1432                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
1433                "-ERR GT and LT options at the same time are not compatible\r\n",
1434            ),
1435            // Seconds that overflow when multiplied into milliseconds. Every
1436            // message names the command it came from.
1437            (
1438                &[b"EXPIRE", b"k", b"9223372036854775807"],
1439                "-ERR invalid expire time in 'expire' command\r\n",
1440            ),
1441            (
1442                &[b"EXPIREAT", b"k", b"9223372036854775807"],
1443                "-ERR invalid expire time in 'expireat' command\r\n",
1444            ),
1445            (
1446                &[b"PEXPIRE", b"k", b"9223372036854775807"],
1447                "-ERR invalid expire time in 'pexpire' command\r\n",
1448            ),
1449        ] {
1450            assert_eq!(f.run(bad), want, "for {bad:?}");
1451        }
1452        assert_eq!(
1453            f.run(&[b"TTL", b"k"]),
1454            ":-1\r\n",
1455            "and none of those put a deadline on anything"
1456        );
1457
1458        // The one of the four that has no arithmetic to overflow. Redis takes
1459        // it and holds the number as given, and a record here holds forty six
1460        // bits, so it lands in the year 4199 instead. D-17.
1461        assert_eq!(
1462            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
1463            ":1\r\n"
1464        );
1465        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
1466    }
1467
1468    #[test]
1469    fn flushing_empties_this_database_or_every_one_of_them() {
1470        let mut f = Fixture::new();
1471        f.run(&[b"SELECT", b"0"]);
1472        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1473        f.run(&[b"SELECT", b"1"]);
1474        f.run(&[b"SET", b"c", b"3"]);
1475        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1476        // ASYNC and SYNC are both taken and neither changes anything, since the
1477        // keyspace is empty before the OK goes out either way.
1478        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
1479        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1480        // Only database one was emptied.
1481        f.run(&[b"SELECT", b"0"]);
1482        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
1483        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
1484        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1485        f.run(&[b"SELECT", b"1"]);
1486        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1487        // Anything else after the name is a syntax error, and so is a third
1488        // argument even when the second one is a word we take.
1489        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
1490        assert_eq!(
1491            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
1492            "-ERR syntax error\r\n"
1493        );
1494    }
1495
1496    #[test]
1497    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
1498        let mut f = Fixture::new();
1499        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
1500        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
1501        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
1502        // Nothing is cached, so nothing is there, one answer per hash asked
1503        // about.
1504        assert_eq!(
1505            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
1506            "*2\r\n:0\r\n:0\r\n"
1507        );
1508        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
1509        assert_eq!(
1510            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
1511            "*0\r\n"
1512        );
1513        assert_eq!(
1514            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
1515            "-ERR Library not found\r\n"
1516        );
1517
1518        // Redis's two messages here are its own, one per container, and one of
1519        // them reads like a typo.
1520        assert_eq!(
1521            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
1522            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
1523        );
1524        assert_eq!(
1525            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
1526            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
1527        );
1528        // A second argument after the mode is the generic one instead, because
1529        // the count is checked before the word is looked at.
1530        assert_eq!(
1531            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
1532            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
1533        );
1534        assert_eq!(
1535            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
1536            "-ERR Unknown argument bogus\r\n"
1537        );
1538        assert_eq!(
1539            f.run(&[b"SCRIPT", b"EXISTS"]),
1540            "-ERR wrong number of arguments for 'script|exists' command\r\n"
1541        );
1542
1543        // The ones that need an interpreter are not here, and say so rather
1544        // than answering OK to a load that loaded nothing.
1545        assert_eq!(
1546            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
1547            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
1548        );
1549        assert_eq!(
1550            f.run(&[b"FUNCTION", b"STATS"]),
1551            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
1552        );
1553    }
1554
1555    #[test]
1556    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
1557        let mut f = Fixture::new();
1558        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
1559        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
1560        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
1561        // Read back as a string it is still an integer, written out as digits
1562        // only because somebody asked for them.
1563        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
1564        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
1565        // A counter that is not a number is the error the store raises and this
1566        // layer only spells, which is the whole point of the split.
1567        f.run(&[b"SET", b"k", b"hello"]);
1568        assert_eq!(
1569            f.run(&[b"INCR", b"k"]),
1570            "-ERR value is not an integer or out of range\r\n"
1571        );
1572        assert_eq!(
1573            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
1574            "-ERR increment would produce NaN or Infinity\r\n"
1575        );
1576    }
1577
1578    /// Every one of these was read off a running 8.8. They are the answers a
1579    /// client library's own test suite checks, and the shapes are not
1580    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
1581    /// integer, `INCREX` is a pair.
1582    #[test]
1583    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
1584        let mut f = Fixture::new();
1585        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
1586        // The same digest a real 8.8 answers for the same five bytes, which is
1587        // what makes `IFDEQ` usable against a mixed deployment.
1588        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
1589        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
1590        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
1591        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
1592        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
1593        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
1594        assert_eq!(
1595            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
1596            "*2\r\n:1\r\n:0\r\n",
1597            "a refused increment reports the value it left alone and applied nothing"
1598        );
1599        assert_eq!(
1600            f.run(&[
1601                b"INCREX",
1602                b"n",
1603                b"BYINT",
1604                b"5",
1605                b"UBOUND",
1606                b"3",
1607                b"SATURATE"
1608            ]),
1609            "*2\r\n:3\r\n:2\r\n"
1610        );
1611        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
1612        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
1613    }
1614
1615    #[test]
1616    fn the_same_answers_come_out_in_resp3_spelling() {
1617        let mut f = Fixture::new();
1618        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
1619        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
1620        // A float counter is a double on RESP3 and the digits in a bulk string
1621        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
1622        assert_eq!(
1623            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
1624            "*2\r\n,1.5\r\n,1.5\r\n"
1625        );
1626        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
1627        // `RESET` puts the protocol back, which is the part that is easy to
1628        // miss and leaves a pooled connection speaking the wrong one.
1629        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
1630        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
1631    }
1632
1633    #[test]
1634    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
1635        let mut f = Fixture::new();
1636        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
1637        assert_eq!(flow, Flow::Continue);
1638        assert_eq!(
1639            reply,
1640            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
1641        );
1642        // A name with a line ending in it cannot write its own frame into the
1643        // stream, which is the reason the error writer maps them to spaces.
1644        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
1645        assert_eq!(reply.matches("\r\n").count(), 1);
1646    }
1647
1648    #[test]
1649    fn arity_is_checked_before_the_command_is() {
1650        let mut f = Fixture::new();
1651        assert_eq!(
1652            f.run(&[b"GET"]),
1653            "-ERR wrong number of arguments for 'get' command\r\n"
1654        );
1655        assert_eq!(
1656            f.run(&[b"MSET", b"k"]),
1657            "-ERR wrong number of arguments for 'mset' command\r\n"
1658        );
1659        // The table says `PING` takes one or more and a real server then
1660        // refuses three, which is the sort of thing that only shows up against
1661        // the real thing.
1662        assert_eq!(
1663            f.run(&[b"PING", b"a", b"b"]),
1664            "-ERR wrong number of arguments for 'ping' command\r\n"
1665        );
1666        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
1667        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
1668        // `DELEX` takes two or four and nothing between.
1669        assert_eq!(
1670            f.run(&[b"DELEX", b"k", b"IFEQ"]),
1671            "-ERR wrong number of arguments for 'delex' command\r\n"
1672        );
1673    }
1674
1675    /// The option rules, all of them measured against 8.8 rather than read off
1676    /// the documentation. The surprising one is that `SET` accepts the same
1677    /// keyword twice and `INCREX` does not.
1678    #[test]
1679    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
1680        let mut f = Fixture::new();
1681        let syntax = "-ERR syntax error\r\n";
1682        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
1683        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
1684        assert_eq!(
1685            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
1686            syntax
1687        );
1688        assert_eq!(
1689            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
1690            syntax
1691        );
1692        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
1693        // Twice is fine, and the last one wins.
1694        assert_eq!(
1695            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
1696            "+OK\r\n"
1697        );
1698        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
1699        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
1700        // `INCREX` refuses what `SET` allows.
1701        assert_eq!(
1702            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
1703            syntax
1704        );
1705        assert_eq!(
1706            f.run(&[b"INCREX", b"n", b"ENX"]),
1707            "-ERR ENX flag requires an expiration\r\n"
1708        );
1709        assert_eq!(
1710            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
1711            "-ERR UBOUND is not an integer or out of range\r\n"
1712        );
1713        assert_eq!(
1714            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
1715            "-ERR LBOUND can't be greater than UBOUND\r\n"
1716        );
1717        assert_eq!(
1718            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
1719            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
1720        );
1721    }
1722
1723    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
1724    /// key that is not there, which answers null without ever looking at the
1725    /// expiration it was given.
1726    #[test]
1727    fn the_expiry_rules_are_redis_own() {
1728        let mut f = Fixture::new();
1729        let bad = "-ERR invalid expire time in 'set' command\r\n";
1730        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
1731        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
1732        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
1733        assert_eq!(
1734            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
1735            bad
1736        );
1737        assert_eq!(
1738            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
1739            "-ERR value is not an integer or out of range\r\n"
1740        );
1741        assert_eq!(
1742            f.run(&[b"SETEX", b"k", b"0", b"v"]),
1743            "-ERR invalid expire time in 'setex' command\r\n"
1744        );
1745        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
1746        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
1747        assert_eq!(
1748            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
1749            "-ERR syntax error\r\n",
1750            "the option list is still checked before the key is looked up"
1751        );
1752        // A deadline in the past is accepted and the key goes with it.
1753        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
1754        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
1755        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1756    }
1757
1758    #[test]
1759    fn mset_takes_its_pairs_from_the_read_buffer() {
1760        let mut f = Fixture::new();
1761        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
1762        assert_eq!(
1763            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
1764            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
1765        );
1766        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
1767        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
1768        assert_eq!(
1769            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
1770            "-ERR wrong number of key-value pairs\r\n"
1771        );
1772        assert_eq!(
1773            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
1774            "-ERR invalid numkeys value\r\n"
1775        );
1776        assert_eq!(
1777            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
1778            "-ERR invalid numkeys value\r\n"
1779        );
1780    }
1781
1782    #[test]
1783    fn lcs_answers_the_length_the_string_and_the_runs() {
1784        let mut f = Fixture::new();
1785        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
1786        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
1787        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
1788        assert_eq!(
1789            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
1790            "*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"
1791        );
1792        // Without `IDX` the two options that only mean something with it are
1793        // accepted and ignored, which is what a real server does.
1794        assert_eq!(
1795            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
1796            "$6\r\nmytext\r\n"
1797        );
1798    }
1799
1800    #[test]
1801    fn select_moves_the_connection_and_the_databases_stay_apart() {
1802        let mut f = Fixture::new();
1803        f.run(&[b"SET", b"k", b"zero"]);
1804        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
1805        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1806        f.run(&[b"SET", b"k", b"four"]);
1807        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1808        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1809        assert_eq!(
1810            f.run(&[b"SELECT", b"99"]),
1811            "-ERR DB index is out of range\r\n"
1812        );
1813        assert_eq!(
1814            f.run(&[b"SELECT", b"-1"]),
1815            "-ERR DB index is out of range\r\n"
1816        );
1817        assert_eq!(
1818            f.run(&[b"SELECT", b"abc"]),
1819            "-ERR value is not an integer or out of range\r\n"
1820        );
1821        // `RESET` brings it back to zero.
1822        f.run(&[b"SELECT", b"4"]);
1823        f.run(&[b"RESET"]);
1824        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1825    }
1826
1827    #[test]
1828    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
1829        let mut f = Fixture::new();
1830        let reply = f.run(&[b"HELLO"]);
1831        assert!(reply.starts_with("*14\r\n"), "{reply}");
1832        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
1833        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
1834        assert!(
1835            reply.contains(":7\r\n"),
1836            "the connection id is in there: {reply}"
1837        );
1838        assert_eq!(
1839            f.run(&[b"HELLO", b"4"]),
1840            "-NOPROTO unsupported protocol version\r\n"
1841        );
1842        assert_eq!(
1843            f.run(&[b"HELLO", b"abc"]),
1844            "-ERR Protocol version is not an integer or out of range\r\n"
1845        );
1846        assert_eq!(
1847            f.run(&[b"HELLO", b"3", b"SETNAME"]),
1848            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
1849        );
1850        assert!(
1851            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
1852                .starts_with("%7\r\n")
1853        );
1854        assert_eq!(f.session.name(), b"bob");
1855        f.run(&[b"RESET"]);
1856        assert_eq!(f.session.name(), b"");
1857    }
1858
1859    #[test]
1860    fn command_describes_this_server_in_the_shape_a_driver_reads() {
1861        let mut f = Fixture::new();
1862        let count = format!(":{}\r\n", COMMANDS.len());
1863        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
1864        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
1865        assert_eq!(
1866            info,
1867            "*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\
1868             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
1869        );
1870        // A null in the list, and the plain one: `$-1` and not `*-1`.
1871        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
1872        assert_eq!(
1873            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
1874            "*1\r\n$8\r\ngetrange\r\n"
1875        );
1876        assert_eq!(
1877            f.run(&[b"COMMAND", b"NOPE"]),
1878            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
1879        );
1880    }
1881
1882    /// A cluster aware client asks this question and then routes on the
1883    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
1884    /// that matters.
1885    #[test]
1886    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
1887        let mut f = Fixture::new();
1888        assert_eq!(
1889            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
1890            "*1\r\n$1\r\nk\r\n"
1891        );
1892        assert_eq!(
1893            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
1894            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1895        );
1896        assert_eq!(
1897            f.run(&[
1898                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
1899            ]),
1900            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1901        );
1902        assert_eq!(
1903            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
1904            "-ERR The command has no key arguments\r\n"
1905        );
1906        assert_eq!(
1907            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
1908            "-ERR Invalid number of arguments specified for command\r\n"
1909        );
1910    }
1911
1912    #[test]
1913    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
1914        let mut f = Fixture::new();
1915        assert_eq!(
1916            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
1917            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
1918        );
1919        // A pattern matches more than one, and a setting two patterns both ask
1920        // for is still sent once.
1921        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
1922        assert!(both.starts_with("*6\r\n"), "{both}");
1923        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
1924        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
1925        assert_eq!(
1926            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
1927            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
1928        );
1929        assert_eq!(
1930            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
1931            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
1932        );
1933        assert_eq!(
1934            f.run(&[b"CONFIG", b"GET"]),
1935            "-ERR wrong number of arguments for 'config|get' command\r\n"
1936        );
1937        // Too few arguments and an odd number of them are different
1938        // complaints, which is the sort of thing only the real server tells
1939        // you.
1940        assert_eq!(
1941            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
1942            "-ERR wrong number of arguments for 'config|set' command\r\n"
1943        );
1944        assert_eq!(
1945            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
1946            "-ERR syntax error\r\n"
1947        );
1948        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
1949        assert_eq!(
1950            f.run(&[b"CONFIG", b"REWRITE"]),
1951            "-ERR The server is running without a config file\r\n"
1952        );
1953    }
1954
1955    #[test]
1956    fn the_eviction_policy_reads_back_what_was_written_to_it() {
1957        let mut f = Fixture::new();
1958        assert_eq!(
1959            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
1960            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
1961        );
1962        assert_eq!(
1963            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
1964            "+OK\r\n",
1965            "the name is matched without regard to case, like every other one"
1966        );
1967        assert_eq!(
1968            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
1969            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
1970        );
1971        // And INFO agrees with CONFIG, which it did not when it was a literal.
1972        assert!(
1973            f.run(&[b"INFO", b"memory"])
1974                .contains("maxmemory_policy:allkeys-lfu"),
1975            "INFO and CONFIG disagree about the policy"
1976        );
1977        // The refusal names every legal value in the order the real server's
1978        // enum table lists them, because a client comparing the message compares
1979        // the whole string.
1980        assert_eq!(
1981            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
1982            "-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"
1983        );
1984        // A bad pair leaves the good one in the same command alone, and the
1985        // policy is checked by the same pass that checks the numbers.
1986        assert_eq!(
1987            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
1988            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
1989        );
1990        f.run(&[
1991            b"CONFIG",
1992            b"SET",
1993            b"hash-max-listpack-entries",
1994            b"7",
1995            b"maxmemory-policy",
1996            b"nonsense",
1997        ]);
1998        assert_eq!(
1999            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2000            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
2001        );
2002    }
2003
2004    #[test]
2005    fn the_three_eviction_numbers_read_back_too() {
2006        let mut f = Fixture::new();
2007        for (name, default, set) in [
2008            ("maxmemory-samples", "5", "12"),
2009            ("lfu-log-factor", "10", "3"),
2010            ("lfu-decay-time", "1", "60"),
2011        ] {
2012            let get = || {
2013                format!(
2014                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
2015                    name.len(),
2016                    default.len()
2017                )
2018            };
2019            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
2020            assert_eq!(
2021                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
2022                "+OK\r\n"
2023            );
2024            assert_eq!(
2025                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
2026                format!(
2027                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
2028                    name.len(),
2029                    set.len()
2030                )
2031            );
2032            // A number that is not a number is refused with the same sentence
2033            // every other number gets, which names the setting the client typed.
2034            assert_eq!(
2035                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
2036                format!(
2037                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
2038                )
2039            );
2040        }
2041    }
2042
2043    #[test]
2044    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
2045        let mut f = Fixture::new();
2046        assert_eq!(
2047            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2048            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
2049            "no limit is the default"
2050        );
2051        // The pairing is Redis's and it is a trap: the bare letter is a power of
2052        // ten and the one with the b is a power of two.
2053        for (typed, bytes) in [
2054            (&b"1024"[..], "1024"),
2055            (b"1k", "1000"),
2056            (b"1kb", "1024"),
2057            (b"1M", "1000000"),
2058            (b"1Mb", "1048576"),
2059            (b"1gb", "1073741824"),
2060            (b"100mb", "104857600"),
2061        ] {
2062            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
2063            assert_eq!(
2064                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2065                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
2066                "set {}",
2067                String::from_utf8_lossy(typed)
2068            );
2069        }
2070        assert!(
2071            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2072            "the report agrees with the setting"
2073        );
2074
2075        // A unit nobody has heard of, and a negative number, which is not a very
2076        // large one however it is spelled.
2077        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
2078            assert_eq!(
2079                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
2080                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
2081                "refused {}",
2082                String::from_utf8_lossy(bad)
2083            );
2084        }
2085        assert!(
2086            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2087            "and the refusal left the old one alone"
2088        );
2089    }
2090
2091    #[test]
2092    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
2093        let mut f = Fixture::new();
2094        f.run(&[b"SET", b"here", b"already"]);
2095        // A byte, which is under what an empty server holds, so nothing this
2096        // command could do would get it under. The default policy is
2097        // `noeviction`, so nothing is what it does.
2098        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
2099        assert_eq!(
2100            f.run(&[b"SET", b"k", b"v"]),
2101            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2102        );
2103        assert_eq!(
2104            f.run(&[b"LPUSH", b"l", b"v"]),
2105            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2106        );
2107        // Reading is allowed, and so is the one thing that would help.
2108        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
2109        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
2110        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
2111
2112        // Taking the limit away lets the write through again.
2113        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2114        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2115    }
2116
2117    #[test]
2118    fn an_allkeys_policy_makes_room_instead_of_refusing() {
2119        let mut f = Fixture::new();
2120        let val = vec![b'v'; 256];
2121        for i in 0..24000u32 {
2122            let k = format!("key:{i:08}");
2123            f.run(&[b"SET", k.as_bytes(), &val]);
2124        }
2125        let full = f.server.memory_bytes();
2126        assert!(
2127            full > 3 * 1024 * 1024,
2128            "the arena is several segments: {full}"
2129        );
2130
2131        // Two megabytes under what it is holding, which is one segment's worth,
2132        // so getting there means giving a whole segment back and not just
2133        // dropping a few records.
2134        let limit = full - 2 * 1024 * 1024;
2135        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
2136        f.run(&[
2137            b"CONFIG",
2138            b"SET",
2139            b"maxmemory",
2140            limit.to_string().as_bytes(),
2141        ]);
2142
2143        // Writes keep working the whole way down. The budget means one command
2144        // does not do it all, so this runs until the server has settled and
2145        // checks that nothing was refused on the way.
2146        for i in 0..2000u32 {
2147            let k = format!("new:{i:08}");
2148            assert_eq!(
2149                f.run(&[b"SET", k.as_bytes(), &val]),
2150                "+OK\r\n",
2151                "write {i} was refused"
2152            );
2153            f.server.refresh_memory();
2154            if f.server.memory_bytes() <= limit {
2155                break;
2156            }
2157        }
2158        assert!(
2159            f.server.memory_bytes() <= limit,
2160            "it never got under: {} against {limit}",
2161            f.server.memory_bytes()
2162        );
2163        let info = f.run(&[b"INFO", b"stats"]);
2164        assert!(!info.contains("evicted_keys:0"), "{info}");
2165        assert!(
2166            f.run(&[b"DBSIZE"]) != ":0\r\n",
2167            "and it did not empty the database to get there"
2168        );
2169    }
2170
2171    #[test]
2172    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
2173        // The limit is judged against a number kept as the collections move,
2174        // rather than found by asking all of them, and the two have to be the
2175        // same number or the limit is enforced against a fiction. This does the
2176        // things that move it, which is growing a collection, shrinking one,
2177        // changing its representation, deleting it and reusing its slot, across
2178        // all five types, and checks the two against each other as it goes.
2179        let mut f = Fixture::new();
2180        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2181        let big = vec![b'v'; 200];
2182
2183        for i in 0..400u32 {
2184            let n = i.to_string();
2185            let n = n.as_bytes();
2186            f.run(&[b"SADD", b"s", n]);
2187            f.run(&[b"SADD", b"s2", &big]);
2188            f.run(&[b"HSET", b"h", n, &big]);
2189            f.run(&[b"RPUSH", b"l", &big]);
2190            f.run(&[b"ZADD", b"z", n, n]);
2191            f.run(&[b"ARSET", b"a", n, &big]);
2192            if i % 7 == 0 {
2193                f.run(&[b"SREM", b"s", n]);
2194                f.run(&[b"HDEL", b"h", n]);
2195                f.run(&[b"LPOP", b"l"]);
2196                f.run(&[b"ZREM", b"z", n]);
2197                f.run(&[b"ARDEL", b"a", n]);
2198            }
2199            if i % 53 == 0 {
2200                // Every type deleted and made again, so a slot goes on the free
2201                // list and comes back holding something else.
2202                f.run(&[b"DEL", b"s2"]);
2203            }
2204            assert_eq!(
2205                f.server.settled_memory(),
2206                f.server.memory_bytes(),
2207                "after round {i}"
2208            );
2209        }
2210
2211        // The run has to have built something, or the two numbers agreeing is
2212        // two zeroes agreeing.
2213        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
2214        assert!(
2215            f.server.memory_bytes() > 512 * 1024,
2216            "{}",
2217            f.server.memory_bytes()
2218        );
2219
2220        // And it survives the collections going away entirely.
2221        f.run(&[b"FLUSHALL"]);
2222        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2223    }
2224
2225    #[test]
2226    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
2227        // A server with no limit does not keep the running total, so setting a
2228        // limit on a database that is already full has to start it from a walk.
2229        // If it did not, the first reading would be zero and the server would
2230        // think it had all the room in the world.
2231        let mut f = Fixture::new();
2232        for i in 0..200u32 {
2233            let n = i.to_string();
2234            f.run(&[b"SADD", b"s", n.as_bytes()]);
2235            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
2236        }
2237        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2238        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2239
2240        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2241        for i in 200..400u32 {
2242            let n = i.to_string();
2243            f.run(&[b"SADD", b"s", n.as_bytes()]);
2244        }
2245        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2246        assert_eq!(
2247            f.server.settled_memory(),
2248            f.server.memory_bytes(),
2249            "the writes it was not watching are in the number it started from"
2250        );
2251    }
2252
2253    #[test]
2254    fn evicted_keys_and_expired_keys_are_different_numbers() {
2255        let mut f = Fixture::new();
2256        // Nothing has been evicted and nothing can be under the default policy,
2257        // so this stays at zero while the other one moves.
2258        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
2259        f.server.db(0).clock_mut().advance(20);
2260        f.run(&[b"GET", b"gone"]);
2261        let info = f.run(&[b"INFO", b"stats"]);
2262        assert!(info.contains("expired_keys:1"), "{info}");
2263        assert!(info.contains("evicted_keys:0"), "{info}");
2264    }
2265
2266    #[test]
2267    fn the_object_subcommands_follow_the_policy() {
2268        let mut f = Fixture::new();
2269        f.run(&[b"SET", b"s", b"v"]);
2270        // Under the default the clock is kept and the counter is not, and under
2271        // an LFU policy it is the other way round. Each subcommand refuses on
2272        // the side where its reading of the three bytes means nothing.
2273        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2274        assert!(
2275            f.run(&[b"OBJECT", b"FREQ", b"s"])
2276                .starts_with("-ERR An LFU maxmemory policy is not selected"),
2277        );
2278
2279        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
2280        assert!(
2281            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
2282                .starts_with("-ERR An LFU maxmemory policy is selected"),
2283        );
2284        // The key was written under a clock policy, so what comes back is that
2285        // clock read as a counter. It is a number and not an error, which is the
2286        // point: switching at runtime does not invalidate anything, it only makes
2287        // the old field mean something else until the key is used again.
2288        assert!(
2289            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
2290            "FREQ should answer under an LFU policy"
2291        );
2292    }
2293
2294    #[test]
2295    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
2296        let mut f = Fixture::new();
2297        f.run(&[b"SET", b"s", b"hello"]);
2298        f.run(&[b"SET", b"n", b"123"]);
2299        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
2300        f.run(&[b"SADD", b"ss", b"a", b"b"]);
2301        f.run(&[b"HSET", b"h", b"f", b"v"]);
2302        for (key, want) in [
2303            (b"s".as_slice(), "embstr"),
2304            (b"n", "int"),
2305            (b"si", "intset"),
2306            (b"ss", "listpack"),
2307            (b"h", "listpack"),
2308        ] {
2309            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
2310            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
2311        }
2312
2313        // A field deadline widens the blob rather than promoting it, and this
2314        // is the only place a client can see that happen.
2315        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
2316        assert_eq!(
2317            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2318            "$10\r\nlistpackex\r\n"
2319        );
2320
2321        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
2322        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2323        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
2324    }
2325
2326    #[test]
2327    fn object_answers_nil_for_a_key_that_is_not_there() {
2328        let mut f = Fixture::new();
2329        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
2330            assert_eq!(
2331                f.run(&[b"OBJECT", sub, b"nokey"]),
2332                "$-1\r\n",
2333                "a nil and not an error, which is what 8.10.1 does"
2334            );
2335        }
2336        // And the key is looked up before FREQ has its complaint, so the
2337        // complaint only reaches a key that exists.
2338        f.run(&[b"SET", b"s", b"v"]);
2339        assert!(
2340            f.run(&[b"OBJECT", b"FREQ", b"s"])
2341                .starts_with("-ERR An LFU maxmemory policy is not"),
2342        );
2343        assert_eq!(
2344            f.run(&[b"OBJECT", b"NOPE", b"s"]),
2345            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
2346        );
2347        assert_eq!(
2348            f.run(&[b"OBJECT", b"ENCODING"]),
2349            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2350        );
2351        assert_eq!(
2352            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
2353            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2354        );
2355        assert_eq!(
2356            f.run(&[b"OBJECT"]),
2357            "-ERR wrong number of arguments for 'object' command\r\n"
2358        );
2359    }
2360
2361    #[test]
2362    fn config_moves_the_ladder_and_object_encoding_agrees() {
2363        let mut f = Fixture::new();
2364        assert_eq!(
2365            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2366            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
2367            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
2368        );
2369        // The old spelling is the same number under a different name, and a
2370        // glob that catches both sends both.
2371        assert_eq!(
2372            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
2373            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
2374        );
2375        assert!(
2376            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
2377                .starts_with("*8\r\n")
2378        );
2379        assert!(
2380            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
2381                .starts_with("*6\r\n")
2382        );
2383
2384        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
2385        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
2386
2387        assert_eq!(
2388            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
2389            "+OK\r\n",
2390            "written under the old name and read back under the new one"
2391        );
2392        assert_eq!(
2393            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2394            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
2395        );
2396        assert_eq!(
2397            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2398            "$8\r\nlistpack\r\n",
2399            "the hash that already exists is left exactly where it was"
2400        );
2401        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
2402        assert_eq!(
2403            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
2404            "$9\r\nhashtable\r\n",
2405            "and the next one built goes straight to a table"
2406        );
2407
2408        // The set has three of these and all three move.
2409        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
2410        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
2411        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
2412        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
2413        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
2414        assert_eq!(
2415            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
2416            "$9\r\nhashtable\r\n"
2417        );
2418    }
2419
2420    #[test]
2421    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
2422        let mut f = Fixture::new();
2423        assert_eq!(
2424            f.run(&[
2425                b"CONFIG",
2426                b"SET",
2427                b"hash-max-listpack-entries",
2428                b"7",
2429                b"set-max-listpack-entries",
2430                b"abc"
2431            ]),
2432            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
2433        );
2434        assert_eq!(
2435            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2436            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
2437            "the pair in front of the bad one did not go in"
2438        );
2439        // The name in the complaint is the one that was typed, so the old
2440        // spelling comes back as the old spelling.
2441        assert_eq!(
2442            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
2443            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
2444        );
2445        assert_eq!(
2446            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
2447            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
2448        );
2449        // A number past what an i64 holds is the parse complaint and not the
2450        // range one, which is upstream reading it before it checks it.
2451        assert_eq!(
2452            f.run(&[
2453                b"CONFIG",
2454                b"SET",
2455                b"set-max-intset-entries",
2456                b"99999999999999999999"
2457            ]),
2458            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
2459        );
2460        assert_eq!(
2461            f.run(&[
2462                b"CONFIG",
2463                b"SET",
2464                b"set-max-intset-entries",
2465                b"9223372036854775807"
2466            ]),
2467            "+OK\r\n"
2468        );
2469    }
2470
2471    #[test]
2472    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
2473        let mut f = Fixture::new();
2474        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
2475        f.run(&[b"SELECT", b"3"]);
2476        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
2477        assert_eq!(
2478            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2479            "$9\r\nhashtable\r\n",
2480            "these are one server wide number in Redis, whatever a Keyspace carries"
2481        );
2482    }
2483
2484    #[test]
2485    fn info_reports_the_numbers_it_can_stand_behind() {
2486        let mut f = Fixture::new();
2487        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2488        let all = f.run(&[b"INFO"]);
2489        assert!(all.contains("redis_version:8.8.0"), "{all}");
2490        assert!(
2491            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
2492            "{all}"
2493        );
2494        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
2495        assert!(all.contains("role:master"), "{all}");
2496        // One section is one section.
2497        let clients = f.run(&[b"INFO", b"clients"]);
2498        assert!(clients.contains("connected_clients:0"), "{clients}");
2499        assert!(!clients.contains("redis_version"), "{clients}");
2500        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
2501    }
2502
2503    #[cfg(unix)]
2504    #[test]
2505    fn info_cpu_reports_processor_time_that_was_really_measured() {
2506        let mut f = Fixture::new();
2507        let cpu = f.run(&[b"INFO", b"cpu"]);
2508        assert!(cpu.contains("# CPU"), "{cpu}");
2509        // Redis's unit/info-command asks for this one by name in three tests.
2510        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
2511        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
2512        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
2513        assert!(!cpu.contains("redis_version"), "{cpu}");
2514
2515        // It is a measurement and not a constant, so it goes up when work
2516        // happens. A tight loop rather than a sleep, because sleeping is the
2517        // one thing that does not move this number.
2518        let before = used_cpu_user(&cpu);
2519        let mut n = 0u64;
2520        let mut rounds = 0;
2521        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
2522            for i in 0..1_000_000u64 {
2523                n = n.wrapping_add(i.wrapping_mul(i));
2524            }
2525            rounds += 1;
2526            // A bound rather than a spin, so a platform where this number does
2527            // not move fails here instead of hanging. Even a clock with whole
2528            // millisecond granularity gets there in the first round or two.
2529            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
2530        }
2531    }
2532
2533    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
2534    #[cfg(unix)]
2535    fn used_cpu_user(info: &str) -> f64 {
2536        info.lines()
2537            .find_map(|l| l.strip_prefix("used_cpu_user:"))
2538            .expect("no used_cpu_user in the reply")
2539            .trim()
2540            .parse()
2541            .expect("used_cpu_user is not a number")
2542    }
2543
2544    /// The safety net under the rule that a body checks its arguments before
2545    /// it writes anything. `MGET` writes its array header first and then reads
2546    /// each key, so if a later argument could fail the header would already be
2547    /// out. Nothing in the string group does that today and this is what would
2548    /// catch the first one that did.
2549    #[test]
2550    fn a_command_that_fails_leaves_nothing_half_written() {
2551        let mut f = Fixture::new();
2552        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
2553        assert_eq!(reply, "-ERR offset is out of range\r\n");
2554        assert!(!reply.contains(':'), "no integer went out in front of it");
2555    }
2556
2557    #[test]
2558    fn quit_answers_first_and_closes_after() {
2559        let mut f = Fixture::new();
2560        let (flow, reply) = f.flow(&[b"QUIT"]);
2561        assert_eq!(reply, "+OK\r\n");
2562        assert_eq!(flow, Flow::Close);
2563    }
2564
2565    #[test]
2566    fn the_command_counter_counts_every_command_including_the_bad_ones() {
2567        let mut f = Fixture::new();
2568        f.run(&[b"PING"]);
2569        f.run(&[b"NOPE"]);
2570        f.run(&[b"GET"]);
2571        assert_eq!(f.server.stats.commands, 3);
2572    }
2573
2574    #[test]
2575    fn a_set_goes_from_bytes_to_bytes() {
2576        let mut f = Fixture::new();
2577        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
2578        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
2579        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
2580        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
2581        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
2582        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
2583        assert_eq!(
2584            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
2585            "*3\r\n:1\r\n:0\r\n:1\r\n"
2586        );
2587        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
2588        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
2589    }
2590
2591    #[test]
2592    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
2593        let mut f = Fixture::new();
2594        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
2595        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
2596        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
2597        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
2598        assert_eq!(
2599            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
2600            "*2\r\n:0\r\n:0\r\n"
2601        );
2602        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
2603    }
2604
2605    #[test]
2606    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
2607        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
2608        // and one that gets a `*` hands it a list, without either of them being
2609        // told which command was sent.
2610        let mut f = Fixture::new();
2611        f.run(&[b"SADD", b"s", b"one"]);
2612        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
2613
2614        f.run(&[b"HELLO", b"3"]);
2615        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
2616    }
2617
2618    #[test]
2619    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
2620        // An intset holds the number, so these digits exist for the first time
2621        // in the reply buffer.
2622        let mut f = Fixture::new();
2623        f.run(&[b"SADD", b"s", b"42"]);
2624        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
2625        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
2626        assert_eq!(
2627            f.run(&[b"SISMEMBER", b"s", b"042"]),
2628            ":0\r\n",
2629            "the member is the bytes and not the number they parse to"
2630        );
2631    }
2632
2633    #[test]
2634    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
2635        let mut f = Fixture::new();
2636        f.run(&[b"SET", b"str", b"v"]);
2637        f.run(&[b"SADD", b"set", b"a"]);
2638
2639        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
2640        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
2641        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
2642        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
2643        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
2644        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
2645        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
2646        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
2647        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
2648
2649        // MGET is the one that does not, because Redis gives nil for the odd
2650        // key out rather than failing the good keys next to it.
2651        assert_eq!(
2652            f.run(&[b"MGET", b"str", b"set", b"nope"]),
2653            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
2654        );
2655        // And plain SET overwrites any type, which takes the body with it.
2656        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
2657        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
2658    }
2659
2660    #[test]
2661    fn a_wrongtype_leaves_nothing_half_written() {
2662        // SMISMEMBER writes an array header and then one reply per member, so
2663        // it is the first command in the server that could get a header out in
2664        // front of an error if it checked its key in the wrong order.
2665        let mut f = Fixture::new();
2666        f.run(&[b"SET", b"k", b"v"]);
2667        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
2668        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
2669        assert!(!reply.contains('*'), "an array header went out in front");
2670    }
2671
2672    #[test]
2673    fn emptying_a_set_takes_the_key_with_it() {
2674        let mut f = Fixture::new();
2675        f.run(&[b"SADD", b"s", b"a", b"b"]);
2676        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2677        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
2678        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
2679        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
2680        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2681    }
2682
2683    /// Pull the cursor and the members out of one `SSCAN` reply.
2684    ///
2685    /// Crude on purpose. A test that walked a set through a real client would
2686    /// be testing the client, and what these tests are about is the shape of
2687    /// the bytes and the fact that a walk sees every member once.
2688    fn split_scan(reply: &str) -> (String, Vec<String>) {
2689        let mut lines = reply.split("\r\n");
2690        assert_eq!(lines.next(), Some("*2"), "got {reply}");
2691        lines.next().expect("the cursor header");
2692        let cursor = lines.next().expect("the cursor").to_owned();
2693        let header = lines.next().expect("the member header");
2694        let n: usize = header[1..].parse().expect("a member count");
2695        let mut members = Vec::with_capacity(n);
2696        for _ in 0..n {
2697            lines.next().expect("a member header");
2698            members.push(lines.next().expect("a member").to_owned());
2699        }
2700        (cursor, members)
2701    }
2702
2703    #[test]
2704    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
2705        let mut f = Fixture::new();
2706        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
2707
2708        let one = f.run(&[b"SPOP", b"s"]);
2709        assert!(
2710            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
2711            "got {one}"
2712        );
2713        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
2714
2715        // A count takes that many, and the last one takes the key with it.
2716        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
2717        assert!(rest.starts_with("*3\r\n"), "got {rest}");
2718        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
2719        // And a pop at a key that is not there is a nil, not an empty bulk.
2720        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
2721        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
2722    }
2723
2724    #[test]
2725    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
2726        // The one place in the server where the reply type carries something
2727        // the command name does not. SPOP's members are distinct so a RESP3
2728        // client can build a set out of them. SRANDMEMBER with a negative count
2729        // can hand back the same member three times, and a set would lose two.
2730        let mut f = Fixture::new();
2731        f.run(&[b"HELLO", b"3"]);
2732        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
2733
2734        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
2735        // And a positive count is an array too, since Redis makes it one.
2736        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
2737
2738        // A negative count against a set of one is where the difference bites:
2739        // the same member three times, which is a three element reply and would
2740        // have been a one element reply if it had gone out as a set.
2741        f.run(&[b"SADD", b"one", b"z"]);
2742        assert_eq!(
2743            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
2744            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
2745        );
2746    }
2747
2748    #[test]
2749    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
2750        let mut f = Fixture::new();
2751        f.run(&[b"SADD", b"s", b"only"]);
2752        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
2753        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
2754        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
2755
2756        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
2757        // The count form answers an empty array rather than a nil, which is the
2758        // pair of answers Redis gives and is not the pair it looks like.
2759        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
2760        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
2761        // Asking for more than is there answers all of it once and not padding.
2762        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
2763    }
2764
2765    #[test]
2766    fn a_pop_count_that_is_not_a_positive_number_says_so() {
2767        let mut f = Fixture::new();
2768        f.run(&[b"SADD", b"s", b"a"]);
2769        let bad = "-ERR value is out of range, must be positive\r\n";
2770        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
2771        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
2772        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
2773        // Zero is allowed and is a real answer rather than an error.
2774        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
2775        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
2776    }
2777
2778    #[test]
2779    fn a_scan_walks_a_set_of_any_size_exactly_once() {
2780        let mut f = Fixture::new();
2781        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
2782        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
2783            .into_iter()
2784            .chain(members.iter().map(Vec::as_slice))
2785            .collect();
2786        f.run(&args);
2787
2788        let mut seen = Vec::new();
2789        let mut cursor = "0".to_owned();
2790        loop {
2791            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
2792            let (next, got) = split_scan(&reply);
2793            seen.extend(got);
2794            cursor = next;
2795            if cursor == "0" {
2796                break;
2797            }
2798        }
2799        seen.sort();
2800        seen.dedup();
2801        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
2802
2803        // A set small enough to be a listpack answers in one call whatever
2804        // cursor it was handed, which is what Redis does for that encoding.
2805        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
2806        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
2807        assert_eq!(cursor, "0");
2808        assert_eq!(got.len(), 3);
2809        // And a key that is not there is a finished scan of nothing.
2810        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
2811    }
2812
2813    #[test]
2814    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
2815        let mut f = Fixture::new();
2816        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
2817
2818        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
2819        let mut got = got;
2820        got.sort();
2821        assert_eq!(got, ["aa", "ab"]);
2822
2823        // An integer member has no digits stored anywhere, so MATCH is the one
2824        // place a scan pays to write some.
2825        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
2826        let mut got = got;
2827        got.sort();
2828        assert_eq!(got, ["12", "13"]);
2829
2830        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
2831        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
2832        assert_eq!(
2833            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
2834            "-ERR syntax error\r\n"
2835        );
2836        // A count under one is a syntax error and not a range error, which is
2837        // the odder of Redis's two answers and the reason it is copied exactly.
2838        assert_eq!(
2839            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
2840            "-ERR syntax error\r\n"
2841        );
2842    }
2843
2844    #[test]
2845    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
2846        let mut f = Fixture::new();
2847        f.run(&[b"SADD", b"src", b"a", b"b"]);
2848        f.run(&[b"SADD", b"dst", b"c"]);
2849
2850        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
2851        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
2852        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
2853        // A member that is not in the source is a zero and moves nothing.
2854        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
2855        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
2856
2857        // A destination that does not exist gets made, and a source that runs
2858        // out goes away.
2859        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
2860        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
2861        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
2862    }
2863
2864    #[test]
2865    fn moving_checks_the_types_in_the_order_redis_checks_them() {
2866        // Not the order it looks like it should be. A source that is not there
2867        // answers zero without ever looking at the destination, so this is a
2868        // zero and not a WRONGTYPE even though the destination is a string.
2869        let mut f = Fixture::new();
2870        f.run(&[b"SET", b"str", b"v"]);
2871        f.run(&[b"SADD", b"set", b"a"]);
2872
2873        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
2874        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
2875        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
2876        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
2877        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
2878        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
2879        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
2880        assert_eq!(
2881            f.run(&[b"SISMEMBER", b"set", b"a"]),
2882            ":1\r\n",
2883            "and none of that moved anything"
2884        );
2885    }
2886
2887    #[test]
2888    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
2889        // SSCAN writes an outer array header before it walks, so it is the
2890        // command most likely to get bytes out in front of an error.
2891        let mut f = Fixture::new();
2892        f.run(&[b"SADD", b"s", b"a"]);
2893        for bad in [
2894            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
2895            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
2896            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
2897        ] {
2898            let reply = f.run(bad);
2899            assert!(reply.starts_with("-ERR"), "got {reply}");
2900            assert!(!reply.contains('*'), "an array header went out in front");
2901        }
2902    }
2903
2904    #[test]
2905    fn a_hash_writes_reads_and_deletes_its_fields() {
2906        let mut f = Fixture::new();
2907        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
2908        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
2909        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
2910        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
2911        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
2912        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
2913        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
2914        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
2915        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
2916        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
2917
2918        // The value the client sent is `9`, so HGET h b must not find the `2`
2919        // that is a value. A search with a step of one would have.
2920        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
2921
2922        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
2923        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
2924        assert_eq!(
2925            f.run(&[b"EXISTS", b"h"]),
2926            ":0\r\n",
2927            "and losing the last field lost the key"
2928        );
2929    }
2930
2931    #[test]
2932    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
2933        let mut f = Fixture::new();
2934        f.run(&[b"HSET", b"h", b"a", b"1"]);
2935        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
2936        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
2937        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
2938        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
2939        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
2940
2941        f.run(&[b"HELLO", b"3"]);
2942        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
2943        assert_eq!(
2944            f.run(&[b"HGETALL", b"nokey"]),
2945            "%0\r\n",
2946            "a missing key is the empty hash and never a nil"
2947        );
2948        assert_eq!(
2949            f.run(&[b"HKEYS", b"h"]),
2950            "*1\r\n$1\r\na\r\n",
2951            "and the two that answer one side stay arrays"
2952        );
2953    }
2954
2955    #[test]
2956    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
2957        let mut f = Fixture::new();
2958        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
2959        assert_eq!(
2960            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
2961            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
2962            "the reply is positional, so b is a nil and not a gap"
2963        );
2964        assert_eq!(
2965            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
2966            "*2\r\n$-1\r\n$-1\r\n",
2967            "and a missing key is all nils rather than an empty array"
2968        );
2969
2970        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
2971        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
2972        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
2973    }
2974
2975    #[test]
2976    fn a_hash_counts_up_and_says_so_when_it_cannot() {
2977        let mut f = Fixture::new();
2978        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
2979        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
2980        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
2981        assert_eq!(
2982            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
2983            "$4\r\n10.5\r\n",
2984            "a bulk string and not a double, on both protocols"
2985        );
2986
2987        f.run(&[b"HSET", b"h", b"s", b"words"]);
2988        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
2989        assert!(
2990            bad.starts_with("-ERR hash value is not an integer"),
2991            "{bad}"
2992        );
2993        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
2994        assert!(
2995            bad.starts_with("-ERR value is not an integer"),
2996            "a bad argument is not yet a hash value, {bad}"
2997        );
2998        assert_eq!(
2999            f.run(&[b"HGET", b"h", b"s"]),
3000            "$5\r\nwords\r\n",
3001            "and neither of them wrote anything"
3002        );
3003    }
3004
3005    #[test]
3006    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
3007        let mut f = Fixture::new();
3008        for i in 0..500 {
3009            let field = format!("field-{i}");
3010            let value = format!("value-{i}");
3011            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
3012        }
3013
3014        let mut seen: Vec<String> = Vec::new();
3015        let mut cursor = "0".to_owned();
3016        loop {
3017            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
3018            let (next, items) = scan_reply(&reply);
3019            assert_eq!(items.len() % 2, 0, "a pair went out half written");
3020            for pair in items.chunks(2) {
3021                assert_eq!(
3022                    pair[0].strip_prefix("field-"),
3023                    pair[1].strip_prefix("value-"),
3024                    "a field came back with someone else's value"
3025                );
3026                seen.push(pair[0].clone());
3027            }
3028            cursor = next;
3029            if cursor == "0" {
3030                break;
3031            }
3032        }
3033        seen.sort();
3034        seen.dedup();
3035        assert_eq!(seen.len(), 500, "every field once and only once");
3036
3037        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
3038        assert!(
3039            items.iter().all(|s| s.starts_with("field-")),
3040            "NOVALUES still sent the values"
3041        );
3042
3043        let (_, one) = scan_reply(&f.run(&[
3044            b"HSCAN",
3045            b"h",
3046            b"0",
3047            b"MATCH",
3048            b"field-499",
3049            b"COUNT",
3050            b"1000",
3051        ]));
3052        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
3053    }
3054
3055    #[test]
3056    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
3057        let mut f = Fixture::new();
3058        f.run(&[b"HSET", b"h", b"a", b"1"]);
3059        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
3060        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
3061        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
3062        assert_eq!(
3063            f.run(&[b"HRANDFIELD", b"h", b"3"]),
3064            "*1\r\n$1\r\na\r\n",
3065            "a positive count is capped at the size of the hash"
3066        );
3067        assert_eq!(
3068            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
3069            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
3070            "and a negative one repeats itself"
3071        );
3072        assert_eq!(
3073            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3074            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3075            "flat on RESP2"
3076        );
3077
3078        f.run(&[b"HELLO", b"3"]);
3079        assert_eq!(
3080            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3081            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3082            "and nested on RESP3, but still an array and never a map"
3083        );
3084    }
3085
3086    #[test]
3087    fn every_hash_command_says_wrongtype_and_writes_nothing() {
3088        let mut f = Fixture::new();
3089        f.run(&[b"SET", b"str", b"v"]);
3090        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3091
3092        for cmd in [
3093            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
3094            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
3095            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
3096            &[b"HGET".as_slice(), b"str", b"f"][..],
3097            &[b"HMGET".as_slice(), b"str", b"f"][..],
3098            &[b"HDEL".as_slice(), b"str", b"f"][..],
3099            &[b"HLEN".as_slice(), b"str"][..],
3100            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
3101            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
3102            &[b"HGETALL".as_slice(), b"str"][..],
3103            &[b"HKEYS".as_slice(), b"str"][..],
3104            &[b"HVALS".as_slice(), b"str"][..],
3105            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
3106            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
3107            &[b"HRANDFIELD".as_slice(), b"str"][..],
3108            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
3109            &[b"HSCAN".as_slice(), b"str", b"0"][..],
3110        ] {
3111            let reply = f.run(cmd);
3112            assert_eq!(reply, wrong, "{:?}", cmd[0]);
3113        }
3114        assert_eq!(
3115            f.run(&[b"GET", b"str"]),
3116            "$1\r\nv\r\n",
3117            "and none of them touched the value"
3118        );
3119    }
3120
3121    #[test]
3122    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
3123        let mut f = Fixture::new();
3124        f.run(&[b"HSET", b"h", b"f", b"v"]);
3125        for bad in [
3126            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
3127            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
3128            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
3129            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
3130        ] {
3131            let reply = f.run(bad);
3132            assert!(reply.starts_with("-ERR"), "got {reply}");
3133            assert!(!reply.contains('*'), "an array header went out in front");
3134        }
3135    }
3136
3137    #[test]
3138    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
3139        let mut f = Fixture::new();
3140        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3141        assert_eq!(
3142            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
3143            "*1\r\n:1\r\n"
3144        );
3145        assert_eq!(
3146            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3147            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
3148            "one answer per field, and the two sentinels are TTL's own"
3149        );
3150
3151        // The same deadline in the other three units, all of them derived from
3152        // the one number the store kept.
3153        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
3154        assert!((99_000..=100_000).contains(&ms), "got {ms}");
3155        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3156        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3157        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
3158        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
3159
3160        assert_eq!(
3161            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3162            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
3163            "one for the deadline taken off, and it does not say what it was"
3164        );
3165        assert_eq!(
3166            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3167            "*1\r\n:-1\r\n"
3168        );
3169        assert_eq!(
3170            f.run(&[b"HGET", b"h", b"a"]),
3171            "$1\r\n1\r\n",
3172            "and the field is still there with the value it had"
3173        );
3174    }
3175
3176    #[test]
3177    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
3178        let mut f = Fixture::new();
3179        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3180        assert_eq!(
3181            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
3182            "*1\r\n:2\r\n",
3183            "two, and not one, because nothing was stored"
3184        );
3185        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3186        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3187
3188        assert_eq!(
3189            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
3190            "*1\r\n:2\r\n"
3191        );
3192        assert_eq!(
3193            f.run(&[b"EXISTS", b"h"]),
3194            ":0\r\n",
3195            "and the last field going took the key with it"
3196        );
3197
3198        // Zero is a delete and not an error, where minus one is an error. That
3199        // is Redis's split and it is easy to get backwards.
3200        f.run(&[b"HSET", b"h", b"a", b"1"]);
3201        assert_eq!(
3202            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
3203            "*1\r\n:2\r\n"
3204        );
3205    }
3206
3207    #[test]
3208    fn a_field_is_gone_once_its_moment_passes() {
3209        let mut f = Fixture::new();
3210        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3211        assert_eq!(
3212            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
3213            "*1\r\n:1\r\n"
3214        );
3215        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
3216
3217        // Time moves once per turn of the event loop and nowhere else, so a
3218        // test moves it by hand rather than by sleeping. There is nothing to
3219        // sleep for: the deadline is a number and so is the clock.
3220        f.server.db(0).clock_mut().advance(60);
3221        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3222        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3223        assert_eq!(
3224            f.run(&[b"HGETALL", b"h"]),
3225            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
3226            "and the walks do not hand back a field that has expired"
3227        );
3228    }
3229
3230    #[test]
3231    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
3232        let mut f = Fixture::new();
3233        for cmd in [
3234            &[
3235                b"HEXPIRE".as_slice(),
3236                b"nokey",
3237                b"100",
3238                b"FIELDS",
3239                b"2",
3240                b"a",
3241                b"b",
3242            ][..],
3243            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3244            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3245            &[
3246                b"HEXPIRETIME".as_slice(),
3247                b"nokey",
3248                b"FIELDS",
3249                b"2",
3250                b"a",
3251                b"b",
3252            ][..],
3253            &[
3254                b"HPERSIST".as_slice(),
3255                b"nokey",
3256                b"FIELDS",
3257                b"2",
3258                b"a",
3259                b"b",
3260            ][..],
3261        ] {
3262            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
3263        }
3264    }
3265
3266    #[test]
3267    fn writing_a_field_clears_the_deadline_that_was_on_it() {
3268        let mut f = Fixture::new();
3269        f.run(&[b"HSET", b"h", b"a", b"1"]);
3270        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
3271        f.run(&[b"HSET", b"h", b"a", b"2"]);
3272        assert_eq!(
3273            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3274            "*1\r\n:-1\r\n",
3275            "Redis has done this since 7.4, and it is why HGETEX exists"
3276        );
3277    }
3278
3279    #[test]
3280    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
3281        let mut f = Fixture::new();
3282        f.run(&[b"HSET", b"h", b"a", b"1"]);
3283        assert_eq!(
3284            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
3285            "*1\r\n:0\r\n",
3286            "XX on a field with no deadline changes nothing"
3287        );
3288        assert_eq!(
3289            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
3290            "*1\r\n:1\r\n"
3291        );
3292        assert_eq!(
3293            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
3294            "*1\r\n:0\r\n",
3295            "and NX will not move one that is already there"
3296        );
3297        assert_eq!(
3298            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
3299            "*1\r\n:0\r\n"
3300        );
3301        assert_eq!(
3302            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
3303            "*1\r\n:1\r\n"
3304        );
3305        assert_eq!(
3306            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
3307            "*1\r\n:1\r\n"
3308        );
3309        assert_eq!(
3310            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3311            "*1\r\n:50\r\n"
3312        );
3313    }
3314
3315    #[test]
3316    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
3317        let mut f = Fixture::new();
3318        f.run(&[b"HSET", b"h", b"a", b"1"]);
3319        for (bad, want) in [
3320            (
3321                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
3322                "-ERR invalid expire time, must be >= 0",
3323            ),
3324            (
3325                &[
3326                    b"HEXPIRE".as_slice(),
3327                    b"h",
3328                    b"9999999999999999",
3329                    b"FIELDS",
3330                    b"1",
3331                    b"a",
3332                ][..],
3333                "-ERR invalid expire time in 'hexpire' command",
3334            ),
3335            (
3336                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
3337                "-ERR wrong number of arguments for 'hexpire' command",
3338            ),
3339            (
3340                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
3341                "-ERR Parameter `numFields` should be greater than 0",
3342            ),
3343            (
3344                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
3345                "-ERR wrong number of arguments",
3346            ),
3347            (
3348                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
3349                "-ERR wrong number of arguments",
3350            ),
3351        ] {
3352            let reply = f.run(bad);
3353            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
3354            assert!(!reply.contains('*'), "an array header went out in front");
3355        }
3356        assert_eq!(
3357            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3358            "*1\r\n:-1\r\n",
3359            "and not one of them put a deadline on anything"
3360        );
3361    }
3362
3363    #[test]
3364    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
3365        let mut f = Fixture::new();
3366        f.run(&[b"SET", b"str", b"v"]);
3367        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3368
3369        for cmd in [
3370            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
3371            &[
3372                b"HPEXPIRE".as_slice(),
3373                b"str",
3374                b"100",
3375                b"FIELDS",
3376                b"1",
3377                b"f",
3378            ][..],
3379            &[
3380                b"HEXPIREAT".as_slice(),
3381                b"str",
3382                b"9999999999",
3383                b"FIELDS",
3384                b"1",
3385                b"f",
3386            ][..],
3387            &[
3388                b"HPEXPIREAT".as_slice(),
3389                b"str",
3390                b"9999999999999",
3391                b"FIELDS",
3392                b"1",
3393                b"f",
3394            ][..],
3395            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3396            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3397            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3398            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3399            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3400        ] {
3401            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
3402        }
3403        assert_eq!(
3404            f.run(&[b"GET", b"str"]),
3405            "$1\r\nv\r\n",
3406            "and none of them touched the value"
3407        );
3408    }
3409
3410    #[test]
3411    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
3412        let mut f = Fixture::new();
3413        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3414        assert_eq!(
3415            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
3416            "*2\r\n$1\r\n1\r\n$-1\r\n",
3417            "positional, so the field that was not there is a nil in its place"
3418        );
3419        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3420        assert_eq!(
3421            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
3422            "*1\r\n$-1\r\n"
3423        );
3424        assert_eq!(
3425            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
3426            "*1\r\n$1\r\n2\r\n"
3427        );
3428        assert_eq!(
3429            f.run(&[b"EXISTS", b"h"]),
3430            ":0\r\n",
3431            "and the last field took the key"
3432        );
3433    }
3434
3435    #[test]
3436    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
3437        let mut f = Fixture::new();
3438        f.run(&[b"HSET", b"h", b"a", b"1"]);
3439        assert_eq!(
3440            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
3441            "*1\r\n$1\r\n1\r\n"
3442        );
3443        assert_eq!(
3444            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3445            "*1\r\n:-1\r\n",
3446            "no option means leave it alone, which is the one place this is not GETEX"
3447        );
3448
3449        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
3450        assert_eq!(
3451            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3452            "*1\r\n:100\r\n"
3453        );
3454        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
3455        assert_eq!(
3456            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3457            "*1\r\n:100\r\n",
3458            "and a plain read really does leave it alone"
3459        );
3460        assert_eq!(
3461            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
3462            "*1\r\n$1\r\n1\r\n"
3463        );
3464        assert_eq!(
3465            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3466            "*1\r\n:-1\r\n"
3467        );
3468
3469        assert_eq!(
3470            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
3471            "*1\r\n$1\r\n1\r\n",
3472            "the value goes out before the deadline that has already gone is applied"
3473        );
3474        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
3475        assert_eq!(
3476            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
3477            "*1\r\n$-1\r\n"
3478        );
3479    }
3480
3481    #[test]
3482    fn hsetex_writes_all_of_it_or_none_of_it() {
3483        let mut f = Fixture::new();
3484        assert_eq!(
3485            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
3486            ":1\r\n"
3487        );
3488        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3489        assert_eq!(
3490            f.run(&[
3491                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
3492            ]),
3493            ":0\r\n",
3494            "FNX wants every field named to be missing"
3495        );
3496        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3497        assert_eq!(
3498            f.run(&[b"HEXISTS", b"h", b"new"]),
3499            ":0\r\n",
3500            "and none of the list was written"
3501        );
3502        assert_eq!(
3503            f.run(&[
3504                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
3505            ]),
3506            ":0\r\n",
3507            "and FXX wants every one of them to be there"
3508        );
3509        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3510        assert_eq!(
3511            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
3512            ":1\r\n"
3513        );
3514        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
3515
3516        assert_eq!(
3517            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
3518            ":0\r\n"
3519        );
3520        assert_eq!(
3521            f.run(&[b"EXISTS", b"gone"]),
3522            ":0\r\n",
3523            "a key with no fields cannot meet FXX and is not created trying"
3524        );
3525    }
3526
3527    #[test]
3528    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
3529        let mut f = Fixture::new();
3530        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
3531        assert_eq!(
3532            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3533            "*1\r\n:100\r\n"
3534        );
3535
3536        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
3537        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
3538        assert_eq!(
3539            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3540            "*1\r\n:100\r\n",
3541            "KEEPTTL put back what the write cleared"
3542        );
3543
3544        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
3545        assert_eq!(
3546            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3547            "*1\r\n:-1\r\n",
3548            "and without it a write clears the deadline the way HSET does"
3549        );
3550
3551        // Any order, because Redis reads these in a loop and not in a fixed
3552        // sequence.
3553        assert_eq!(
3554            f.run(&[
3555                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
3556            ]),
3557            ":1\r\n"
3558        );
3559        assert_eq!(
3560            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3561            "*1\r\n:100\r\n"
3562        );
3563
3564        assert_eq!(
3565            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
3566            ":1\r\n",
3567            "written, and not the separate code the HEXPIRE family has for this"
3568        );
3569        assert_eq!(
3570            f.run(&[b"EXISTS", b"h"]),
3571            ":0\r\n",
3572            "and storing it and then removing it emptied the hash"
3573        );
3574    }
3575
3576    #[test]
3577    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
3578        let mut f = Fixture::new();
3579        f.run(&[b"HSET", b"h", b"a", b"1"]);
3580        for (bad, want) in [
3581            // HGETDEL has three sentences of its own for these three mistakes.
3582            (
3583                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
3584                "-ERR Number of fields must be a positive integer",
3585            ),
3586            (
3587                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
3588                "-ERR The `numfields` parameter must match the number of arguments",
3589            ),
3590            (
3591                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
3592                "-ERR Mandatory argument FIELDS is missing or not at the right position",
3593            ),
3594            // And HGETEX and HSETEX have three different ones between them.
3595            (
3596                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
3597                "-ERR invalid number of fields",
3598            ),
3599            (
3600                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
3601                "-ERR wrong number of arguments",
3602            ),
3603            (
3604                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
3605                "-ERR unknown argument: FIELD",
3606            ),
3607            (
3608                &[
3609                    b"HGETEX".as_slice(),
3610                    b"h",
3611                    b"KEEPTTL",
3612                    b"FIELDS",
3613                    b"1",
3614                    b"a",
3615                ][..],
3616                "-ERR unknown argument: KEEPTTL",
3617            ),
3618            (
3619                &[
3620                    b"HGETEX".as_slice(),
3621                    b"h",
3622                    b"EX",
3623                    b"100",
3624                    b"PERSIST",
3625                    b"FIELDS",
3626                    b"1",
3627                    b"a",
3628                ][..],
3629                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
3630            ),
3631            (
3632                &[
3633                    b"HSETEX".as_slice(),
3634                    b"h",
3635                    b"EX",
3636                    b"1",
3637                    b"KEEPTTL",
3638                    b"FIELDS",
3639                    b"1",
3640                    b"a",
3641                    b"1",
3642                ][..],
3643                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
3644            ),
3645            (
3646                &[
3647                    b"HSETEX".as_slice(),
3648                    b"h",
3649                    b"FNX",
3650                    b"FXX",
3651                    b"FIELDS",
3652                    b"1",
3653                    b"a",
3654                    b"1",
3655                ][..],
3656                "-ERR Only one of FXX or FNX arguments can be specified",
3657            ),
3658            (
3659                &[
3660                    b"HSETEX".as_slice(),
3661                    b"h",
3662                    b"FIELDS",
3663                    b"2",
3664                    b"a",
3665                    b"1",
3666                    b"b",
3667                ][..],
3668                "-ERR wrong number of arguments",
3669            ),
3670            (
3671                &[
3672                    b"HGETEX".as_slice(),
3673                    b"h",
3674                    b"EX",
3675                    b"-1",
3676                    b"FIELDS",
3677                    b"1",
3678                    b"a",
3679                ][..],
3680                "-ERR invalid expire time, must be >= 0",
3681            ),
3682            (
3683                &[
3684                    b"HGETEX".as_slice(),
3685                    b"h",
3686                    b"PXAT",
3687                    b"99999999999999",
3688                    b"FIELDS",
3689                    b"1",
3690                    b"a",
3691                ][..],
3692                "-ERR invalid expire time in 'hgetex' command",
3693            ),
3694            (
3695                &[
3696                    b"HSETEX".as_slice(),
3697                    b"h",
3698                    b"EX",
3699                    b"abc",
3700                    b"FIELDS",
3701                    b"1",
3702                    b"a",
3703                    b"1",
3704                ][..],
3705                "-ERR value is not an integer or out of range",
3706            ),
3707        ] {
3708            let reply = f.run(bad);
3709            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
3710            assert!(!reply.contains('*'), "an array header went out in front");
3711        }
3712        assert_eq!(
3713            f.run(&[b"HGET", b"h", b"a"]),
3714            "$1\r\n1\r\n",
3715            "and not one of them wrote anything"
3716        );
3717        assert_eq!(
3718            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3719            "*1\r\n:-1\r\n"
3720        );
3721    }
3722
3723    #[test]
3724    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
3725        let mut f = Fixture::new();
3726        f.run(&[b"SET", b"str", b"v"]);
3727        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3728        for cmd in [
3729            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3730            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
3731            &[
3732                b"HGETEX".as_slice(),
3733                b"str",
3734                b"EX",
3735                b"100",
3736                b"FIELDS",
3737                b"1",
3738                b"f",
3739            ][..],
3740            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
3741        ] {
3742            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
3743        }
3744        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
3745    }
3746
3747    /// The one integer of a single element array reply.
3748    /// The number out of a plain integer reply.
3749    ///
3750    /// [`int_reply`] is the same thing wrapped in a one element array, which is
3751    /// the shape every hash field command answers in.
3752    fn int(reply: &str) -> i64 {
3753        let body = reply
3754            .strip_prefix(':')
3755            .and_then(|s| s.strip_suffix("\r\n"))
3756            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
3757        body.parse().expect("an integer")
3758    }
3759
3760    fn int_reply(reply: &str) -> i64 {
3761        let body = reply
3762            .strip_prefix("*1\r\n:")
3763            .and_then(|s| s.strip_suffix("\r\n"))
3764            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
3765        body.parse().expect("an integer")
3766    }
3767
3768    /// The cursor and the flat items of a scan reply.
3769    fn scan_reply(reply: &str) -> (String, Vec<String>) {
3770        let mut lines = reply.split("\r\n");
3771        assert_eq!(lines.next(), Some("*2"), "got {reply}");
3772        lines.next().expect("the cursor header");
3773        let cursor = lines.next().expect("a cursor").to_owned();
3774        let header = lines.next().expect("an item count");
3775        let n: usize = header[1..].parse().expect("a count");
3776        let mut items = Vec::with_capacity(n);
3777        for _ in 0..n {
3778            lines.next().expect("an item header");
3779            items.push(lines.next().expect("an item").to_owned());
3780        }
3781        (cursor, items)
3782    }
3783
3784    /// The members of a set reply, sorted, since none of these promise an
3785    /// order and a test that asserted one would be asserting an accident.
3786    fn sorted(reply: &str) -> Vec<String> {
3787        let mut lines = reply.split("\r\n");
3788        let header = lines.next().expect("a header");
3789        assert!(
3790            header.starts_with('*') || header.starts_with('~'),
3791            "got {reply}"
3792        );
3793        let n: usize = header[1..].parse().expect("a member count");
3794        let mut got = Vec::with_capacity(n);
3795        for _ in 0..n {
3796            lines.next().expect("a member header");
3797            got.push(lines.next().expect("a member").to_owned());
3798        }
3799        got.sort();
3800        got
3801    }
3802
3803    #[test]
3804    fn the_algebra_answers_what_the_sets_share_and_do_not() {
3805        let mut f = Fixture::new();
3806        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
3807        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
3808        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
3809
3810        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
3811        assert_eq!(
3812            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
3813            ["1", "2", "3", "4", "5"]
3814        );
3815        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
3816        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
3817
3818        // A key that is not there is an empty set, which empties an
3819        // intersection and does nothing at all to a union.
3820        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
3821        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
3822        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
3823        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
3824    }
3825
3826    #[test]
3827    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
3828        let mut f = Fixture::new();
3829        f.run(&[b"SADD", b"a", b"x"]);
3830        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
3831        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
3832        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
3833
3834        f.run(&[b"HELLO", b"3"]);
3835        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
3836        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
3837        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
3838        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
3839    }
3840
3841    #[test]
3842    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
3843        let mut f = Fixture::new();
3844        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
3845        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
3846
3847        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
3848        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
3849        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
3850        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
3851        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
3852        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
3853
3854        // An empty answer deletes the destination rather than leaving an empty
3855        // set behind, and the destination may be one of the sources.
3856        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
3857        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
3858        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
3859        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
3860
3861        // And a destination holding something else is overwritten, the same way
3862        // SET overwrites, rather than refused.
3863        f.run(&[b"SET", b"str", b"v"]);
3864        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
3865        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
3866    }
3867
3868    #[test]
3869    fn sintercard_counts_without_building_and_stops_at_a_limit() {
3870        let mut f = Fixture::new();
3871        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
3872        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
3873
3874        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
3875        assert_eq!(
3876            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
3877            ":2\r\n"
3878        );
3879        assert_eq!(
3880            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
3881            ":3\r\n",
3882            "a limit of zero is no limit"
3883        );
3884        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
3885        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
3886
3887        // The counted keys are what make its three error messages its own.
3888        assert_eq!(
3889            f.run(&[b"SINTERCARD", b"0", b"a"]),
3890            "-ERR numkeys should be greater than 0\r\n"
3891        );
3892        assert_eq!(
3893            f.run(&[b"SINTERCARD", b"abc", b"a"]),
3894            "-ERR numkeys should be greater than 0\r\n"
3895        );
3896        assert_eq!(
3897            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
3898            "-ERR Number of keys can't be greater than number of args\r\n"
3899        );
3900        assert_eq!(
3901            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
3902            "-ERR LIMIT can't be negative\r\n"
3903        );
3904        assert_eq!(
3905            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
3906            "-ERR syntax error\r\n"
3907        );
3908        // A key really can be called LIMIT, which is why the count exists.
3909        f.run(&[b"SADD", b"LIMIT", b"2"]);
3910        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
3911    }
3912
3913    #[test]
3914    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
3915        let mut f = Fixture::new();
3916        f.run(&[b"SADD", b"a", b"1"]);
3917        f.run(&[b"SADD", b"d", b"old"]);
3918        f.run(&[b"SET", b"str", b"v"]);
3919
3920        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3921        for bad in [
3922            &[b"SINTER".as_slice(), b"a", b"str"][..],
3923            &[b"SUNION".as_slice(), b"str"][..],
3924            &[b"SDIFF".as_slice(), b"a", b"str"][..],
3925            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
3926            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
3927            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
3928            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
3929        ] {
3930            let reply = f.run(bad);
3931            assert_eq!(reply, wrong, "for {:?}", bad[0]);
3932        }
3933        assert_eq!(
3934            f.run(&[b"SMEMBERS", b"d"]),
3935            "*1\r\n$3\r\nold\r\n",
3936            "and the destination was left alone every time"
3937        );
3938    }
3939
3940    /// The leak a set can spring that nothing on the wire would ever show: the
3941    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
3942    #[test]
3943    fn churning_sets_does_not_grow_the_server() {
3944        let mut f = Fixture::new();
3945        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
3946        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
3947            .chain(std::iter::once(&b"s"[..]))
3948            .chain(members.iter().map(Vec::as_slice))
3949            .collect();
3950
3951        f.run(&args);
3952        f.run(&[b"DEL", b"s"]);
3953        f.server.compact_step();
3954        let after_first = f.server.memory_bytes();
3955
3956        for _ in 0..200 {
3957            f.run(&args);
3958            f.run(&[b"DEL", b"s"]);
3959            f.server.compact_step();
3960        }
3961        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3962        assert!(
3963            f.server.memory_bytes() <= after_first * 2,
3964            "held {} after two hundred passes against {after_first} after one",
3965            f.server.memory_bytes()
3966        );
3967    }
3968
3969    /// A RESP2 array of bulk strings, which is what most of the list replies
3970    /// are and what writing them out by hand in every assertion looks like.
3971    fn bulks(parts: &[&str]) -> String {
3972        let mut s = format!("*{}\r\n", parts.len());
3973        for p in parts {
3974            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
3975        }
3976        s
3977    }
3978
3979    #[test]
3980    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
3981        let mut f = Fixture::new();
3982        // Each element in turn goes at the head, so the last one sent is at the
3983        // front when it is over. That reads like a bug in the client and it is
3984        // what every Redis has always done.
3985        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
3986        assert_eq!(
3987            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
3988            bulks(&["c", "b", "a"])
3989        );
3990        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
3991        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
3992        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
3993        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
3994        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
3995        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
3996    }
3997
3998    #[test]
3999    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
4000        let mut f = Fixture::new();
4001        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
4002        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
4003        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4004        f.run(&[b"RPUSH", b"k", b"a"]);
4005        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
4006        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
4007        assert_eq!(
4008            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4009            bulks(&["z", "a", "y"])
4010        );
4011    }
4012
4013    /// The four ways a pop can come back with nothing, which are three
4014    /// different replies and a RESP2 client can tell all of them apart.
4015    #[test]
4016    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
4017        let mut f = Fixture::new();
4018        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
4019        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
4020        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
4021        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
4022        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4023        // A count of zero against a list that is there is an empty array and
4024        // not a null array, which is the fourth answer.
4025        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
4026        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
4027        // More than there is takes what there is and the key goes with it.
4028        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
4029        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4030    }
4031
4032    #[test]
4033    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
4034        let mut f = Fixture::new();
4035        f.run(&[b"RPUSH", b"k", b"a"]);
4036        let range = "-ERR value is out of range, must be positive\r\n";
4037        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
4038        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
4039        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
4040        // Redis calls this an arity error and not a syntax error, which is a
4041        // distinction it does not always make.
4042        assert_eq!(
4043            f.run(&[b"LPOP", b"k", b"1", b"2"]),
4044            "-ERR wrong number of arguments for 'lpop' command\r\n"
4045        );
4046        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4047    }
4048
4049    #[test]
4050    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
4051        let mut f = Fixture::new();
4052        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4053        assert_eq!(
4054            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4055            bulks(&["a", "b", "c"])
4056        );
4057        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
4058        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
4059        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
4060        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
4061        assert_eq!(
4062            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
4063            bulks(&["a", "b", "c"])
4064        );
4065        // A key that is not there is an empty range and not a nil, which is the
4066        // one place a list disagrees with a set.
4067        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
4068        assert_eq!(
4069            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
4070            "-ERR value is not an integer or out of range\r\n"
4071        );
4072    }
4073
4074    #[test]
4075    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
4076        let mut f = Fixture::new();
4077        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4078        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
4079        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
4080        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
4081        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
4082        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
4083        assert_eq!(
4084            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4085            bulks(&["a", "b", "z"])
4086        );
4087        // Both ways of missing are errors here rather than a nil, because a
4088        // list is never empty and there is nothing else the reply could be.
4089        assert_eq!(
4090            f.run(&[b"LSET", b"k", b"99", b"z"]),
4091            "-ERR index out of range\r\n"
4092        );
4093        assert_eq!(
4094            f.run(&[b"LSET", b"nope", b"0", b"z"]),
4095            "-ERR no such key\r\n"
4096        );
4097    }
4098
4099    #[test]
4100    fn linsert_says_three_things_with_one_signed_number() {
4101        let mut f = Fixture::new();
4102        // Zero for a key that is not there, which is not the same as minus one
4103        // for a pivot that is not in a list that is.
4104        assert_eq!(
4105            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
4106            ":0\r\n"
4107        );
4108        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4109        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
4110        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
4111        assert_eq!(
4112            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4113            bulks(&["X", "a", "b", "Y"])
4114        );
4115        assert_eq!(
4116            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
4117            ":-1\r\n"
4118        );
4119        assert_eq!(
4120            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
4121            "-ERR syntax error\r\n"
4122        );
4123    }
4124
4125    #[test]
4126    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
4127        let mut f = Fixture::new();
4128        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
4129        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
4130        assert_eq!(
4131            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4132            bulks(&["b", "c", "a"])
4133        );
4134        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
4135        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4136        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
4137        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
4138        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4139        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
4140    }
4141
4142    #[test]
4143    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
4144        let mut f = Fixture::new();
4145        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
4146        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
4147        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4148        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
4149        // leave `EXISTS` answering zero rather than leaving an empty one.
4150        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
4151        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4152        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
4153    }
4154
4155    #[test]
4156    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
4157        let mut f = Fixture::new();
4158        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
4159        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
4160        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
4161        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
4162        assert_eq!(
4163            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
4164            "*2\r\n:0\r\n:3\r\n"
4165        );
4166        assert_eq!(
4167            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
4168            "*3\r\n:6\r\n:3\r\n:0\r\n"
4169        );
4170        // MAXLEN counts elements looked at and not matches found, so three
4171        // stops after `a b c` and finds the one match in it.
4172        assert_eq!(
4173            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
4174            "*1\r\n:0\r\n"
4175        );
4176        // Nothing found is three different replies depending on how it was
4177        // asked and whether the key is there at all.
4178        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
4179        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
4180        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
4181        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
4182    }
4183
4184    #[test]
4185    fn lpos_words_its_three_mistakes_the_way_redis_does() {
4186        let mut f = Fixture::new();
4187        f.run(&[b"RPUSH", b"p", b"a"]);
4188        // The whole sentence and not a prefix, because the older wording of it
4189        // is still all over the internet and clients match on the text.
4190        assert_eq!(
4191            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
4192            "-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"
4193        );
4194        assert_eq!(
4195            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
4196            "-ERR COUNT can't be negative\r\n"
4197        );
4198        assert_eq!(
4199            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
4200            "-ERR MAXLEN can't be negative\r\n"
4201        );
4202        assert_eq!(
4203            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
4204            "-ERR syntax error\r\n"
4205        );
4206        assert_eq!(
4207            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
4208            "-ERR syntax error\r\n"
4209        );
4210    }
4211
4212    #[test]
4213    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
4214        let mut f = Fixture::new();
4215        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4216        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
4217        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4218        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
4219        assert_eq!(
4220            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
4221            "$1\r\na\r\n"
4222        );
4223        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
4224        // The same key twice is the documented way to rotate a list and falls
4225        // out of taking the element before deciding where to put it.
4226        f.run(&[b"DEL", b"r"]);
4227        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
4228        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
4229        assert_eq!(
4230            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
4231            bulks(&["3", "1", "2"])
4232        );
4233        assert_eq!(
4234            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
4235            "$-1\r\n"
4236        );
4237        assert_eq!(
4238            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
4239            "-ERR syntax error\r\n"
4240        );
4241    }
4242
4243    #[test]
4244    fn a_move_checks_the_destination_before_it_takes_anything() {
4245        let mut f = Fixture::new();
4246        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4247        f.run(&[b"SET", b"str", b"v"]);
4248        assert_eq!(
4249            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
4250            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
4251        );
4252        // The element is still where it was, rather than having gone nowhere.
4253        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4254    }
4255
4256    #[test]
4257    fn lmpop_answers_from_the_first_key_that_has_anything() {
4258        let mut f = Fixture::new();
4259        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
4260        // The name of the key that answered comes back with the elements,
4261        // because the client cannot work out which one it was.
4262        assert_eq!(
4263            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
4264            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
4265        );
4266        assert_eq!(
4267            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
4268            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
4269        );
4270        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
4271        // A null array and not a null, even though what it stands in for is an
4272        // array holding a key name and then another array.
4273        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
4274    }
4275
4276    #[test]
4277    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
4278        let mut f = Fixture::new();
4279        f.run(&[b"RPUSH", b"k", b"a"]);
4280        assert_eq!(
4281            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
4282            "-ERR numkeys should be greater than 0\r\n"
4283        );
4284        assert_eq!(
4285            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
4286            "-ERR numkeys should be greater than 0\r\n"
4287        );
4288        assert_eq!(
4289            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
4290            "-ERR count should be greater than 0\r\n"
4291        );
4292        // A key count that eats the direction is a syntax error and not a
4293        // sentence about key counts, because the direction is simply not there.
4294        assert_eq!(
4295            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
4296            "-ERR syntax error\r\n"
4297        );
4298        assert_eq!(
4299            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
4300            "-ERR syntax error\r\n"
4301        );
4302        assert_eq!(
4303            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
4304            "-ERR syntax error\r\n"
4305        );
4306        assert_eq!(
4307            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
4308            "-ERR syntax error\r\n"
4309        );
4310        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4311    }
4312
4313    #[test]
4314    fn every_list_command_says_wrongtype_and_writes_nothing() {
4315        let mut f = Fixture::new();
4316        f.run(&[b"SET", b"str", b"v"]);
4317        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4318        for cmd in [
4319            &[b"LPUSH".as_slice(), b"str", b"a"][..],
4320            &[b"RPUSH", b"str", b"a"],
4321            &[b"LPUSHX", b"str", b"a"],
4322            &[b"RPUSHX", b"str", b"a"],
4323            &[b"LPOP", b"str"],
4324            &[b"LPOP", b"str", b"2"],
4325            &[b"RPOP", b"str"],
4326            &[b"LLEN", b"str"],
4327            &[b"LRANGE", b"str", b"0", b"-1"],
4328            &[b"LINDEX", b"str", b"0"],
4329            &[b"LSET", b"str", b"0", b"a"],
4330            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
4331            &[b"LREM", b"str", b"0", b"a"],
4332            &[b"LTRIM", b"str", b"0", b"-1"],
4333            &[b"LPOS", b"str", b"a"],
4334            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
4335            &[b"RPOPLPUSH", b"str", b"d"],
4336            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
4337            &[b"LMPOP", b"1", b"str", b"LEFT"],
4338        ] {
4339            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
4340        }
4341        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
4342        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4343    }
4344
4345    /// A timeout is not an integer and it is not an ordinary float either: the
4346    /// three sentences it can answer with are its own, and which one a given
4347    /// argument gets is not what reading the code would suggest.
4348    #[test]
4349    fn a_timeout_has_three_ways_of_being_wrong() {
4350        let mut f = Fixture::new();
4351        let not_float = "-ERR timeout is not a float or out of range\r\n";
4352        let range = "-ERR timeout is out of range\r\n";
4353        for (bad, want) in [
4354            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
4355            (&[b"BLPOP", b"k", b"nan"], not_float),
4356            (&[b"BLPOP", b"k", b""], not_float),
4357            // Whitespace on either side, which `strtold` would take and Redis
4358            // does not.
4359            (&[b"BLPOP", b"k", b" 1"], not_float),
4360            (&[b"BLPOP", b"k", b"1 "], not_float),
4361            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
4362            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
4363            // These three parse, so they are not the not-a-float error, and all
4364            // three are further off than an i64 of milliseconds reaches.
4365            (&[b"BLPOP", b"k", b"1e400"], range),
4366            (&[b"BLPOP", b"k", b"inf"], range),
4367            (&[b"BLPOP", b"k", b"9999999999999999"], range),
4368            (&[b"BRPOP", b"k", b"abc"], not_float),
4369            (
4370                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
4371                not_float,
4372            ),
4373            (
4374                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
4375                "-ERR timeout is negative\r\n",
4376            ),
4377            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
4378        ] {
4379            assert_eq!(f.run(bad), want, "for {bad:?}");
4380        }
4381    }
4382
4383    /// A timeout of exactly zero means no timeout, and there are two ways of
4384    /// writing exactly zero.
4385    #[test]
4386    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
4387        let mut f = Fixture::new();
4388        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
4389            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
4390            assert_eq!(flow, Flow::Block, "for {timeout:?}");
4391            assert!(out.is_empty(), "for {timeout:?}");
4392        }
4393        // Positive, so it is a real deadline, and the deadline is this
4394        // millisecond. Nothing is written here either: the reply comes from the
4395        // sweep, which is the engine's and not this layer's.
4396        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
4397        assert_eq!(flow, Flow::Block);
4398        assert!(out.is_empty());
4399    }
4400
4401    #[test]
4402    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
4403        let mut f = Fixture::new();
4404        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
4405
4406        // The one difference from LPOP: the reply names the key that answered,
4407        // which is what makes BLPOP over several keys usable.
4408        assert_eq!(
4409            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
4410            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
4411        );
4412        assert_eq!(
4413            f.run(&[b"BRPOP", b"L", b"0"]),
4414            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
4415        );
4416        assert_eq!(
4417            f.run(&[
4418                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
4419            ]),
4420            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4421        );
4422        assert_eq!(
4423            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
4424            "$1\r\nd\r\n"
4425        );
4426        assert_eq!(
4427            f.run(&[b"EXISTS", b"L"]),
4428            ":0\r\n",
4429            "and the key went with it"
4430        );
4431        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
4432        // Onto itself, which is how a list is rotated and is a real thing to ask
4433        // a blocking move for.
4434        f.run(&[b"RPUSH", b"D", b"x"]);
4435        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
4436        assert_eq!(
4437            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
4438            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
4439        );
4440    }
4441
4442    #[test]
4443    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
4444        let mut f = Fixture::new();
4445        f.run(&[b"RPUSH", b"k", b"a"]);
4446        for (bad, want) in [
4447            (
4448                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
4449                "-ERR numkeys should be greater than 0\r\n",
4450            ),
4451            (
4452                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
4453                "-ERR numkeys should be greater than 0\r\n",
4454            ),
4455            // Two keys named and one given, so the word that should have been
4456            // the direction is a key and there is no direction left.
4457            (
4458                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
4459                "-ERR syntax error\r\n",
4460            ),
4461            (
4462                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
4463                "-ERR syntax error\r\n",
4464            ),
4465            (
4466                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
4467                "-ERR syntax error\r\n",
4468            ),
4469            (
4470                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
4471                "-ERR syntax error\r\n",
4472            ),
4473            // A count that is not a number at all gets the same sentence a zero
4474            // or a negative one gets, rather than the usual one about integers.
4475            (
4476                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
4477                "-ERR count should be greater than 0\r\n",
4478            ),
4479            (
4480                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
4481                "-ERR count should be greater than 0\r\n",
4482            ),
4483        ] {
4484            assert_eq!(f.run(bad), want, "for {bad:?}");
4485        }
4486        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
4487    }
4488
4489    #[test]
4490    fn a_blocking_move_reads_its_directions_before_its_timeout() {
4491        let mut f = Fixture::new();
4492        // Both are wrong. Redis checks the directions first, so this is the
4493        // syntax error and not a complaint about the timeout.
4494        assert_eq!(
4495            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
4496            "-ERR syntax error\r\n"
4497        );
4498        assert_eq!(
4499            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
4500            "-ERR syntax error\r\n"
4501        );
4502    }
4503
4504    /// The four ways a blocking command sees a key of another type, and the one
4505    /// way it does not.
4506    #[test]
4507    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
4508        let mut f = Fixture::new();
4509        f.run(&[b"SET", b"S", b"v"]);
4510        f.run(&[b"RPUSH", b"D", b"x"]);
4511        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4512
4513        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
4514        // Every key is checked even when an earlier one would have blocked, so
4515        // an empty key in front of a string does not hide it.
4516        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
4517        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
4518        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
4519        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
4520        // The destination, which is only reached because the source has
4521        // something in it.
4522        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
4523        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
4524
4525        // And the one that does not: an empty source means the destination is
4526        // never looked at, so this waits rather than erroring, and on a real
4527        // server it times out.
4528        assert_eq!(
4529            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
4530                .0,
4531            Flow::Block
4532        );
4533    }
4534
4535    /// The same churn the set and the string get, because a list that leaks a
4536    /// chunk per push looks exactly like one that does not until it has run for
4537    /// an afternoon.
4538    #[test]
4539    fn churning_lists_does_not_grow_the_server() {
4540        let mut f = Fixture::new();
4541        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
4542        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
4543            .into_iter()
4544            .chain(vals.iter().map(Vec::as_slice))
4545            .collect();
4546
4547        f.run(&args);
4548        f.run(&[b"DEL", b"k"]);
4549        f.server.compact_step();
4550        let after_first = f.server.memory_bytes();
4551
4552        for _ in 0..200 {
4553            f.run(&args);
4554            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
4555            f.server.compact_step();
4556        }
4557        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4558        assert!(
4559            f.server.memory_bytes() <= after_first * 2,
4560            "held {} after two hundred passes against {after_first} after one",
4561            f.server.memory_bytes()
4562        );
4563    }
4564
4565    // ------------------------------------------------------------ sorted set
4566
4567    #[test]
4568    fn a_sorted_set_takes_scores_and_gives_them_back() {
4569        let mut f = Fixture::new();
4570        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
4571        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
4572        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
4573        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
4574        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
4575        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
4576        assert_eq!(
4577            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
4578            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
4579        );
4580        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
4581        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
4582        // The key goes when the last member does.
4583        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
4584        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4585    }
4586
4587    #[test]
4588    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
4589        let mut f = Fixture::new();
4590        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
4591        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
4592        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
4593        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
4594
4595        f.out = Out::new(Proto::Resp3);
4596        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
4597        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
4598        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
4599        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
4600    }
4601
4602    #[test]
4603    fn the_zadd_options_gate_what_gets_written() {
4604        let mut f = Fixture::new();
4605        f.run(&[b"ZADD", b"z", b"5", b"a"]);
4606        // NX leaves a member that is there alone, XX will not create one.
4607        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
4608        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
4609        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
4610        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
4611        // GT and LT only move a score one way.
4612        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
4613        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
4614        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
4615        // CH counts a moved score and plain ZADD does not.
4616        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
4617        assert_eq!(
4618            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
4619            ":2\r\n"
4620        );
4621    }
4622
4623    #[test]
4624    fn zadd_incr_answers_a_score_or_nothing_at_all() {
4625        let mut f = Fixture::new();
4626        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
4627        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
4628        // A gate that refuses is the string nil, because the reply it stands in
4629        // for is a score.
4630        assert_eq!(
4631            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
4632            "$-1\r\n"
4633        );
4634        assert_eq!(
4635            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
4636            "$-1\r\n"
4637        );
4638        assert_eq!(
4639            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
4640            "$-1\r\n"
4641        );
4642        assert_eq!(
4643            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
4644            "$1\r\n8\r\n"
4645        );
4646        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
4647        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
4648    }
4649
4650    #[test]
4651    fn the_two_infinities_will_not_be_added_together() {
4652        let mut f = Fixture::new();
4653        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
4654        let nan = "-ERR resulting score is not a number (NaN)\r\n";
4655        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
4656        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
4657        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
4658        // And a key made for an increment that then fails does not stay behind.
4659        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
4660    }
4661
4662    #[test]
4663    fn zadd_says_its_mistakes_the_way_redis_says_them() {
4664        let mut f = Fixture::new();
4665        // The pairs are counted before the options are looked at, so this is a
4666        // syntax error about having none and not a complaint about NX and XX.
4667        assert_eq!(
4668            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
4669            "-ERR syntax error\r\n"
4670        );
4671        assert_eq!(
4672            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
4673            "-ERR XX and NX options at the same time are not compatible\r\n"
4674        );
4675        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
4676        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
4677        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
4678        assert_eq!(
4679            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
4680            "-ERR INCR option supports a single increment-element pair\r\n"
4681        );
4682        // An odd number of arguments after the options.
4683        assert_eq!(
4684            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
4685            "-ERR syntax error\r\n"
4686        );
4687        // Every score is read before the first is stored.
4688        assert_eq!(
4689            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
4690            "-ERR value is not a valid float\r\n"
4691        );
4692        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
4693    }
4694
4695    #[test]
4696    fn a_rank_says_where_a_member_sits_from_either_end() {
4697        let mut f = Fixture::new();
4698        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4699        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
4700        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
4701        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
4702        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
4703        // WITHSCORE changes both shapes: the answer and the nothing.
4704        assert_eq!(
4705            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
4706            "*2\r\n:1\r\n$1\r\n2\r\n"
4707        );
4708        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
4709        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
4710        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
4711        // A bad option is a syntax error and one argument too many is an arity
4712        // error, which is Redis's split.
4713        assert_eq!(
4714            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
4715            "-ERR syntax error\r\n"
4716        );
4717        assert_eq!(
4718            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
4719            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
4720        );
4721    }
4722
4723    #[test]
4724    fn the_two_counts_read_their_two_kinds_of_bound() {
4725        let mut f = Fixture::new();
4726        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4727        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
4728        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
4729        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
4730        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
4731        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
4732        assert_eq!(
4733            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
4734            "-ERR min or max is not a float\r\n"
4735        );
4736
4737        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
4738        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
4739        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
4740        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
4741        // A bare member is not a bound, because a member can start with any
4742        // byte and there would be no way to say the bracket if it were optional.
4743        assert_eq!(
4744            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
4745            "-ERR min or max not valid string range item\r\n"
4746        );
4747    }
4748
4749    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
4750    ///
4751    /// Every byte in here was read off a real 8.10.1 rather than worked out,
4752    /// because the interesting part of this command is not what it selects, it
4753    /// is which of the two ends the client is expected to name first.
4754    #[test]
4755    fn one_range_command_selects_by_rank_or_score_or_name() {
4756        let mut f = Fixture::new();
4757        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4758        assert_eq!(
4759            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
4760            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4761        );
4762        assert_eq!(
4763            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
4764            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4765        );
4766        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
4767        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
4768        // REV over ranks reverses the walk and leaves the two arguments alone,
4769        // because a rank counts from the end the walk starts at.
4770        assert_eq!(
4771            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
4772            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4773        );
4774        assert_eq!(
4775            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
4776            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4777        );
4778        // And REV over scores does swap them, since a bound does not count from
4779        // anywhere. This is the one line of the parse that tells the two apart.
4780        assert_eq!(
4781            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
4782            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
4783        );
4784        assert_eq!(
4785            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
4786            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4787        );
4788        assert_eq!(
4789            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
4790            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4791        );
4792    }
4793
4794    /// The older spellings, which are the same six windows with the mode in the
4795    /// name and the high end named first on the three that go backwards.
4796    #[test]
4797    fn the_older_range_spellings_name_their_high_end_first() {
4798        let mut f = Fixture::new();
4799        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4800        assert_eq!(
4801            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
4802            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
4803        );
4804        assert_eq!(
4805            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
4806            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
4807        );
4808        assert_eq!(
4809            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
4810            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
4811        );
4812        assert_eq!(
4813            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
4814            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
4815        );
4816        // The two arguments the wrong way round is an empty answer and not an
4817        // error, which is what the swap being in the parse rather than in the
4818        // window buys.
4819        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
4820        assert_eq!(
4821            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
4822            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
4823        );
4824        assert_eq!(
4825            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
4826            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
4827        );
4828        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
4829        // way of spelling the mode, they are a syntax error.
4830        for cmd in [
4831            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
4832            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
4833            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
4834        ] {
4835            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
4836        }
4837    }
4838
4839    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
4840    /// only some of them accept.
4841    #[test]
4842    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
4843        let mut f = Fixture::new();
4844        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4845        assert_eq!(
4846            f.run(&[
4847                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
4848            ]),
4849            "*1\r\n$1\r\nb\r\n"
4850        );
4851        // A negative offset skips past everything, a negative count is no bound.
4852        assert_eq!(
4853            f.run(&[
4854                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
4855            ]),
4856            "*0\r\n"
4857        );
4858        assert_eq!(
4859            f.run(&[
4860                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
4861            ]),
4862            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4863        );
4864        // The two options in either order, which falls out of the parse loop.
4865        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";
4866        assert_eq!(
4867            f.run(&[
4868                b"ZRANGEBYSCORE",
4869                b"z",
4870                b"1",
4871                b"3",
4872                b"WITHSCORES",
4873                b"LIMIT",
4874                b"0",
4875                b"2"
4876            ]),
4877            both
4878        );
4879        assert_eq!(
4880            f.run(&[
4881                b"ZRANGEBYSCORE",
4882                b"z",
4883                b"1",
4884                b"3",
4885                b"LIMIT",
4886                b"0",
4887                b"2",
4888                b"WITHSCORES"
4889            ]),
4890            both
4891        );
4892        // LIMIT on a range by rank is refused after the whole option list has
4893        // been read, so this complains about LIMIT and not about WITHSCORES.
4894        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
4895        assert_eq!(
4896            f.run(&[
4897                b"ZREVRANGE",
4898                b"z",
4899                b"0",
4900                b"-1",
4901                b"WITHSCORES",
4902                b"LIMIT",
4903                b"0",
4904                b"1"
4905            ]),
4906            needs_by
4907        );
4908        assert_eq!(
4909            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
4910            needs_by
4911        );
4912        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
4913        assert_eq!(
4914            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
4915            not_bylex
4916        );
4917        assert_eq!(
4918            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
4919            not_bylex
4920        );
4921        // Two modes at once, an option nobody knows, a LIMIT missing its count,
4922        // and the three number errors, which are three different sentences.
4923        for cmd in [
4924            &[
4925                b"ZRANGE".as_slice(),
4926                b"z",
4927                b"0",
4928                b"-1",
4929                b"BYSCORE",
4930                b"BYLEX",
4931            ][..],
4932            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
4933            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
4934        ] {
4935            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
4936        }
4937        assert_eq!(
4938            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
4939            "-ERR min or max is not a float\r\n"
4940        );
4941        assert_eq!(
4942            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
4943            "-ERR min or max not valid string range item\r\n"
4944        );
4945        assert_eq!(
4946            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
4947            "-ERR value is not an integer or out of range\r\n"
4948        );
4949    }
4950
4951    /// `WITHSCORES` is the one place in this group where the two protocols
4952    /// disagree about the shape of the reply and not just the type of a value.
4953    #[test]
4954    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
4955        let mut f = Fixture::new();
4956        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4957        assert_eq!(
4958            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
4959            "*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"
4960        );
4961        f.out = Out::new(Proto::Resp3);
4962        assert_eq!(
4963            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
4964            "*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"
4965        );
4966        assert_eq!(
4967            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
4968            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
4969        );
4970    }
4971
4972    /// The store form, which is the same parse with the destination in front.
4973    #[test]
4974    fn a_range_store_writes_the_window_into_another_key() {
4975        let mut f = Fixture::new();
4976        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
4977        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
4978        // A window that selects nothing deletes the destination rather than
4979        // leaving an empty sorted set, because an empty one does not exist.
4980        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
4981        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4982        assert_eq!(
4983            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
4984            ":2\r\n"
4985        );
4986        assert_eq!(
4987            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
4988            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
4989        );
4990        // The destination is allowed to be the source, because the result is
4991        // built whole before anything is written over.
4992        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
4993        assert_eq!(
4994            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
4995            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
4996        );
4997        // It takes every option ZRANGE takes except WITHSCORES, which is a
4998        // plain syntax error here and not the sentence about BYLEX.
4999        assert_eq!(
5000            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
5001            "-ERR syntax error\r\n"
5002        );
5003    }
5004
5005    /// The three removals, which are the read side's window with the walk
5006    /// turned into a removal and no options at all.
5007    #[test]
5008    fn the_three_removals_share_their_window_with_the_reads() {
5009        let mut f = Fixture::new();
5010        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5011        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
5012        assert_eq!(
5013            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5014            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5015        );
5016        assert_eq!(
5017            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
5018            ":1\r\n"
5019        );
5020        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
5021        // The last member going takes the key with it.
5022        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
5023        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5024        assert_eq!(
5025            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
5026            ":0\r\n"
5027        );
5028        assert_eq!(
5029            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
5030            "-ERR value is not an integer or out of range\r\n"
5031        );
5032    }
5033
5034    /// The algebra, which is one gather and three names for it.
5035    #[test]
5036    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
5037        let mut f = Fixture::new();
5038        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5039        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5040        assert_eq!(
5041            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
5042            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
5043        );
5044        // The scores are added where a member is in both, and the answer comes
5045        // out in the order those combined scores put it in.
5046        assert_eq!(
5047            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
5048            "*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"
5049        );
5050        assert_eq!(
5051            f.run(&[
5052                b"ZUNION",
5053                b"2",
5054                b"z",
5055                b"y",
5056                b"WEIGHTS",
5057                b"2",
5058                b"3",
5059                b"WITHSCORES"
5060            ]),
5061            "*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"
5062        );
5063        assert_eq!(
5064            f.run(&[
5065                b"ZUNION",
5066                b"2",
5067                b"z",
5068                b"y",
5069                b"AGGREGATE",
5070                b"MIN",
5071                b"WITHSCORES"
5072            ]),
5073            "*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"
5074        );
5075        assert_eq!(
5076            f.run(&[
5077                b"ZUNION",
5078                b"2",
5079                b"z",
5080                b"y",
5081                b"AGGREGATE",
5082                b"MAX",
5083                b"WITHSCORES"
5084            ]),
5085            "*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"
5086        );
5087        assert_eq!(
5088            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
5089            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
5090        );
5091        assert_eq!(
5092            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
5093            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
5094        );
5095        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
5096        // A plain set is an input, and it behaves as a sorted set in which
5097        // every member scores one.
5098        f.run(&[b"SADD", b"p", b"a", b"d"]);
5099        assert_eq!(
5100            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
5101            "*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"
5102        );
5103        // A difference never combines two scores, so it has nothing for either
5104        // of the two options to do and refuses both.
5105        for cmd in [
5106            &[
5107                b"ZDIFF".as_slice(),
5108                b"2",
5109                b"z",
5110                b"y",
5111                b"WEIGHTS",
5112                b"1",
5113                b"1",
5114            ][..],
5115            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
5116        ] {
5117            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5118        }
5119    }
5120
5121    /// The count of keys, which is what lets a key be named `WEIGHTS`.
5122    #[test]
5123    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
5124        let mut f = Fixture::new();
5125        f.run(&[b"ZADD", b"z", b"1", b"a"]);
5126        f.run(&[b"ZADD", b"y", b"2", b"b"]);
5127        // Redis names the command in this one, so each spelling says its own.
5128        assert_eq!(
5129            f.run(&[b"ZUNION", b"0", b"z"]),
5130            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5131        );
5132        assert_eq!(
5133            f.run(&[b"ZUNION", b"-1", b"z"]),
5134            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5135        );
5136        assert_eq!(
5137            f.run(&[b"ZINTERCARD", b"0", b"z"]),
5138            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
5139        );
5140        // A count bigger than the line is a plain syntax error, which reads
5141        // oddly and is what Redis says.
5142        assert_eq!(
5143            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
5144            "-ERR syntax error\r\n"
5145        );
5146        assert_eq!(
5147            f.run(&[b"ZUNION", b"x", b"z"]),
5148            "-ERR value is not an integer or out of range\r\n"
5149        );
5150        // A WEIGHTS list that is not one per key is a syntax error, and a
5151        // weight that is not a number gets a sentence of its own.
5152        assert_eq!(
5153            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
5154            "-ERR syntax error\r\n"
5155        );
5156        assert_eq!(
5157            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
5158            "-ERR weight value is not a float\r\n"
5159        );
5160        assert_eq!(
5161            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
5162            "-ERR syntax error\r\n"
5163        );
5164    }
5165
5166    /// The three store forms, which answer a count and take no WITHSCORES.
5167    #[test]
5168    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
5169        let mut f = Fixture::new();
5170        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5171        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5172        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
5173        assert_eq!(
5174            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
5175            "*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"
5176        );
5177        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
5178        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
5179        // An empty result deletes the destination rather than leaving an empty
5180        // sorted set, because an empty one does not exist.
5181        assert_eq!(
5182            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
5183            ":0\r\n"
5184        );
5185        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5186        // The destination is allowed to name its own source.
5187        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
5188        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
5189        for cmd in [
5190            &[
5191                b"ZUNIONSTORE".as_slice(),
5192                b"d",
5193                b"2",
5194                b"z",
5195                b"y",
5196                b"WITHSCORES",
5197            ][..],
5198            &[
5199                b"ZDIFFSTORE",
5200                b"d",
5201                b"2",
5202                b"z",
5203                b"y",
5204                b"WEIGHTS",
5205                b"1",
5206                b"1",
5207            ],
5208        ] {
5209            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5210        }
5211    }
5212
5213    /// `ZINTERCARD`, which counts without building anything.
5214    #[test]
5215    fn intercard_counts_and_stops_at_its_limit() {
5216        let mut f = Fixture::new();
5217        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5218        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
5219        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
5220        // A limit of zero is no limit, which is Redis's reading of it.
5221        assert_eq!(
5222            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
5223            ":2\r\n"
5224        );
5225        assert_eq!(
5226            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
5227            ":1\r\n"
5228        );
5229        // A negative limit and a limit that is not a number at all get the same
5230        // sentence, which looks like a mistake in Redis and is copied as one.
5231        let bad = "-ERR LIMIT can't be negative\r\n";
5232        assert_eq!(
5233            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
5234            bad
5235        );
5236        assert_eq!(
5237            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
5238            bad
5239        );
5240        for cmd in [
5241            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
5242            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
5243            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
5244        ] {
5245            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5246        }
5247    }
5248
5249    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
5250    #[test]
5251    fn a_draw_answers_one_member_or_an_array_of_them() {
5252        let mut f = Fixture::new();
5253        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5254        // No count is one member or a nil, a count is an array that may be
5255        // empty, and those are two reply types the client has to tell apart.
5256        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
5257        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
5258        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
5259        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
5260        // A positive count draws without replacement, so a count over the size
5261        // answers the whole set and never a member twice.
5262        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
5263        assert!(all.starts_with("*3\r\n"), "{all}");
5264        for m in ["a", "b", "c"] {
5265            assert!(all.contains(m), "{all}");
5266        }
5267        // A negative one draws with replacement and answers exactly as many as
5268        // it was asked for, whatever the size of the set.
5269        assert!(
5270            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
5271            "five draws with replacement"
5272        );
5273        assert!(
5274            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
5275                .starts_with("*4\r\n"),
5276            "two pairs, flat on RESP2"
5277        );
5278        f.out = Out::new(Proto::Resp3);
5279        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
5280        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
5281        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
5282        f.out = Out::new(Proto::Resp2);
5283        assert_eq!(
5284            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
5285            "-ERR syntax error\r\n"
5286        );
5287        assert_eq!(
5288            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
5289            "-ERR value is not an integer or out of range\r\n"
5290        );
5291    }
5292
5293    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
5294    #[test]
5295    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
5296        let mut f = Fixture::new();
5297        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5298        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";
5299        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5300        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
5301        assert_eq!(
5302            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
5303            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
5304        );
5305        assert_eq!(
5306            f.run(&[b"ZSCAN", b"nokey", b"0"]),
5307            "*2\r\n$1\r\n0\r\n*0\r\n"
5308        );
5309        // A score stays a bulk string on RESP3, which is the one place the two
5310        // protocols agree about a score and everywhere else they do not.
5311        f.out = Out::new(Proto::Resp3);
5312        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5313        f.out = Out::new(Proto::Resp2);
5314        assert_eq!(
5315            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
5316            "-ERR NOVALUES option can only be used in HSCAN\r\n"
5317        );
5318        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
5319        assert_eq!(
5320            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
5321            "-ERR syntax error\r\n"
5322        );
5323    }
5324
5325    /// The count is what decides the shape, and its value is not.
5326    #[test]
5327    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
5328        let mut f = Fixture::new();
5329        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5330        // No count, so one flat pair, and the score is a bulk string on RESP2.
5331        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
5332        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
5333        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
5334        // A count, so pairs, and on RESP2 they are flattened into one run.
5335        assert_eq!(
5336            f.run(&[b"ZPOPMIN", b"z", b"2"]),
5337            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
5338        );
5339        // An empty array rather than a null, which is where a sorted set pop and
5340        // a list pop part company, and the same answer a count of zero gives.
5341        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
5342        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
5343        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
5344        // The last member takes the key with it.
5345        assert_eq!(
5346            f.run(&[b"ZPOPMIN", b"z", b"9"]),
5347            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5348        );
5349        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5350
5351        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
5352        f.out = Out::new(Proto::Resp3);
5353        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
5354        assert_eq!(
5355            f.run(&[b"ZPOPMIN", b"z", b"1"]),
5356            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
5357        );
5358        f.out = Out::new(Proto::Resp2);
5359        // Both of these are the range error rather than the usual sentence about
5360        // integers, which is the odd answer and so the one worth copying.
5361        let bad = "-ERR value is out of range, must be positive\r\n";
5362        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
5363        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
5364        assert_eq!(
5365            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
5366            "-ERR syntax error\r\n"
5367        );
5368    }
5369
5370    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
5371    #[test]
5372    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
5373        let mut f = Fixture::new();
5374        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5375        assert_eq!(
5376            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
5377            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
5378        );
5379        // Nested on RESP2 as well, because the key name is already in front of
5380        // the pairs and there is nothing left to flatten into.
5381        assert_eq!(
5382            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
5383            "*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"
5384        );
5385        // A null array and not a null, the same as LMPOP.
5386        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
5387        f.out = Out::new(Proto::Resp3);
5388        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
5389        f.out = Out::new(Proto::Resp2);
5390        let numkeys = "-ERR numkeys should be greater than 0\r\n";
5391        for bad in [
5392            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
5393            &[b"ZMPOP", b"-1", b"z", b"MIN"],
5394            &[b"ZMPOP", b"x", b"z", b"MIN"],
5395        ] {
5396            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
5397        }
5398        let count = "-ERR count should be greater than 0\r\n";
5399        for bad in [
5400            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
5401            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
5402            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
5403        ] {
5404            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
5405        }
5406        let syntax = "-ERR syntax error\r\n";
5407        for bad in [
5408            // Two keys named and one given, so the word that should have been
5409            // the direction is a key and there is no direction left.
5410            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
5411            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
5412            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
5413            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
5414        ] {
5415            assert_eq!(f.run(bad), syntax, "{bad:?}");
5416        }
5417    }
5418
5419    /// The three that wait, when there is something there and they do not have
5420    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
5421    #[test]
5422    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
5423        let mut f = Fixture::new();
5424        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5425        assert_eq!(
5426            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
5427            (
5428                Flow::Continue,
5429                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
5430            )
5431        );
5432        assert_eq!(
5433            f.run(&[b"BZPOPMAX", b"z", b"0"]),
5434            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
5435        );
5436        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
5437        assert_eq!(
5438            f.run(&[
5439                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
5440            ]),
5441            "*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"
5442        );
5443        f.out = Out::new(Proto::Resp3);
5444        assert_eq!(
5445            f.run(&[b"BZPOPMIN", b"z", b"0"]),
5446            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
5447        );
5448        f.out = Out::new(Proto::Resp2);
5449        // Nothing to take, so the client is parked and nothing was written.
5450        assert_eq!(
5451            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
5452            (Flow::Block, String::new())
5453        );
5454        assert_eq!(
5455            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
5456            (Flow::Block, String::new())
5457        );
5458        // The timeout is read before the key count, so this complains about the
5459        // timeout and not about the count.
5460        assert_eq!(
5461            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
5462            "-ERR timeout is not a float or out of range\r\n"
5463        );
5464        assert_eq!(
5465            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
5466            "-ERR numkeys should be greater than 0\r\n"
5467        );
5468        assert_eq!(
5469            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
5470            "-ERR timeout is negative\r\n"
5471        );
5472    }
5473
5474    /// A parked sorted set client is served by whatever puts a member under one
5475    /// of its keys, and is not served by something of another type landing
5476    /// there.
5477    #[test]
5478    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
5479        let mut f = Fixture::new();
5480        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
5481        assert_eq!(f.server.waiters().len(), 1);
5482        // A string under the key is not what it asked for, so it stays parked
5483        // rather than being handed a WRONGTYPE on a command that was accepted.
5484        f.run(&[b"SET", b"z", b"v"]);
5485        let mut out = Out::new(Proto::Resp2);
5486        assert!(!f.server.serve_waiter(0, 0, &mut out));
5487        assert!(out.as_slice().is_empty());
5488        f.run(&[b"DEL", b"z"]);
5489        f.run(&[b"ZADD", b"z", b"5", b"m"]);
5490        assert!(f.server.serve_waiter(0, 0, &mut out));
5491        assert_eq!(
5492            core::str::from_utf8(out.as_slice()).expect("ascii"),
5493            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
5494        );
5495        // And the member is gone, which is what makes a queue of workers on a
5496        // sorted set work at all.
5497        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5498    }
5499
5500    #[test]
5501    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
5502        let mut f = Fixture::new();
5503        f.run(&[b"SET", b"s", b"v"]);
5504        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5505        for cmd in [
5506            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
5507            &[b"ZINCRBY", b"s", b"1", b"a"],
5508            &[b"ZCARD", b"s"],
5509            &[b"ZSCORE", b"s", b"a"],
5510            &[b"ZMSCORE", b"s", b"a"],
5511            &[b"ZREM", b"s", b"a"],
5512            &[b"ZRANK", b"s", b"a"],
5513            &[b"ZREVRANK", b"s", b"a"],
5514            &[b"ZCOUNT", b"s", b"1", b"2"],
5515            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
5516            &[b"ZRANGE", b"s", b"0", b"-1"],
5517            &[b"ZREVRANGE", b"s", b"0", b"-1"],
5518            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
5519            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
5520            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
5521            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
5522            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
5523            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
5524            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
5525            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
5526            &[b"ZUNION", b"1", b"s"],
5527            &[b"ZINTER", b"1", b"s"],
5528            &[b"ZDIFF", b"1", b"s"],
5529            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
5530            &[b"ZINTERSTORE", b"d", b"1", b"s"],
5531            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
5532            &[b"ZINTERCARD", b"1", b"s"],
5533            &[b"ZRANDMEMBER", b"s"],
5534            &[b"ZSCAN", b"s", b"0"],
5535            &[b"ZPOPMIN", b"s"],
5536            &[b"ZPOPMAX", b"s", b"2"],
5537            &[b"ZMPOP", b"1", b"s", b"MIN"],
5538            &[b"BZPOPMIN", b"s", b"0"],
5539            &[b"BZPOPMAX", b"s", b"0"],
5540            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
5541        ] {
5542            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
5543        }
5544        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
5545    }
5546
5547    /// The same churn the set, the string and the list get, because a sorted
5548    /// set that leaks a tree node per add looks exactly like one that does not
5549    /// until it has run for an afternoon.
5550    #[test]
5551    fn churning_sorted_sets_does_not_grow_the_server() {
5552        let mut f = Fixture::new();
5553        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
5554        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
5555        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
5556        for i in 0..200 {
5557            args.push(&scores[i]);
5558            args.push(&members[i]);
5559        }
5560
5561        f.run(&args);
5562        f.run(&[b"DEL", b"z"]);
5563        f.server.compact_step();
5564        let after_first = f.server.memory_bytes();
5565
5566        for _ in 0..200 {
5567            f.run(&args);
5568            f.run(&[b"DEL", b"z"]);
5569            f.server.compact_step();
5570        }
5571        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5572        assert!(
5573            f.server.memory_bytes() <= after_first * 2,
5574            "held {} after two hundred passes against {after_first} after one",
5575            f.server.memory_bytes()
5576        );
5577    }
5578
5579    // ----------------------------------------------------------------- array
5580
5581    #[test]
5582    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
5583        let mut f = Fixture::new();
5584        // Three consecutive positions from a high index, and the reply is how
5585        // many of them were empty before rather than how many were written.
5586        assert_eq!(
5587            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
5588            ":3\r\n"
5589        );
5590        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
5591        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
5592        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
5593        // A hole and a key that is not there are the same answer.
5594        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
5595        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
5596        assert_eq!(
5597            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
5598            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
5599        );
5600        // Scattered pairs in one command, last write wins within it.
5601        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
5602        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
5603    }
5604
5605    /// The two numbers an array reports are not the same number, and one of
5606    /// them does not fit a signed integer.
5607    #[test]
5608    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
5609        let mut f = Fixture::new();
5610        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
5611        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
5612        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
5613        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
5614        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
5615        // Deleting in the middle leaves the high water mark where it was.
5616        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
5617        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
5618        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
5619
5620        // The top of the space is addressable, and its length is a number with
5621        // bit sixty three set, so the reply has to be unsigned or it comes back
5622        // negative.
5623        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
5624        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
5625        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
5626        // And one past it does not exist, so a write that would reach it fails
5627        // before any of it lands.
5628        assert_eq!(
5629            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
5630            "-ERR array index overflow\r\n"
5631        );
5632        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
5633    }
5634
5635    /// One reply per position and not one per element, which is the whole
5636    /// reason the range is capped.
5637    #[test]
5638    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
5639        let mut f = Fixture::new();
5640        f.run(&[b"ARSET", b"a", b"1", b"x"]);
5641        assert_eq!(
5642            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
5643            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
5644        );
5645        // The two ends may come in either order, and the answer is reversed
5646        // rather than empty.
5647        assert_eq!(
5648            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
5649            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
5650        );
5651        // A key that is not there reads like an array of nothing but holes.
5652        assert_eq!(
5653            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
5654            "*2\r\n$-1\r\n$-1\r\n"
5655        );
5656        // A range wider than a million positions is refused and not trimmed,
5657        // because against a missing key it is a request for as many nulls as
5658        // the range is wide.
5659        assert_eq!(
5660            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
5661            "-ERR range exceeds maximum of 1000000 items\r\n"
5662        );
5663    }
5664
5665    /// Every index in the argument list is read before the key is touched, so
5666    /// a bad one at the end leaves nothing half written.
5667    #[test]
5668    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
5669        let mut f = Fixture::new();
5670        assert_eq!(
5671            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
5672            "-ERR invalid array index\r\n"
5673        );
5674        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
5675        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
5676        assert_eq!(
5677            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
5678            "-ERR invalid array index\r\n"
5679        );
5680        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
5681        // An index is unsigned here, so the numbers a list would take are not
5682        // the last element, they are errors.
5683        assert_eq!(
5684            f.run(&[b"ARGET", b"a", b"-1"]),
5685            "-ERR invalid array index\r\n"
5686        );
5687        // And a pair list with an odd tail is an arity error rather than a
5688        // syntax one.
5689        assert_eq!(
5690            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
5691            "-ERR wrong number of arguments for 'armset' command\r\n"
5692        );
5693        assert_eq!(
5694            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
5695            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
5696        );
5697    }
5698
5699    #[test]
5700    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
5701        let mut f = Fixture::new();
5702        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
5703        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
5704        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
5705        // Two ranges in one command, and the second one covers the whole space
5706        // without walking it.
5707        assert_eq!(
5708            f.run(&[
5709                b"ARDELRANGE",
5710                b"a",
5711                b"100",
5712                b"200",
5713                b"0",
5714                b"18446744073709551614"
5715            ]),
5716            ":2\r\n"
5717        );
5718        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
5719        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
5720        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
5721    }
5722
5723    /// A value goes out as the bytes it came in as, whichever of the three ways
5724    /// the array found to store it.
5725    #[test]
5726    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
5727        let mut f = Fixture::new();
5728        let long = vec![b'v'; 200];
5729        f.run(&[
5730            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
5731            b"short", b"5", &long, b"6", b"-0",
5732        ]);
5733        // 42 is an integer, 007 is not one because it does not print back the
5734        // same, 3.5 survives a double and 3.14 does not, and the last two are a
5735        // word packed string and a blob.
5736        assert_eq!(
5737            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
5738            format!(
5739                "*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",
5740                String::from_utf8_lossy(&long)
5741            )
5742        );
5743    }
5744
5745    #[test]
5746    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
5747        let mut f = Fixture::new();
5748        f.run(&[b"ARSET", b"a", b"0", b"x"]);
5749        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
5750        assert_eq!(
5751            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
5752            "$12\r\nsliced-array\r\n"
5753        );
5754        // And it is a body like any other, so the key commands work on it.
5755        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
5756        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
5757        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
5758        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
5759        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
5760        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
5761    }
5762
5763    #[test]
5764    fn every_array_command_refuses_a_key_holding_something_else() {
5765        let mut f = Fixture::new();
5766        f.run(&[b"SET", b"s", b"v"]);
5767        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5768        for cmd in [
5769            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
5770            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
5771            &[b"ARGET".as_ref(), b"s", b"0"][..],
5772            &[b"ARMGET".as_ref(), b"s", b"0"][..],
5773            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
5774            &[b"ARLEN".as_ref(), b"s"][..],
5775            &[b"ARCOUNT".as_ref(), b"s"][..],
5776            &[b"ARDEL".as_ref(), b"s", b"0"][..],
5777            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
5778            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
5779            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
5780            &[b"ARNEXT".as_ref(), b"s"][..],
5781            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
5782            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
5783            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
5784            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
5785            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
5786            &[b"ARINFO".as_ref(), b"s"][..],
5787        ] {
5788            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
5789        }
5790    }
5791
5792    /// Two of the array commands look the key up before they read the index and
5793    /// the rest read the index first, so the same broken argument gets two
5794    /// different errors depending on which command it went to.
5795    #[test]
5796    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
5797        let mut f = Fixture::new();
5798        f.run(&[b"SET", b"s", b"v"]);
5799        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5800        let bad = "-ERR invalid array index\r\n";
5801        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
5802        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
5803        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
5804        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
5805        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
5806        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
5807        // And on a key that is an array the index is just an index.
5808        f.run(&[b"ARSET", b"a", b"0", b"x"]);
5809        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
5810        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
5811    }
5812
5813    #[test]
5814    fn an_append_follows_a_cursor_the_client_can_move() {
5815        let mut f = Fixture::new();
5816        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
5817        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
5818        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
5819        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
5820        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
5821
5822        // A seek says where the next one goes, and a missing key has no cursor
5823        // to move and is not created by the asking.
5824        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
5825        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
5826        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
5827        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
5828        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
5829        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
5830        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
5831
5832        // The top of the space is the one index only ARSEEK will take, and it
5833        // leaves the cursor with nowhere to go.
5834        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
5835        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
5836        assert_eq!(
5837            f.run(&[b"ARINSERT", b"a", b"x"]),
5838            "-ERR insert index overflow\r\n"
5839        );
5840        assert_eq!(
5841            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
5842            "-ERR invalid array index\r\n"
5843        );
5844    }
5845
5846    #[test]
5847    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
5848        let mut f = Fixture::new();
5849        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
5850        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
5851        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
5852        assert_eq!(
5853            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
5854            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
5855        );
5856        // Growing it after it has wrapped puts the survivors back in the order
5857        // they arrived, which is the whole point of paying for the rebuild.
5858        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
5859        assert_eq!(
5860            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
5861            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
5862        );
5863        // The size is read before the key, so a bad one is a bad size wherever
5864        // it is sent.
5865        assert_eq!(
5866            f.run(&[b"ARRING", b"r", b"0", b"x"]),
5867            "-ERR size must be positive\r\n"
5868        );
5869        assert_eq!(
5870            f.run(&[b"ARRING", b"r", b"big", b"x"]),
5871            "-ERR invalid size\r\n"
5872        );
5873    }
5874
5875    #[test]
5876    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
5877        let mut f = Fixture::new();
5878        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
5879        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
5880        assert_eq!(
5881            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
5882            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
5883        );
5884        assert_eq!(
5885            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
5886            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
5887        );
5888        assert_eq!(
5889            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
5890            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
5891            "more than there is gets what there is"
5892        );
5893        // Nothing asked for is an empty reply, and Redis answers that before it
5894        // has read the option or looked at the key.
5895        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
5896        assert_eq!(
5897            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
5898            "-ERR syntax error\r\n"
5899        );
5900        assert_eq!(
5901            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
5902            "-ERR invalid COUNT\r\n"
5903        );
5904
5905        // With no cursor the tail of the array is the anchor, and a hole inside
5906        // the window is reported as one.
5907        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
5908        assert_eq!(
5909            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
5910            "*2\r\n$-1\r\n$1\r\nz\r\n"
5911        );
5912    }
5913
5914    #[test]
5915    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
5916        let mut f = Fixture::new();
5917        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
5918        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
5919        // The whole index space, which ARGETRANGE refuses and this one answers
5920        // in three visits because holes cost nothing.
5921        assert_eq!(
5922            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
5923            "*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"
5924        );
5925        assert_eq!(
5926            f.run(&[
5927                b"ARSCAN",
5928                b"a",
5929                b"18446744073709551614",
5930                b"0",
5931                b"LIMIT",
5932                b"1"
5933            ]),
5934            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
5935        );
5936        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
5937        assert_eq!(
5938            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
5939            "-ERR LIMIT must be positive\r\n"
5940        );
5941        assert_eq!(
5942            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
5943            "-ERR syntax error\r\n"
5944        );
5945        assert_eq!(
5946            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
5947            "-ERR wrong number of arguments for 'arscan' command\r\n"
5948        );
5949    }
5950
5951    #[test]
5952    fn a_grep_answers_the_indexes_whose_elements_match() {
5953        let mut f = Fixture::new();
5954        assert_eq!(
5955            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
5956            "*0\r\n"
5957        );
5958        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
5959
5960        // The two bounds take the ends of the array as well as an index, and a
5961        // reversed range is walked backwards the way ARSCAN walks one.
5962        assert_eq!(
5963            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
5964            "*3\r\n:0\r\n:1\r\n:2\r\n"
5965        );
5966        assert_eq!(
5967            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
5968            "*3\r\n:2\r\n:1\r\n:0\r\n"
5969        );
5970        assert_eq!(
5971            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
5972            "*2\r\n:1\r\n:2\r\n"
5973        );
5974
5975        // One test each. NOCASE reaches all four of them and it may be written
5976        // after the pattern it applies to.
5977        assert_eq!(
5978            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
5979            "*1\r\n:0\r\n"
5980        );
5981        assert_eq!(
5982            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
5983            "*2\r\n:0\r\n:3\r\n"
5984        );
5985        assert_eq!(
5986            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
5987            "*1\r\n:2\r\n"
5988        );
5989        assert_eq!(
5990            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
5991            "*2\r\n:1\r\n:2\r\n"
5992        );
5993
5994        // OR is the default and AND has to be asked for, and either way the
5995        // last of a repeated option wins.
5996        let both: &[&[u8]] = &[
5997            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
5998        ];
5999        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
6000        assert_eq!(
6001            f.run(&[
6002                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
6003            ]),
6004            "*0\r\n"
6005        );
6006        assert_eq!(
6007            f.run(&[
6008                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
6009            ]),
6010            "*2\r\n:0\r\n:1\r\n"
6011        );
6012
6013        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
6014        // not the positions it had to look at.
6015        assert_eq!(
6016            f.run(&[
6017                b"ARGREP",
6018                b"a",
6019                b"-",
6020                b"+",
6021                b"MATCH",
6022                b"a",
6023                b"WITHVALUES",
6024                b"LIMIT",
6025                b"2"
6026            ]),
6027            "*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"
6028        );
6029        assert_eq!(
6030            f.run(&[
6031                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
6032            ]),
6033            "*1\r\n:3\r\n"
6034        );
6035    }
6036
6037    /// Everything ARGREP refuses, in the order it refuses it.
6038    #[test]
6039    fn a_grep_reports_a_broken_command_the_way_redis_does() {
6040        let mut f = Fixture::new();
6041        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
6042        let syntax = "-ERR syntax error\r\n";
6043
6044        // The bounds are read before the plan, so a bad index beats a bad
6045        // predicate whichever way round the two are written.
6046        assert_eq!(
6047            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
6048            "-ERR invalid array index\r\n"
6049        );
6050        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
6051        // A keyword with nothing after it, and a command that asks for nothing.
6052        assert_eq!(
6053            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
6054            syntax
6055        );
6056        assert_eq!(
6057            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
6058            syntax
6059        );
6060        assert_eq!(
6061            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
6062            syntax,
6063            "a command with no predicate in it at all"
6064        );
6065        assert_eq!(
6066            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
6067            "-ERR LIMIT must be positive\r\n"
6068        );
6069        assert_eq!(
6070            f.run(&[
6071                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
6072            ]),
6073            "-ERR value is not an integer or out of range\r\n"
6074        );
6075        assert_eq!(
6076            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
6077            "-ERR regular expression is empty\r\n"
6078        );
6079        assert_eq!(
6080            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
6081            "-ERR invalid regular expression: Missing ')'\r\n"
6082        );
6083        assert_eq!(
6084            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
6085            "-ERR regular expression backreferences are not supported\r\n"
6086        );
6087        // The arity is minus six, so a predicate keyword with no pattern after
6088        // it is short by one and never reaches the parser.
6089        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
6090        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
6091        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
6092    }
6093
6094    #[test]
6095    fn an_op_reduces_a_range_to_one_number() {
6096        let mut f = Fixture::new();
6097        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
6098        assert_eq!(
6099            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
6100            "$4\r\n-0.5\r\n"
6101        );
6102        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
6103        assert_eq!(
6104            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
6105            "$3\r\n2.5\r\n"
6106        );
6107        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
6108        assert_eq!(
6109            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
6110            ":1\r\n"
6111        );
6112        // An aggregate is written with seventeen significant digits, which is
6113        // Redis's own choice and not what a score comes back as.
6114        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
6115        assert_eq!(
6116            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
6117            "$19\r\n0.30000000000000004\r\n"
6118        );
6119        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
6120        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
6121
6122        // Nothing to work with is a null, and a missing key is a null for the
6123        // aggregates and a zero for the two that count.
6124        f.run(&[b"ARSET", b"w", b"0", b"word"]);
6125        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
6126        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
6127        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
6128
6129        assert_eq!(
6130            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
6131            "-ERR unknown operation\r\n"
6132        );
6133        assert_eq!(
6134            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
6135            "-ERR MATCH requires a value argument\r\n"
6136        );
6137        assert_eq!(
6138            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
6139            "-ERR wrong number of arguments for 'arop' command\r\n"
6140        );
6141    }
6142
6143    #[test]
6144    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
6145        let mut f = Fixture::new();
6146        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
6147        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
6148        let short = f.run(&[b"ARINFO", b"a"]);
6149        assert!(
6150            short.starts_with("*14\r\n"),
6151            "seven pairs on RESP2: {short}"
6152        );
6153        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
6154        assert!(
6155            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
6156            "{short}"
6157        );
6158        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
6159        let full = f.run(&[b"ARINFO", b"a", b"full"]);
6160        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
6161        // Two values one apart are held sparsely, so the dense count is zero and
6162        // the two dense averages have nothing to average.
6163        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
6164        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
6165        assert!(
6166            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
6167            "{full}"
6168        );
6169        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
6170
6171        // On RESP3 the same reply is a map and the averages are doubles.
6172        let mut g = Fixture::new();
6173        g.run(&[b"HELLO", b"3"]);
6174        g.run(&[b"ARINSERT", b"a", b"x"]);
6175        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
6176        assert!(map.starts_with("%12\r\n"), "{map}");
6177        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
6178        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
6179    }
6180
6181    #[test]
6182    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
6183        let mut f = Fixture::new();
6184        // Whole numbers up to two to the sixty second come back as integers,
6185        // and past that the digit generator takes over and uses an exponent.
6186        for (score, want) in [
6187            ("3", "3"),
6188            ("3.5", "3.5"),
6189            ("0.3", "0.3"),
6190            ("1e30", "1e+30"),
6191            ("1e19", "1e+19"),
6192            ("1e-7", "1e-7"),
6193            ("0.000001", "0.000001"),
6194            ("4611686018427387904", "4611686018427387904"),
6195            ("-0", "-0"),
6196        ] {
6197            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
6198            assert_eq!(
6199                f.run(&[b"ZSCORE", b"z", b"m"]),
6200                format!("${}\r\n{want}\r\n", want.len()),
6201                "score {score}"
6202            );
6203        }
6204
6205        // The same bytes on RESP3, where the reply is a double rather than a
6206        // bulk string.
6207        let mut g = Fixture::new();
6208        g.run(&[b"HELLO", b"3"]);
6209        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
6210        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
6211        // The two float increments are not this printer. They go through
6212        // ld2string in its human mode, which is a fixed point conversion with
6213        // the trailing zeros taken off, so they never write an exponent, and
6214        // they reply with a bulk string on both protocols.
6215        assert_eq!(
6216            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
6217            "$31\r\n1000000000000000000000000000000\r\n"
6218        );
6219        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
6220        assert_eq!(
6221            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
6222            "$20\r\n10000000000000000000\r\n"
6223        );
6224    }
6225}