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