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 migrate;
62mod scan;
63mod scripting;
64mod server;
65mod sets;
66mod strings;
67pub mod table;
68mod zsets;
69
70pub use args::Args;
71pub use blocking::{Parked, Waiters};
72pub use table::{COMMANDS, Spec, arity_ok, lookup};
73
74use crate::reply::Out;
75use yo_common::{Code, Error};
76use yo_kv::{Clock, Keyspace};
77
78/// How many databases a server has.
79///
80/// Redis's default is sixteen and its `databases` setting can change it. Ours
81/// is sixteen and cannot, which is why `CONFIG GET databases` can answer with a
82/// constant. Nothing in the design needs the number to be fixed; nothing yet
83/// needs it not to be.
84pub const DATABASES: usize = 16;
85
86/// Every database's bit in [`Server::dirty`], which is what a fresh server
87/// starts on so that the first maintenance turn asks all of them.
88///
89/// A `u64` holds sixteen bits with room to spare, and the assertion below is
90/// what turns raising [`DATABASES`] past sixty four into a build failure rather
91/// than a shift that silently drops the databases past the end.
92const ALL_DATABASES: u64 = if DATABASES == 64 {
93    u64::MAX
94} else {
95    (1u64 << DATABASES) - 1
96};
97const _: () = assert!(DATABASES <= 64);
98
99/// How many keys one command throws away before it leaves the rest to the next.
100///
101/// A bound and not a loop to the end, because this runs in front of a client
102/// that is waiting for its reply, and a server a long way over its limit would
103/// otherwise hold that client for as long as it took to walk all the way back
104/// under. Sixty four is a batch's worth of commands, so a server that went over
105/// by what one batch allocated comes back under in one command, and a server
106/// whose limit was just cut in half works through it over the next few thousand
107/// rather than in one long stall. Redis bounds the same loop by a time slice
108/// instead of a count and hands the rest to a timer; there is no timer here, so
109/// the rest goes to the next command that runs.
110const EVICT_BUDGET: usize = 64;
111
112/// What a server says to a command that would allocate when it has no room.
113///
114/// Redis's `shared.oomerr`, word for word including the full stop, because
115/// clients match on the `OOM` prefix and people match on the sentence.
116const OOM: &[u8] = b"command not allowed when used memory > 'maxmemory'.";
117
118/// What the connection should do after a command.
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum Flow {
121    /// Read the next command.
122    Continue,
123    /// Write what is buffered and then close, which is what `QUIT` asks for.
124    Close,
125    /// Nothing was written and nothing is owed yet.
126    ///
127    /// The client is on the waiter list and its reply comes when a key it named
128    /// has something in it or when its deadline passes, whichever happens first.
129    /// Until then the connection stops reading commands, because a client that
130    /// is waiting for an answer is not a client that has sent another question.
131    Block,
132}
133
134/// The numbers `INFO` reports that this layer cannot see for itself.
135///
136/// The reactor owns the sockets, so the reactor is what knows how many clients
137/// there are. It writes these directly and nothing here does anything with them
138/// except report them.
139#[derive(Debug, Clone, Copy, Default)]
140pub struct Stats {
141    /// Connections open right now.
142    pub clients: u64,
143    /// Connections accepted since the server started.
144    pub connections: u64,
145    /// Commands run since the server started, which this layer counts itself.
146    pub commands: u64,
147}
148
149/// Everything a server holds.
150///
151/// One of these per shard thread, not one per process: the databases inside are
152/// not `Sync` and are reached by sending their thread a command. What makes
153/// this a server rather than a shard is that it is the whole of what a
154/// connection can address.
155pub struct Server {
156    dbs: Vec<Keyspace>,
157    clock: Clock,
158    started_ms: u64,
159    /// Where the next maintenance turn starts looking, so that a database
160    /// under constant write load cannot hold the other fifteen's space.
161    next_db: usize,
162    /// One bit per database, set when a command ran against it.
163    ///
164    /// The maintenance turn after every batch used to ask all sixteen
165    /// databases whether they had anything to collect, and asking costs a load
166    /// and a store in each one. Fifteen of those are cold lines on a server
167    /// where every client is on database zero, which is every server, and the
168    /// answer is no every time. This is the cheap half of the question: a
169    /// database nobody has touched since it last said no cannot have started
170    /// saying yes.
171    dirty: u64,
172    /// What the connections are holding, kept by the engine.
173    conn_bytes: usize,
174    /// The `maxmemory` limit in bytes, zero when there is not one.
175    ///
176    /// Zero is the default and it is the whole reason the check in front of
177    /// every write is one comparison against a field that is already warm.
178    maxmemory: u64,
179    /// What [`Server::memory_bytes`] said at the last maintenance turn.
180    ///
181    /// The reading is a walk over every collection in every database and cannot
182    /// go on a command path, so the command path reads this instead and is at
183    /// most one batch behind. What that costs is overshoot: a server can end a
184    /// batch holding one batch's worth of allocation more than its limit before
185    /// anything notices. A batch is 64 commands, so that is bounded by what 64
186    /// commands can allocate and not by how long the server runs.
187    ///
188    /// Only kept up to date when there is a limit to judge it against. A server
189    /// with no `maxmemory` never reads it and never pays for it.
190    used: usize,
191    /// Which database the next eviction draws from.
192    ///
193    /// Its own cursor and not [`Server::next_db`], because eviction and
194    /// compaction move at different rates and sharing one would make the
195    /// database that gets compacted depend on how many keys were evicted.
196    evict_db: usize,
197    /// Which database the next active expiry sweep starts at.
198    ///
199    /// A third cursor for the same reason there is a second one. A sweep runs on
200    /// every turn of the loop and compaction runs when there is dead space, so
201    /// sharing a cursor would make which database gets swept depend on which one
202    /// was last collected.
203    expire_db: usize,
204    /// The millisecond the last active expiry sweep ran on, so the next one on
205    /// the same millisecond does not bother.
206    expire_ms: u64,
207    /// Clients parked on a blocking command.
208    waiters: Waiters,
209    /// Sockets `MIGRATE` is holding open to the servers it has talked to.
210    ///
211    /// Empty on a server nobody has migrated a key out of, which is nearly all
212    /// of them, and it costs a vector's three words to be empty.
213    peers: migrate::Peers,
214    /// The numbers the reactor keeps for `INFO`.
215    pub stats: Stats,
216}
217
218impl Server {
219    /// A server with [`DATABASES`] empty databases on the system clock.
220    #[must_use]
221    pub fn new() -> Server {
222        let clock = Clock::system();
223        Server {
224            dbs: (0..DATABASES)
225                .map(|_| Keyspace::with_clock(clock))
226                .collect(),
227            clock,
228            started_ms: clock.now_ms(),
229            next_db: 0,
230            dirty: ALL_DATABASES,
231            conn_bytes: 0,
232            maxmemory: 0,
233            used: 0,
234            evict_db: 0,
235            expire_db: 0,
236            expire_ms: 0,
237            waiters: Waiters::default(),
238            peers: migrate::Peers::default(),
239            stats: Stats::default(),
240        }
241    }
242
243    /// A server on a clock the caller moves by hand, for tests.
244    #[must_use]
245    pub fn with_clock(clock: Clock) -> Server {
246        Server {
247            dbs: (0..DATABASES)
248                .map(|_| Keyspace::with_clock(clock))
249                .collect(),
250            clock,
251            started_ms: clock.now_ms(),
252            next_db: 0,
253            dirty: ALL_DATABASES,
254            conn_bytes: 0,
255            maxmemory: 0,
256            used: 0,
257            evict_db: 0,
258            expire_db: 0,
259            expire_ms: 0,
260            waiters: Waiters::default(),
261            peers: migrate::Peers::default(),
262            stats: Stats::default(),
263        }
264    }
265
266    /// One database, by index.
267    ///
268    /// # Panics
269    ///
270    /// If `i` is not a database. `SELECT` is the only way a client changes the
271    /// index and it checks, so an index that is out of range here is a bug in
272    /// the caller and not something a client can ask for.
273    pub fn db(&mut self, i: usize) -> &mut Keyspace {
274        // The borrow is mutable, so assume it is used. Anything that only reads
275        // has [`Server::db_ref`] and does not come through here.
276        self.dirty |= 1u64 << i;
277        &mut self.dbs[i]
278    }
279
280    /// One database, by index, without taking it mutably.
281    ///
282    /// What the prefetch stage needs. It runs for all 64 commands in a batch
283    /// before any of them executes, so it cannot hold the mutable borrow `run`
284    /// is about to want, and it does not need one: warming a cache line reads
285    /// nothing and changes nothing.
286    ///
287    /// # Panics
288    ///
289    /// As [`Server::db`].
290    #[must_use]
291    pub fn db_ref(&self, i: usize) -> &Keyspace {
292        &self.dbs[i]
293    }
294
295    /// Take a new clock reading and give it to every database.
296    ///
297    /// Once per turn of the event loop, which is the only place time moves. A
298    /// command asking what the time is gets the answer the whole batch got, so
299    /// two keys written by the same batch expire together (`04` section 3).
300    pub fn refresh_clock(&mut self) {
301        self.clock.refresh();
302        let now = self.clock.now_ms();
303        for db in &mut self.dbs {
304            db.clock_mut().set(now);
305        }
306    }
307
308    /// Move every clock here to `ms` by hand, for tests about expiry.
309    ///
310    /// A test cannot wait a hundred seconds and a test that waits a hundred
311    /// milliseconds is a test that fails on a loaded machine, so time moves on
312    /// request. The system clock underneath will overwrite this on the next
313    /// [`Server::refresh_clock`], which is why this is only useful in a test
314    /// that drives commands directly rather than through the event loop.
315    pub fn set_clock_ms(&mut self, ms: u64) {
316        self.clock.set(ms);
317        for db in &mut self.dbs {
318            db.clock_mut().set(ms);
319        }
320    }
321
322    /// Seconds since this server was built.
323    #[must_use]
324    pub fn uptime_secs(&self) -> u64 {
325        self.clock.now_ms().saturating_sub(self.started_ms) / 1000
326    }
327
328    /// Bytes held by every database's index and arena, plus the read and reply
329    /// buffers of every connection.
330    ///
331    /// The buffers are in here because they are real and because Redis counts
332    /// its own, so leaving them out would make the one number people compare
333    /// flattering rather than true. They are not a database, so nothing in the
334    /// keyspace can change them and the engine has to say when they move.
335    #[must_use]
336    pub fn memory_bytes(&self) -> usize {
337        self.dbs.iter().map(Keyspace::memory_bytes).sum::<usize>() + self.conn_bytes
338    }
339
340    /// What the keyspace itself is holding, live records only.
341    ///
342    /// `used_memory` minus this is what the store costs to run: the index, the
343    /// space dead records are sitting in until compaction gets to them, and the
344    /// connections' buffers.
345    #[must_use]
346    pub fn dataset_bytes(&self) -> usize {
347        self.dbs
348            .iter()
349            .map(|db| db.map().arena().live_bytes() as usize)
350            .sum()
351    }
352
353    /// Bytes the arenas are holding, live and dead together.
354    #[must_use]
355    pub fn arena_bytes(&self) -> usize {
356        self.dbs
357            .iter()
358            .map(|db| db.map().arena().reserved_bytes() as usize)
359            .sum()
360    }
361
362    /// Bytes the indexes are holding.
363    #[must_use]
364    pub fn index_bytes(&self) -> usize {
365        self.dbs
366            .iter()
367            .map(|db| db.map().index().memory_bytes())
368            .sum()
369    }
370
371    /// Arena segments whose pages are real, across every database.
372    #[must_use]
373    pub fn segment_count(&self) -> usize {
374        self.dbs
375            .iter()
376            .map(|db| db.map().arena().resident_segments())
377            .sum()
378    }
379
380    /// What the connections' read and reply buffers are holding.
381    #[must_use]
382    pub const fn conn_bytes(&self) -> usize {
383        self.conn_bytes
384    }
385
386    /// Note that the connections are holding `delta` bytes more than they were,
387    /// or fewer when it is negative.
388    ///
389    /// A delta and not a total because the alternative is a walk over every
390    /// connection, and the walk would have to happen on a turn of the loop
391    /// rather than when `INFO` asks, which puts the cost of a report on the
392    /// command path of a server nobody is asking.
393    pub fn note_conn_bytes(&mut self, delta: isize) {
394        self.conn_bytes = self.conn_bytes.saturating_add_signed(delta);
395    }
396
397    /// Keys reclaimed by running into them after their deadline.
398    #[must_use]
399    pub fn expired_keys(&self) -> u64 {
400        self.dbs.iter().map(Keyspace::expired_keys).sum()
401    }
402
403    /// Keys thrown away to make room, which is the other number entirely.
404    #[must_use]
405    pub fn evicted_keys(&self) -> u64 {
406        self.dbs.iter().map(Keyspace::evicted_keys).sum()
407    }
408
409    /// The `maxmemory` limit in bytes, zero when there is not one.
410    #[must_use]
411    pub const fn maxmemory(&self) -> u64 {
412        self.maxmemory
413    }
414
415    /// Set the limit, and take a reading straight away.
416    ///
417    /// The reading is here rather than left to the next maintenance turn because
418    /// a client that sets the limit and sends a write in the same batch expects
419    /// the write to be judged against the limit it just set, and because the
420    /// cached number is meaningless until the first time there is a limit to
421    /// compare it with.
422    ///
423    /// Turning the limit on also turns on the running total every slab keeps of
424    /// what its collections hold, and turning it off turns that back off, so a
425    /// server with no limit is not paying to count something nobody reads. The
426    /// first reading after switching it on is the walk that the total starts
427    /// from, and it is the only walk.
428    pub fn set_maxmemory(&mut self, bytes: u64) {
429        self.maxmemory = bytes;
430        for db in &mut self.dbs {
431            db.track_memory(bytes != 0);
432        }
433        self.used = self.settled_memory();
434    }
435
436    /// Take a fresh memory reading, which the maintenance turn does once a batch.
437    ///
438    /// Nothing at all when there is no limit, which is the default and is every
439    /// server that has not asked for one.
440    pub fn refresh_memory(&mut self) {
441        if self.maxmemory != 0 {
442            self.used = self.settled_memory();
443        }
444    }
445
446    /// [`Server::memory_bytes`], asked the cheap way.
447    ///
448    /// The same number. The difference is that this asks each database only
449    /// about the collections that could have moved since the last time, which is
450    /// what a batch touched rather than what the server holds, so it can be
451    /// asked once a batch and again on every command that is over the limit.
452    fn settled_memory(&mut self) -> usize {
453        self.dbs
454            .iter_mut()
455            .map(Keyspace::settled_memory_bytes)
456            .sum::<usize>()
457            + self.conn_bytes
458    }
459
460    /// Make room under the `maxmemory` limit, throwing keys away if that is what
461    /// it takes. Answers whether there is anything left it could throw away.
462    ///
463    /// Redis runs the same thing from `processCommand` before every command and
464    /// so does this: a client that writes has to be judged at the moment it
465    /// writes, not a batch later, or the limit is a suggestion.
466    ///
467    /// Three things happen in the loop and all three are needed. Eviction picks
468    /// a key and drops it. Compaction gives the pages back, because dropping a
469    /// key marks its record dead and returns nothing on its own, so a loop that
470    /// only evicted would throw the whole keyspace away and watch the number
471    /// stay where it was. The reading is taken again each time round, because
472    /// the two of them together are the only thing that moves it.
473    ///
474    /// # Why running out of budget is not a no
475    ///
476    /// `false` means there was nothing left to evict, which is `noeviction`, or
477    /// a `volatile` policy on a database where nothing has a deadline, or a
478    /// keyspace that is already empty. It does not mean the server is still over
479    /// its limit, and that difference is Redis's: `performEvictions` answers
480    /// `EVICT_FAIL` only when it has run out of things to delete, and
481    /// `processCommand` refuses the client on that and on nothing else. Running
482    /// out of time part way through a job it is doing well comes back as
483    /// `EVICT_RUNNING` and the command goes through, because a server that is
484    /// evicting steadily and refusing every write while it does it is worse for
485    /// the client than a little overshoot.
486    ///
487    /// # What the limit is worth
488    ///
489    /// Space comes back a segment at a time and a segment is two megabytes, so
490    /// this holds a server to its limit give or take a segment. A `maxmemory` of
491    /// a few hundred megabytes gets what it asked for. A `maxmemory` of four
492    /// megabytes is asking for a precision this store does not have.
493    pub fn make_room(&mut self) -> bool {
494        if self.maxmemory == 0 || self.used as u64 <= self.maxmemory {
495            return true;
496        }
497        // The cached reading is a batch old and the batch may have compacted
498        // since, so take a fresh one before throwing anything away. It is the
499        // settled reading and not the walk, so what this costs is the handful of
500        // collections the last batch touched and not the whole database.
501        self.used = self.settled_memory();
502        let mut budget = EVICT_BUDGET;
503        while self.used as u64 > self.maxmemory {
504            if !self.evict_step() {
505                return false;
506            }
507            self.compact_hard_step();
508            self.used = self.settled_memory();
509            budget -= 1;
510            if budget == 0 {
511                break;
512            }
513        }
514        true
515    }
516
517    /// Throw one key away, from whichever database has one to give.
518    ///
519    /// Round robin from a cursor rather than always starting at database zero,
520    /// so a server using more than one of them does not empty the first before
521    /// touching the second. Almost every server is on database zero only, where
522    /// this is one call that answers and fifteen that say the map is empty.
523    fn evict_step(&mut self) -> bool {
524        for turn in 0..self.dbs.len() {
525            let i = (self.evict_db + turn) % self.dbs.len();
526            if self.dbs[i].evict_one() {
527                self.evict_db = (i + 1) % self.dbs.len();
528                self.dirty |= 1u64 << i;
529                return true;
530            }
531        }
532        false
533    }
534
535    /// The sweep the shard loop calls, at most once a millisecond.
536    ///
537    /// The gate is the whole difference between this and [`Server::expire_step`].
538    /// A maintenance slice runs on every turn of the loop and a turn is a
539    /// hundred nanoseconds, so an ungated sweep would draw a fresh sample ten
540    /// thousand times per millisecond and spend a real share of the shard on
541    /// looking for keys that cannot have died since the last look. Nothing in a
542    /// database changes fast enough to be worth asking about more often than the
543    /// clock can tell the difference, and the clock here is milliseconds.
544    ///
545    /// A millisecond is also far finer than Redis, whose slow cycle runs at ten
546    /// hertz, so this is not the thing that decides how promptly memory comes
547    /// back. What it decides is that an idle server sweeps a thousand times a
548    /// second rather than a million.
549    pub fn expire_slice(&mut self, budget: usize) -> usize {
550        let now = self.clock.now_ms();
551        if now == self.expire_ms {
552            return 0;
553        }
554        self.expire_ms = now;
555        self.expire_step(budget)
556    }
557
558    /// Sweep dead keys out of the databases, spending at most `budget` looks.
559    ///
560    /// Answers what it spent, so the caller can charge its maintenance slice for
561    /// it. See [`yo_kv::expiry`] for why the budget is in keys looked at.
562    ///
563    /// Round robin from its own cursor, and every database gets offered whatever
564    /// is left of the budget rather than a sixteenth of it each, so a server on
565    /// database zero only, which is nearly every server, spends the whole slice
566    /// where the keys are. The fifteen empty ones cost a comparison apiece
567    /// because a database with no key carrying a deadline says so without
568    /// drawing anything.
569    ///
570    /// The cursor moves to the database after whichever one did the work, so two
571    /// busy databases take turns instead of the lower numbered one starving the
572    /// other.
573    pub fn expire_step(&mut self, budget: usize) -> usize {
574        let mut spent = 0;
575        for turn in 0..self.dbs.len() {
576            if spent >= budget {
577                break;
578            }
579            let i = (self.expire_db + turn) % self.dbs.len();
580            let c = self.dbs[i].expire_cycle(budget - spent);
581            spent += c.examined;
582            if c.expired > 0 {
583                self.expire_db = (i + 1) % self.dbs.len();
584                self.dirty |= 1u64 << i;
585            }
586        }
587        spent
588    }
589
590    /// One slice of compaction for a server that is over its limit.
591    ///
592    /// Takes the databases in the same order [`Server::compact_step`] does and
593    /// stops at the first one that had something to move, and it asks with the
594    /// ratios off. See [`Keyspace::compact_hard`] for what that changes.
595    fn compact_hard_step(&mut self) -> Option<usize> {
596        for turn in 0..self.dbs.len() {
597            let i = (self.next_db + turn) % self.dbs.len();
598            if let Some(moved) = self.dbs[i].compact_hard() {
599                self.next_db = (i + 1) % self.dbs.len();
600                return Some(moved);
601            }
602        }
603        None
604    }
605
606    /// Give one database's dead space back, if any database has enough of it to
607    /// be worth the move. `None` when no database had a candidate.
608    ///
609    /// Once per batch, next to the clock. Overwriting a key writes a new record
610    /// and counts the old one dead, so without this a server holds everything
611    /// it has ever written: 400000 sets over 100000 keys measured at 742 bytes
612    /// a key against Redis at 144 for the same load, and the whole difference
613    /// was dead records nothing ever came back for.
614    ///
615    /// At most one segment moves per call and the search starts one database
616    /// further along each time, so the cost of asking is a comparison per
617    /// database and the cost of acting is bounded by a segment.
618    pub fn compact_step(&mut self) -> Option<usize> {
619        for turn in 0..self.dbs.len() {
620            let i = (self.next_db + turn) % self.dbs.len();
621            // Nothing has run against this database since it last said it had
622            // nothing to collect, so it still has nothing to collect and the
623            // line it lives on stays where it is.
624            if self.dirty & (1 << i) == 0 {
625                continue;
626            }
627            if let Some(moved) = self.dbs[i].compact_step() {
628                self.next_db = (i + 1) % self.dbs.len();
629                return Some(moved);
630            }
631            self.dirty &= !(1u64 << i);
632        }
633        None
634    }
635}
636
637impl Default for Server {
638    fn default() -> Server {
639        Server::new()
640    }
641}
642
643/// What one connection has chosen.
644pub struct Session {
645    db: usize,
646    id: u64,
647    name: Vec<u8>,
648}
649
650impl Session {
651    /// A new connection, on database zero with no name.
652    #[must_use]
653    pub fn new(id: u64) -> Session {
654        Session {
655            db: 0,
656            id,
657            name: Vec::new(),
658        }
659    }
660
661    /// The connection id, which `HELLO` reports and `CLIENT` will.
662    #[must_use]
663    pub const fn id(&self) -> u64 {
664        self.id
665    }
666
667    /// Which database this connection is working in.
668    #[must_use]
669    pub const fn db(&self) -> usize {
670        self.db
671    }
672
673    /// The name the client gave itself, empty if it gave none.
674    #[must_use]
675    pub fn name(&self) -> &[u8] {
676        &self.name
677    }
678
679    /// Put everything back the way it was when the connection was opened.
680    ///
681    /// The protocol is not here because it is not here: it lives in the reply
682    /// buffer, and `RESET` sets it back there.
683    pub fn reset(&mut self) {
684        self.db = 0;
685        self.name.clear();
686    }
687
688    /// Record the name from `HELLO ... SETNAME`.
689    fn set_name(&mut self, name: &[u8]) {
690        yo_alloc::allow(|| {
691            self.name.clear();
692            self.name.extend_from_slice(name);
693        });
694    }
695}
696
697/// Run one command and write its reply.
698///
699/// The name is looked up and the arity is checked here, once, so that no body
700/// has to. Everything after that is the command's own.
701pub fn execute(server: &mut Server, session: &mut Session, args: Args<'_>, out: &mut Out) -> Flow {
702    // The decoder never produces a command with no name. If one ever arrives,
703    // it is not something to answer.
704    if args.is_empty() {
705        return Flow::Continue;
706    }
707    server.stats.commands += 1;
708
709    let Some(spec) = lookup(args.name()) else {
710        write_error(out, &args::unknown_command(args));
711        return Flow::Continue;
712    };
713    if !arity_ok(spec, args.len()) {
714        write_error(out, &args::wrong_arity(spec.name));
715        return Flow::Continue;
716    }
717
718    // The limit first, so a server with no `maxmemory`, which is the default and
719    // is nearly all of them, pays one comparison against a field that is already
720    // warm. Every command and not only the writes, because that is where Redis
721    // puts it: making room is the server's job whatever the client asked for,
722    // and the flag only decides who gets told no when there is no room to make.
723    //
724    // The flag is Redis's own `denyoom` and the list of commands carrying it is
725    // Redis's list, so a command that only frees is let through with nothing
726    // left, which is what lets a client dig itself out with `DEL`.
727    if server.maxmemory != 0 && !server.make_room() && spec.flags.contains(&"denyoom") {
728        out.error_line(b"OOM ", OOM);
729        return Flow::Continue;
730    }
731
732    // Which databases the maintenance turn after this batch has to ask. Marked
733    // for every command and not only for the writes, because a read can make
734    // garbage too: a `GET` on a key whose expiry has passed reaps it, and the
735    // record it dropped is exactly the kind of thing the collector is for.
736    // `COPY`, `SWAPDB` and `FLUSHALL` reach a database nobody selected, so the
737    // two groups that hold them mark all of them rather than the session's.
738    server.dirty |= match spec.group {
739        "string" | "set" | "hash" | "list" | "zset" | "array" => 1u64 << session.db,
740        _ => ALL_DATABASES,
741    };
742
743    let mark = out.len();
744    // Before the group, because the five that block are list commands and would
745    // otherwise land in `lists`, which is handed one database and nothing that
746    // could park a client. The flag is the right thing to branch on rather than
747    // a list of names: it is what `COMMAND INFO` reports about exactly these
748    // commands, and the sorted set and stream ones that arrive later carry it
749    // too.
750    let done = if spec.flags.contains(&"blocking") {
751        blocking::execute(server, session, spec, args, out)
752    } else {
753        match spec.group {
754            "string" => {
755                let db = session.db;
756                strings::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
757            }
758            "set" => {
759                let db = session.db;
760                sets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
761            }
762            "hash" => {
763                let db = session.db;
764                hashes::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
765            }
766            "list" => {
767                let db = session.db;
768                lists::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
769            }
770            "zset" => {
771                let db = session.db;
772                zsets::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
773            }
774            "array" => {
775                let db = session.db;
776                arrays::execute(&mut server.dbs[db], spec, args, out).map(|()| Flow::Continue)
777            }
778            // The one keyspace command that needs more than the databases,
779            // because the socket it talks down is held on the server between
780            // commands and not opened again for each one.
781            "keyspace" if spec.name == "migrate" => {
782                migrate::execute(server, session.db, args, out).map(|()| Flow::Continue)
783            }
784            // Every database and not the one the session is on, because `COPY` takes
785            // a `DB n` and writes into a database nobody selected.
786            "keyspace" => keyspace::execute(&mut server.dbs, session.db, spec, args, out)
787                .map(|()| Flow::Continue),
788            "scripting" => scripting::execute(spec, args, out).map(|()| Flow::Continue),
789            _ => server::execute(server, session, spec, args, out),
790        }
791    };
792    match done {
793        Ok(flow) => flow,
794        Err(e) => {
795            out.truncate(mark);
796            write_error(out, &e);
797            Flow::Continue
798        }
799    }
800}
801
802/// The error line for an error value.
803///
804/// The prefix is what a client branches on, and there are only two of them in
805/// this milestone: `WRONGTYPE` for a command sent at the wrong kind of value,
806/// and `ERR` for everything else. The three errors that need a different one,
807/// `NOPROTO`, `WRONGPASS` and `OOM`, are written where they are decided rather
808/// than routed through here. `OOM` is not a [`Code`] of its own because
809/// [`Code::Full`] already covers the string that is too long for
810/// `proto-max-bulk-len`, and that one goes out as `ERR` on a real server.
811fn write_error(out: &mut Out, e: &Error) {
812    let prefix: &[u8] = match e.code() {
813        Code::WrongType => b"WRONGTYPE ",
814        _ => b"ERR ",
815    };
816    out.error_line(prefix, e.message().as_bytes());
817}
818
819#[cfg(test)]
820mod tests {
821    use super::*;
822    use crate::proto::{Limits, Proto};
823    use crate::request::Argv;
824
825    /// Build the wire bytes for a command.
826    ///
827    /// Tests go through the codec rather than around it, so an argument in a
828    /// test is the same borrowed slice a connection produces.
829    pub(crate) fn encode(parts: &[&[u8]]) -> Vec<u8> {
830        let mut wire = format!("*{}\r\n", parts.len()).into_bytes();
831        for p in parts {
832            wire.extend_from_slice(format!("${}\r\n", p.len()).as_bytes());
833            wire.extend_from_slice(p);
834            wire.extend_from_slice(b"\r\n");
835        }
836        wire
837    }
838
839    /// A server, a connection and a buffer, driven the way the reactor will.
840    struct Fixture {
841        server: Server,
842        session: Session,
843        argv: Argv,
844        out: Out,
845    }
846
847    impl Fixture {
848        fn new() -> Fixture {
849            Fixture {
850                server: Server::new(),
851                session: Session::new(7),
852                argv: Argv::new(),
853                out: Out::new(Proto::Resp2),
854            }
855        }
856
857        /// Run one command and answer with the bytes it wrote.
858        fn run(&mut self, parts: &[&[u8]]) -> String {
859            self.flow(parts).1
860        }
861
862        /// Run one command and answer with the bytes exactly as written.
863        ///
864        /// [`Fixture::run`] goes through `from_utf8_lossy`, which is fine for
865        /// every reply that is text and destroys a `DUMP` payload, since a
866        /// payload is arbitrary bytes and a checksum on the end of them.
867        fn raw(&mut self, parts: &[&[u8]]) -> Vec<u8> {
868            let wire = encode(parts);
869            self.argv.decode(&wire, &Limits::default()).unwrap();
870            self.out.clear();
871            execute(
872                &mut self.server,
873                &mut self.session,
874                Args::new(&self.argv, &wire),
875                &mut self.out,
876            );
877            self.out.as_slice().to_vec()
878        }
879
880        /// Move every clock in the server on by `ms`.
881        fn advance(&mut self, ms: u64) {
882            for db in 0..DATABASES {
883                self.server.db(db).clock_mut().advance(ms);
884            }
885        }
886
887        /// The same, with what the connection should do next.
888        fn flow(&mut self, parts: &[&[u8]]) -> (Flow, String) {
889            let wire = encode(parts);
890            self.argv.decode(&wire, &Limits::default()).unwrap();
891            self.out.clear();
892            let flow = execute(
893                &mut self.server,
894                &mut self.session,
895                Args::new(&self.argv, &wire),
896                &mut self.out,
897            );
898            (
899                flow,
900                String::from_utf8_lossy(self.out.as_slice()).into_owned(),
901            )
902        }
903    }
904
905    /// What a client does all day: write the same keys again and again. Every
906    /// one of those writes leaves the previous record behind, so a server that
907    /// never compacts holds every version of every key it has ever been sent.
908    #[test]
909    fn rewriting_the_same_keys_does_not_grow_the_server() {
910        let mut f = Fixture::new();
911        let val = vec![b'v'; 1024];
912        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
913
914        for k in &keys {
915            f.run(&[b"SET", k, &val]);
916        }
917        f.server.compact_step();
918        let after_first = f.server.memory_bytes();
919
920        // 64 KiB a pass, five hundred passes, and the same 64 keys at the end
921        // of it. Thirty two megabytes written to hold sixty four kilobytes,
922        // which is the shape of a real workload and is enough churn to fill
923        // sixteen segments if nothing ever comes back.
924        for _ in 0..500 {
925            for k in &keys {
926                f.run(&[b"SET", k, &val]);
927            }
928            f.server.compact_step();
929        }
930
931        assert!(
932            f.server.memory_bytes() <= after_first * 2,
933            "held {} after five hundred passes against {after_first} after one",
934            f.server.memory_bytes()
935        );
936        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
937        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
938    }
939
940    /// The same churn on a database nobody starts on, either side of a quiet
941    /// spell long enough for the maintenance turn to stop asking about it.
942    ///
943    /// The turn after each batch skips a database that has already said it has
944    /// nothing to collect and has not been touched since, which is what keeps a
945    /// server whose clients are all on database zero from loading and storing
946    /// in the other fifteen every batch to be told no. Two things could go
947    /// wrong with that. A database might never be marked at all, so this uses
948    /// database nine, which nothing marks by accident. And a database whose
949    /// mark was cleared might never get it back, so this drains the collector
950    /// until it says there is nothing left, checks the mark really is gone, and
951    /// then writes another thirty two megabytes through the same sixty four
952    /// keys. If either went wrong the server would hold all of it.
953    #[test]
954    fn a_database_nobody_started_on_is_still_collected() {
955        let mut f = Fixture::new();
956        assert_eq!(f.run(&[b"SELECT", b"9"]), "+OK\r\n");
957        let val = vec![b'v'; 1024];
958        let keys: Vec<Vec<u8>> = (0..64).map(|i| format!("key:{i}").into_bytes()).collect();
959
960        for k in &keys {
961            f.run(&[b"SET", k, &val]);
962        }
963        while f.server.compact_step().is_some() {}
964        assert_eq!(
965            f.server.dirty & (1 << 9),
966            0,
967            "database nine was drained and should not be asked again until it is written to"
968        );
969        let after_first = f.server.memory_bytes();
970
971        for _ in 0..500 {
972            for k in &keys {
973                f.run(&[b"SET", k, &val]);
974            }
975            f.server.compact_step();
976        }
977
978        assert!(
979            f.server.memory_bytes() <= after_first * 2,
980            "held {} after five hundred passes against {after_first} after one",
981            f.server.memory_bytes()
982        );
983        assert_eq!(f.run(&[b"DBSIZE"]), format!(":{}\r\n", keys.len()));
984        assert_eq!(f.run(&[b"STRLEN", b"key:7"]), ":1024\r\n");
985        // And nothing landed anywhere else on the way.
986        f.run(&[b"SELECT", b"0"]);
987        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
988    }
989
990    #[test]
991    fn a_command_goes_from_bytes_to_bytes() {
992        let mut f = Fixture::new();
993        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
994        assert_eq!(f.run(&[b"GET", b"k"]), "$1\r\nv\r\n");
995        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
996        assert_eq!(f.run(&[b"STRLEN", b"k"]), ":1\r\n");
997        // The name is matched whatever case it came in, and so are the options.
998        assert_eq!(f.run(&[b"set", b"k", b"v2", b"xx"]), "+OK\r\n");
999        assert_eq!(f.run(&[b"GET", b"k"]), "$2\r\nv2\r\n");
1000    }
1001
1002    #[test]
1003    fn deleting_counts_keys_removed_and_existing_counts_arguments_matched() {
1004        let mut f = Fixture::new();
1005        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
1006        // A key named twice exists twice and can only be deleted once, and both
1007        // of those are Redis's answers rather than tidier ones.
1008        assert_eq!(f.run(&[b"EXISTS", b"a", b"a", b"nosuch"]), ":2\r\n");
1009        assert_eq!(f.run(&[b"DEL", b"a", b"a", b"nosuch"]), ":1\r\n");
1010        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1011        // UNLINK is the same body and reports the same way.
1012        assert_eq!(f.run(&[b"UNLINK", b"b", b"c"]), ":2\r\n");
1013        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1014    }
1015
1016    #[test]
1017    fn type_is_a_simple_string_and_says_none_for_a_key_that_is_not_there() {
1018        let mut f = Fixture::new();
1019        f.run(&[b"SET", b"k", b"v"]);
1020        // A simple string on both protocols, which is unusual: most replies
1021        // that carry a word are bulk strings.
1022        assert_eq!(f.run(&[b"TYPE", b"k"]), "+string\r\n");
1023        assert_eq!(f.run(&[b"TYPE", b"nosuch"]), "+none\r\n");
1024    }
1025
1026    #[test]
1027    fn touch_counts_the_way_exists_counts() {
1028        let mut f = Fixture::new();
1029        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
1030        assert_eq!(f.run(&[b"TOUCH", b"a", b"b"]), ":2\r\n");
1031        assert_eq!(
1032            f.run(&[b"TOUCH", b"a", b"a"]),
1033            ":2\r\n",
1034            "twice counts twice"
1035        );
1036        assert_eq!(f.run(&[b"TOUCH", b"a", b"nosuch"]), ":1\r\n");
1037        assert_eq!(f.run(&[b"TOUCH", b"nosuch"]), ":0\r\n");
1038    }
1039
1040    #[test]
1041    fn a_rename_moves_the_deadline_with_the_value_and_drops_the_one_it_lands_on() {
1042        let mut f = Fixture::new();
1043        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1044        f.run(&[b"SET", b"b", b"v2", b"EX", b"500"]);
1045
1046        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "+OK\r\n");
1047        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1048        assert_eq!(
1049            f.run(&[b"TTL", b"b"]),
1050            ":100\r\n",
1051            "the source's and not b's"
1052        );
1053        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
1054    }
1055
1056    #[test]
1057    fn a_rename_with_no_source_is_an_error_and_not_a_zero() {
1058        let mut f = Fixture::new();
1059        assert_eq!(f.run(&[b"RENAME", b"a", b"b"]), "-ERR no such key\r\n");
1060        // The source is checked before the destination, so this is the error
1061        // and not the zero RENAMENX would otherwise answer for a taken name.
1062        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), "-ERR no such key\r\n");
1063    }
1064
1065    #[test]
1066    fn renamenx_refuses_a_taken_name_including_the_one_it_already_has() {
1067        let mut f = Fixture::new();
1068        f.run(&[b"MSET", b"a", b"v1", b"b", b"v2"]);
1069
1070        assert_eq!(f.run(&[b"RENAMENX", b"a", b"b"]), ":0\r\n");
1071        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1072        // Renaming onto itself is 0 here and OK for plain RENAME, which is the
1073        // one call the two disagree about and neither does any work for.
1074        assert_eq!(f.run(&[b"RENAMENX", b"a", b"a"]), ":0\r\n");
1075        assert_eq!(f.run(&[b"RENAME", b"a", b"a"]), "+OK\r\n");
1076        assert_eq!(f.run(&[b"RENAMENX", b"a", b"c"]), ":1\r\n");
1077        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\nv1\r\n");
1078    }
1079
1080    #[test]
1081    fn renaming_a_set_does_not_touch_a_member() {
1082        let mut f = Fixture::new();
1083        for i in 0..300 {
1084            f.run(&[b"SADD", b"s", format!("m{i}").as_bytes()]);
1085        }
1086        let before = f.server.memory_bytes();
1087
1088        assert_eq!(f.run(&[b"RENAME", b"s", b"t"]), "+OK\r\n");
1089        assert_eq!(f.run(&[b"SCARD", b"t"]), ":300\r\n");
1090        assert_eq!(f.run(&[b"TYPE", b"t"]), "+set\r\n");
1091        assert!(
1092            f.server.memory_bytes().abs_diff(before) < 256,
1093            "the members were copied: {} against {before}",
1094            f.server.memory_bytes()
1095        );
1096    }
1097
1098    #[test]
1099    fn a_copy_is_a_second_value_and_not_a_second_name() {
1100        let mut f = Fixture::new();
1101        f.run(&[b"SADD", b"s", b"m1", b"m2"]);
1102
1103        assert_eq!(f.run(&[b"COPY", b"s", b"t"]), ":1\r\n");
1104        f.run(&[b"SADD", b"t", b"m3"]);
1105        assert_eq!(f.run(&[b"SCARD", b"s"]), ":2\r\n", "the original is intact");
1106        assert_eq!(f.run(&[b"SCARD", b"t"]), ":3\r\n");
1107    }
1108
1109    /// Every type a key can hold, copied, because two of them used to panic.
1110    ///
1111    /// `COPY` reads the value out of the source through one match on the type
1112    /// tag, and that match had a catch all at the bottom from back when a set
1113    /// and a hash were the only bodies. The list and the sorted set landed after
1114    /// it and nobody came back, so `COPY mylist other` took the shard down. It
1115    /// is an ordinary command against a type the server supports everywhere
1116    /// else, so this walks all five rather than the two that were broken: the
1117    /// point is that the next type cannot land the same way.
1118    #[test]
1119    fn every_type_can_be_copied() {
1120        let mut f = Fixture::new();
1121        f.run(&[b"SET", b"str", b"v1"]);
1122        f.run(&[b"SADD", b"set", b"m1"]);
1123        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1124        f.run(&[b"RPUSH", b"list", b"a", b"b"]);
1125        f.run(&[b"ZADD", b"zset", b"1", b"m1"]);
1126
1127        for name in [
1128            &b"str"[..],
1129            &b"set"[..],
1130            &b"hash"[..],
1131            &b"list"[..],
1132            &b"zset"[..],
1133        ] {
1134            let dst = [name, b":copy"].concat();
1135            assert_eq!(
1136                f.run(&[b"COPY", name, &dst]),
1137                ":1\r\n",
1138                "copying {}",
1139                String::from_utf8_lossy(name)
1140            );
1141            assert_eq!(f.run(&[b"TYPE", name]), f.run(&[b"TYPE", &dst]));
1142        }
1143
1144        assert_eq!(f.run(&[b"LRANGE", b"list:copy", b"0", b"-1"]), {
1145            let mut want = String::from("*2\r\n");
1146            want.push_str("$1\r\na\r\n$1\r\nb\r\n");
1147            want
1148        });
1149        assert_eq!(f.run(&[b"ZSCORE", b"zset:copy", b"m1"]), "$1\r\n1\r\n");
1150
1151        // And the copy is its own value, not a second name for the source.
1152        f.run(&[b"RPUSH", b"list:copy", b"c"]);
1153        assert_eq!(f.run(&[b"LLEN", b"list"]), ":2\r\n");
1154        assert_eq!(f.run(&[b"LLEN", b"list:copy"]), ":3\r\n");
1155    }
1156
1157    #[test]
1158    fn a_copy_refuses_a_taken_destination_until_it_is_told_it_can_have_it() {
1159        let mut f = Fixture::new();
1160        f.run(&[b"SET", b"a", b"v1", b"EX", b"100"]);
1161        f.run(&[b"SET", b"b", b"v2"]);
1162
1163        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":0\r\n");
1164        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv2\r\n");
1165        assert_eq!(f.run(&[b"COPY", b"a", b"b", b"REPLACE"]), ":1\r\n");
1166        assert_eq!(f.run(&[b"GET", b"b"]), "$2\r\nv1\r\n");
1167        assert_eq!(f.run(&[b"TTL", b"b"]), ":100\r\n", "the deadline came too");
1168        assert_eq!(f.run(&[b"COPY", b"nosuch", b"z"]), ":0\r\n");
1169    }
1170
1171    #[test]
1172    fn a_copy_into_another_database_is_a_copy_and_onto_itself_there_is_too() {
1173        let mut f = Fixture::new();
1174        f.run(&[b"SET", b"a", b"v1"]);
1175
1176        // Same key, different database, so this is not the same object and is
1177        // an ordinary copy. Same key in the same database is the error below.
1178        assert_eq!(f.run(&[b"COPY", b"a", b"a", b"DB", b"1"]), ":1\r\n");
1179        f.run(&[b"SELECT", b"1"]);
1180        assert_eq!(f.run(&[b"GET", b"a"]), "$2\r\nv1\r\n");
1181        assert_eq!(
1182            f.run(&[b"COPY", b"a", b"a", b"DB", b"0"]),
1183            ":0\r\n",
1184            "taken"
1185        );
1186        assert_eq!(
1187            f.run(&[b"COPY", b"a", b"a", b"DB", b"0", b"REPLACE"]),
1188            ":1\r\n"
1189        );
1190    }
1191
1192    #[test]
1193    fn sort_takes_its_options_in_any_order_and_the_last_one_wins() {
1194        let mut f = Fixture::new();
1195        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1196        assert_eq!(
1197            f.run(&[b"SORT", b"l"]),
1198            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1199        );
1200        // DESC then ASC is ASC, because the only thing ASC does is undo a DESC.
1201        assert_eq!(
1202            f.run(&[b"SORT", b"l", b"DESC", b"asc"]),
1203            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1204        );
1205        assert_eq!(
1206            f.run(&[b"sort", b"l", b"LIMIT", b"1", b"1", b"DESC"]),
1207            "*1\r\n$1\r\n2\r\n"
1208        );
1209    }
1210
1211    #[test]
1212    fn sort_reads_a_key_per_element_for_by_and_for_get() {
1213        let mut f = Fixture::new();
1214        f.run(&[b"RPUSH", b"l", b"a", b"b"]);
1215        f.run(&[b"MSET", b"w_a", b"2", b"w_b", b"1", b"d_b", b"bee"]);
1216        // `b` weighs less so it comes first, and its `GET` hits where `a`'s
1217        // misses, which is a nil in the middle of the array and not a short one.
1218        assert_eq!(
1219            f.run(&[b"SORT", b"l", b"BY", b"w_*", b"GET", b"#", b"GET", b"d_*"]),
1220            "*4\r\n$1\r\nb\r\n$3\r\nbee\r\n$1\r\na\r\n$-1\r\n"
1221        );
1222    }
1223
1224    #[test]
1225    fn sort_store_writes_a_list_and_answers_its_length() {
1226        let mut f = Fixture::new();
1227        f.run(&[b"RPUSH", b"l", b"3", b"1", b"2"]);
1228        assert_eq!(f.run(&[b"SORT", b"l", b"STORE", b"out"]), ":3\r\n");
1229        assert_eq!(f.run(&[b"TYPE", b"out"]), "+list\r\n");
1230        assert_eq!(
1231            f.run(&[b"LRANGE", b"out", b"0", b"-1"]),
1232            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$1\r\n3\r\n"
1233        );
1234        // An empty result takes the destination with it rather than leaving a
1235        // list that holds nothing.
1236        assert_eq!(f.run(&[b"SORT", b"missing", b"STORE", b"out"]), ":0\r\n");
1237        assert_eq!(f.run(&[b"EXISTS", b"out"]), ":0\r\n");
1238    }
1239
1240    #[test]
1241    fn sort_ro_does_not_know_the_word_store() {
1242        let mut f = Fixture::new();
1243        f.run(&[b"RPUSH", b"l", b"2", b"1"]);
1244        assert_eq!(f.run(&[b"SORT_RO", b"l"]), "*2\r\n$1\r\n1\r\n$1\r\n2\r\n");
1245        assert_eq!(
1246            f.run(&[b"SORT_RO", b"l", b"STORE", b"d"]),
1247            "-ERR syntax error\r\n"
1248        );
1249        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1250    }
1251
1252    #[test]
1253    fn sort_refuses_what_it_cannot_sort() {
1254        let mut f = Fixture::new();
1255        assert_eq!(f.run(&[b"SORT", b"nosuchkey"]), "*0\r\n");
1256        f.run(&[b"SET", b"s", b"x"]);
1257        assert_eq!(
1258            f.run(&[b"SORT", b"s"]),
1259            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
1260        );
1261        f.run(&[b"RPUSH", b"words", b"one", b"two"]);
1262        assert_eq!(
1263            f.run(&[b"SORT", b"words"]),
1264            "-ERR One or more scores can't be converted into double\r\n"
1265        );
1266        assert_eq!(
1267            f.run(&[b"SORT", b"words", b"ALPHA"]),
1268            "*2\r\n$3\r\none\r\n$3\r\ntwo\r\n"
1269        );
1270        assert_eq!(f.run(&[b"SORT", b"words", b"BY"]), "-ERR syntax error\r\n");
1271    }
1272
1273    #[test]
1274    fn move_takes_the_key_out_of_one_database_and_puts_it_in_another() {
1275        let mut f = Fixture::new();
1276        assert_eq!(f.run(&[b"RPUSH", b"l", b"a", b"b"]), ":2\r\n");
1277        assert_eq!(f.run(&[b"MOVE", b"l", b"1"]), ":1\r\n");
1278        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1279        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1280        assert_eq!(
1281            f.run(&[b"LRANGE", b"l", b"0", b"-1"]),
1282            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
1283        );
1284        // And back, which proves the body survived the trip rather than being
1285        // rebuilt from a copy that happened to look the same.
1286        assert_eq!(f.run(&[b"MOVE", b"l", b"0"]), ":1\r\n");
1287        assert_eq!(f.run(&[b"EXISTS", b"l"]), ":0\r\n");
1288    }
1289
1290    #[test]
1291    fn move_answers_zero_when_either_end_says_no() {
1292        let mut f = Fixture::new();
1293        assert_eq!(f.run(&[b"MOVE", b"nope", b"1"]), ":0\r\n");
1294        assert_eq!(f.run(&[b"SET", b"a", b"here"]), "+OK\r\n");
1295        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1296        assert_eq!(f.run(&[b"SET", b"a", b"there"]), "+OK\r\n");
1297        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1298        // The destination is taken, so nothing moves and the source is still
1299        // there with what it had.
1300        assert_eq!(f.run(&[b"MOVE", b"a", b"1"]), ":0\r\n");
1301        assert_eq!(f.run(&[b"GET", b"a"]), "$4\r\nhere\r\n");
1302        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1303        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nthere\r\n");
1304    }
1305
1306    #[test]
1307    fn move_refuses_a_database_that_is_not_one_and_the_one_it_is_on() {
1308        let mut f = Fixture::new();
1309        assert_eq!(
1310            f.run(&[b"MOVE", b"a", b"0"]),
1311            "-ERR source and destination objects are the same\r\n"
1312        );
1313        assert_eq!(
1314            f.run(&[b"MOVE", b"a", b"99"]),
1315            "-ERR DB index is out of range\r\n"
1316        );
1317        assert_eq!(
1318            f.run(&[b"MOVE", b"a", b"-1"]),
1319            "-ERR DB index is out of range\r\n"
1320        );
1321        assert_eq!(
1322            f.run(&[b"MOVE", b"a", b"x"]),
1323            "-ERR value is not an integer or out of range\r\n"
1324        );
1325    }
1326
1327    #[test]
1328    fn swapdb_swaps_what_two_connections_would_see() {
1329        let mut f = Fixture::new();
1330        assert_eq!(f.run(&[b"SET", b"k", b"zero"]), "+OK\r\n");
1331        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1332        assert_eq!(f.run(&[b"SET", b"k", b"one"]), "+OK\r\n");
1333        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
1334
1335        assert_eq!(f.run(&[b"SWAPDB", b"0", b"1"]), "+OK\r\n");
1336        // Still on database zero, and database zero is a different database.
1337        assert_eq!(f.run(&[b"GET", b"k"]), "$3\r\none\r\n");
1338        assert_eq!(f.run(&[b"SELECT", b"1"]), "+OK\r\n");
1339        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1340        // A database swapped with itself is fine and changes nothing.
1341        assert_eq!(f.run(&[b"SWAPDB", b"1", b"1"]), "+OK\r\n");
1342        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
1343    }
1344
1345    #[test]
1346    fn swapdb_says_which_index_it_could_not_read() {
1347        let mut f = Fixture::new();
1348        assert_eq!(
1349            f.run(&[b"SWAPDB", b"x", b"1"]),
1350            "-ERR invalid first DB index\r\n"
1351        );
1352        assert_eq!(
1353            f.run(&[b"SWAPDB", b"0", b"y"]),
1354            "-ERR invalid second DB index\r\n"
1355        );
1356        // A number too big to be an index on a server that keeps one in an int
1357        // is the same complaint, and a plausible one that is not ours is the
1358        // range complaint instead. The split is Redis's.
1359        assert_eq!(
1360            f.run(&[b"SWAPDB", b"99999999999999", b"1"]),
1361            "-ERR invalid first DB index\r\n"
1362        );
1363        assert_eq!(
1364            f.run(&[b"SWAPDB", b"0", b"99"]),
1365            "-ERR DB index is out of range\r\n"
1366        );
1367        assert_eq!(
1368            f.run(&[b"SWAPDB", b"-1", b"0"]),
1369            "-ERR DB index is out of range\r\n"
1370        );
1371    }
1372
1373    #[test]
1374    fn wait_answers_zero_replicas_without_waiting() {
1375        let mut f = Fixture::new();
1376        assert_eq!(f.run(&[b"SET", b"a", b"v"]), "+OK\r\n");
1377        assert_eq!(f.run(&[b"WAIT", b"0", b"0"]), ":0\r\n");
1378        // A replica that is never going to arrive, and a timeout that would be
1379        // a real wait on a server that had one.
1380        assert_eq!(f.run(&[b"WAIT", b"3", b"1000"]), ":0\r\n");
1381        // Negative replicas is not an error, because zero is already more than
1382        // it asked for.
1383        assert_eq!(f.run(&[b"WAIT", b"-1", b"0"]), ":0\r\n");
1384        assert_eq!(
1385            f.run(&[b"WAIT", b"x", b"0"]),
1386            "-ERR value is not an integer or out of range\r\n"
1387        );
1388        assert_eq!(
1389            f.run(&[b"WAIT", b"0", b"-1"]),
1390            "-ERR timeout is negative\r\n"
1391        );
1392        assert_eq!(
1393            f.run(&[b"WAIT", b"0", b"1.5"]),
1394            "-ERR timeout is not an integer or out of range\r\n"
1395        );
1396    }
1397
1398    #[test]
1399    fn waitaof_answers_two_zeroes_and_refuses_a_local_wait() {
1400        let mut f = Fixture::new();
1401        assert_eq!(f.run(&[b"WAITAOF", b"0", b"0", b"0"]), "*2\r\n:0\r\n:0\r\n");
1402        assert_eq!(
1403            f.run(&[b"WAITAOF", b"1", b"0", b"0"]),
1404            "-ERR WAITAOF cannot be used when numlocal is set but appendonly is disabled.\r\n"
1405        );
1406        assert_eq!(
1407            f.run(&[b"WAITAOF", b"2", b"0", b"0"]),
1408            "-ERR value is out of range, value must between 0 and 1\r\n"
1409        );
1410        assert_eq!(
1411            f.run(&[b"WAITAOF", b"0", b"-1", b"0"]),
1412            "-ERR value is out of range, must be positive\r\n"
1413        );
1414        // The arguments are all read before the server looks at itself, so a
1415        // bad timeout beats the append only complaint even with numlocal set.
1416        assert_eq!(
1417            f.run(&[b"WAITAOF", b"1", b"0", b"-5"]),
1418            "-ERR timeout is negative\r\n"
1419        );
1420    }
1421
1422    /// The bytes inside a bulk reply, with the header and the trailing break
1423    /// taken off. Every `DUMP` test needs this and none of them care how the
1424    /// length was written.
1425    fn payload(reply: &[u8]) -> Vec<u8> {
1426        let head = reply.windows(2).position(|w| w == b"\r\n").unwrap();
1427        reply[head + 2..reply.len() - 2].to_vec()
1428    }
1429
1430    #[test]
1431    fn a_value_survives_a_dump_and_a_restore() {
1432        let mut f = Fixture::new();
1433        f.run(&[b"SET", b"s", b"hello"]);
1434        f.run(&[b"RPUSH", b"l", b"a", b"b", b"c"]);
1435        f.run(&[b"SADD", b"t", b"1", b"2", b"3"]);
1436        f.run(&[b"SADD", b"u", b"x", b"y"]);
1437        f.run(&[b"HSET", b"h", b"f", b"1", b"g", b"2"]);
1438        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"2.5", b"b"]);
1439
1440        for key in [&b"s"[..], b"l", b"t", b"u", b"h", b"z"] {
1441            let mut copy = key.to_vec();
1442            copy.push(b'2');
1443            let bytes = payload(&f.raw(&[b"DUMP", key]));
1444            assert_eq!(f.run(&[b"RESTORE", &copy, b"0", &bytes]), "+OK\r\n");
1445            assert_eq!(f.run(&[b"TYPE", &copy]), f.run(&[b"TYPE", key]));
1446        }
1447
1448        assert_eq!(f.run(&[b"GET", b"s2"]), "$5\r\nhello\r\n");
1449        assert_eq!(
1450            f.run(&[b"LRANGE", b"l2", b"0", b"-1"]),
1451            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
1452        );
1453        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"t2"])), ["1", "2", "3"]);
1454        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"u2"])), ["x", "y"]);
1455        assert_eq!(f.run(&[b"HGET", b"h2", b"g"]), "$1\r\n2\r\n");
1456        assert_eq!(f.run(&[b"ZSCORE", b"z2", b"b"]), "$3\r\n2.5\r\n");
1457        // The encoding survives too, since the payload names the plainest legal
1458        // type and the loader puts the value back on the rung it belongs on.
1459        assert_eq!(
1460            f.run(&[b"OBJECT", b"ENCODING", b"t2"]),
1461            f.run(&[b"OBJECT", b"ENCODING", b"t"])
1462        );
1463    }
1464
1465    #[test]
1466    fn a_dumped_hash_keeps_its_field_deadlines() {
1467        let mut f = Fixture::new();
1468        f.run(&[b"HSET", b"h", b"keep", b"1", b"go", b"2"]);
1469        assert_eq!(
1470            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"go"]),
1471            "*1\r\n:1\r\n"
1472        );
1473        let bytes = payload(&f.raw(&[b"DUMP", b"h"]));
1474        assert_eq!(f.run(&[b"RESTORE", b"h2", b"0", &bytes]), "+OK\r\n");
1475        assert_eq!(
1476            f.run(&[b"HTTL", b"h2", b"FIELDS", b"2", b"keep", b"go"]),
1477            "*2\r\n:-1\r\n:100\r\n"
1478        );
1479    }
1480
1481    #[test]
1482    fn dump_leaves_the_deadline_behind_and_restore_is_given_a_new_one() {
1483        let mut f = Fixture::new();
1484        f.run(&[b"SET", b"a", b"v", b"EX", b"100"]);
1485        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1486        assert_eq!(f.run(&[b"RESTORE", b"b", b"0", &bytes]), "+OK\r\n");
1487        assert_eq!(f.run(&[b"TTL", b"b"]), ":-1\r\n");
1488        assert_eq!(f.run(&[b"RESTORE", b"c", b"5000", &bytes]), "+OK\r\n");
1489        assert_eq!(f.run(&[b"TTL", b"c"]), ":5\r\n");
1490        // An absolute deadline that has already gone is not an error. The key is
1491        // not created and the reply is the same OK a live one gets.
1492        assert_eq!(
1493            f.run(&[b"RESTORE", b"d", b"1", &bytes, b"ABSTTL"]),
1494            "+OK\r\n"
1495        );
1496        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
1497    }
1498
1499    #[test]
1500    fn dump_answers_nothing_for_a_key_that_is_not_there() {
1501        let mut f = Fixture::new();
1502        assert_eq!(f.run(&[b"DUMP", b"nope"]), "$-1\r\n");
1503        f.run(&[b"SET", b"gone", b"v", b"PX", b"10"]);
1504        f.advance(50);
1505        assert_eq!(f.run(&[b"DUMP", b"gone"]), "$-1\r\n");
1506    }
1507
1508    #[test]
1509    fn restore_refuses_a_key_that_is_there_unless_it_is_told_to_replace() {
1510        let mut f = Fixture::new();
1511        f.run(&[b"SET", b"a", b"first"]);
1512        f.run(&[b"SET", b"b", b"second"]);
1513        let bytes = payload(&f.raw(&[b"DUMP", b"b"]));
1514        assert_eq!(
1515            f.run(&[b"RESTORE", b"a", b"0", &bytes]),
1516            "-BUSYKEY Target key name already exists.\r\n"
1517        );
1518        assert_eq!(f.run(&[b"GET", b"a"]), "$5\r\nfirst\r\n");
1519        assert_eq!(
1520            f.run(&[b"RESTORE", b"a", b"0", &bytes, b"REPLACE"]),
1521            "+OK\r\n"
1522        );
1523        assert_eq!(f.run(&[b"GET", b"a"]), "$6\r\nsecond\r\n");
1524    }
1525
1526    /// The busy key comes before the payload, which is not the order the
1527    /// arguments read in. Whether a key is taken should not depend on whether
1528    /// the bytes behind it happened to be good.
1529    #[test]
1530    fn restore_asks_about_the_key_before_it_looks_at_the_bytes() {
1531        let mut f = Fixture::new();
1532        f.run(&[b"SET", b"a", b"v"]);
1533        assert_eq!(
1534            f.run(&[b"RESTORE", b"a", b"0", b"rubbish"]),
1535            "-BUSYKEY Target key name already exists.\r\n"
1536        );
1537        // And the options come before even that, so a bad FREQ beats the busy
1538        // key the same way a bad DB beats a missing source in COPY.
1539        assert_eq!(
1540            f.run(&[b"RESTORE", b"a", b"0", b"rubbish", b"FREQ", b"300"]),
1541            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1542        );
1543    }
1544
1545    #[test]
1546    fn restore_can_tell_a_bad_footer_from_bad_bytes() {
1547        let mut f = Fixture::new();
1548        f.run(&[b"SET", b"a", b"hello"]);
1549        let good = payload(&f.raw(&[b"DUMP", b"a"]));
1550
1551        let mut flipped = good.clone();
1552        flipped[2] ^= 0x40;
1553        assert_eq!(
1554            f.run(&[b"RESTORE", b"b", b"0", &flipped]),
1555            "-ERR DUMP payload version or checksum are wrong\r\n"
1556        );
1557        assert_eq!(
1558            f.run(&[b"RESTORE", b"b", b"0", b"short"]),
1559            "-ERR DUMP payload version or checksum are wrong\r\n"
1560        );
1561        // A footer that is right over a body that is not. The type byte says
1562        // string and there is nothing behind it, so the checksum agrees and the
1563        // value does not exist.
1564        let mut truncated = good[..1].to_vec();
1565        truncated.extend_from_slice(&good[good.len() - 10..good.len() - 8]);
1566        let crc = yo_common::crc::crc64(0, &truncated);
1567        truncated.extend_from_slice(&crc.to_le_bytes());
1568        assert_eq!(
1569            f.run(&[b"RESTORE", b"b", b"0", &truncated]),
1570            "-ERR Bad data format\r\n"
1571        );
1572        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
1573    }
1574
1575    #[test]
1576    fn restore_checks_the_three_numbers_a_client_can_get_wrong() {
1577        let mut f = Fixture::new();
1578        f.run(&[b"SET", b"a", b"v"]);
1579        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1580        assert_eq!(
1581            f.run(&[b"RESTORE", b"b", b"-1", &bytes]),
1582            "-ERR Invalid TTL value, must be >= 0\r\n"
1583        );
1584        assert_eq!(
1585            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"-1"]),
1586            "-ERR Invalid IDLETIME value, must be >= 0\r\n"
1587        );
1588        assert_eq!(
1589            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ", b"256"]),
1590            "-ERR Invalid FREQ value, must be >= 0 and <= 255\r\n"
1591        );
1592        // Both are accepted and both are then dropped, which is D-26.
1593        assert_eq!(
1594            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"IDLETIME", b"90"]),
1595            "+OK\r\n"
1596        );
1597        assert_eq!(
1598            f.run(&[b"RESTORE", b"c", b"0", &bytes, b"FREQ", b"200", b"REPLACE"]),
1599            "+OK\r\n"
1600        );
1601    }
1602
1603    /// Neither word is refused for being the wrong one. Each is only accepted
1604    /// while the other is unset, so the second of the two falls through to the
1605    /// plain syntax error rather than getting a message of its own.
1606    #[test]
1607    fn restore_takes_idletime_or_freq_and_not_both() {
1608        let mut f = Fixture::new();
1609        f.run(&[b"SET", b"a", b"v"]);
1610        let bytes = payload(&f.raw(&[b"DUMP", b"a"]));
1611        assert_eq!(
1612            f.run(&[
1613                b"RESTORE",
1614                b"b",
1615                b"0",
1616                &bytes,
1617                b"IDLETIME",
1618                b"1",
1619                b"FREQ",
1620                b"2"
1621            ]),
1622            "-ERR syntax error\r\n"
1623        );
1624        assert_eq!(
1625            f.run(&[
1626                b"RESTORE",
1627                b"b",
1628                b"0",
1629                &bytes,
1630                b"FREQ",
1631                b"2",
1632                b"IDLETIME",
1633                b"1"
1634            ]),
1635            "-ERR syntax error\r\n"
1636        );
1637        assert_eq!(
1638            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"FREQ"]),
1639            "-ERR syntax error\r\n"
1640        );
1641        assert_eq!(
1642            f.run(&[b"RESTORE", b"b", b"0", &bytes, b"NOSUCH"]),
1643            "-ERR syntax error\r\n"
1644        );
1645    }
1646
1647    #[test]
1648    fn copy_checks_its_options_before_it_looks_for_anything() {
1649        let mut f = Fixture::new();
1650        // No key exists at all, and every one of these is still the option
1651        // complaint rather than a zero, which is the order a real server uses.
1652        assert_eq!(
1653            f.run(&[b"COPY", b"a", b"b", b"DB", b"99"]),
1654            "-ERR DB index is out of range\r\n"
1655        );
1656        assert_eq!(
1657            f.run(&[b"COPY", b"a", b"b", b"DB", b"-1"]),
1658            "-ERR DB index is out of range\r\n"
1659        );
1660        assert_eq!(
1661            f.run(&[b"COPY", b"a", b"b", b"DB", b"x"]),
1662            "-ERR value is not an integer or out of range\r\n"
1663        );
1664        assert_eq!(
1665            f.run(&[b"COPY", b"a", b"b", b"nonsense"]),
1666            "-ERR syntax error\r\n"
1667        );
1668        assert_eq!(
1669            f.run(&[b"COPY", b"a", b"a"]),
1670            "-ERR source and destination objects are the same\r\n"
1671        );
1672        // Repeated, reordered and lowercased, and the last DB wins.
1673        assert_eq!(
1674            f.run(&[b"COPY", b"a", b"b", b"dB", b"1", b"rEpLaCe", b"db", b"2"]),
1675            ":0\r\n"
1676        );
1677    }
1678
1679    #[test]
1680    fn time_is_two_bulk_strings_and_moves() {
1681        let mut f = Fixture::new();
1682        let first = f.run(&[b"TIME"]);
1683        assert!(first.starts_with("*2\r\n$"), "got {first}");
1684        let parts: Vec<&str> = first.split("\r\n").collect();
1685        let secs: i64 = parts[2].parse().expect("seconds as decimal text");
1686        let micros: i64 = parts[4].parse().expect("microseconds as decimal text");
1687        assert!(secs > 1_700_000_000, "a real wall clock, got {secs}");
1688        assert!((0..1_000_000).contains(&micros), "got {micros}");
1689        // The coarse clock the keyspace uses is a cached millisecond that a
1690        // background tick refreshes, so a TIME built on it would answer the
1691        // same microsecond twice in a row here.
1692        assert_ne!(first, f.run(&[b"TIME"]));
1693    }
1694
1695    #[test]
1696    fn a_keyspace_scan_walks_every_key_once() {
1697        let mut f = Fixture::new();
1698        for i in 0..500 {
1699            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
1700        }
1701
1702        let mut seen: Vec<String> = Vec::new();
1703        let mut cursor = "0".to_owned();
1704        let mut calls = 0;
1705        loop {
1706            let (next, keys) = scan_reply(&f.run(&[b"SCAN", cursor.as_bytes(), b"COUNT", b"32"]));
1707            seen.extend(keys);
1708            cursor = next;
1709            calls += 1;
1710            assert!(calls < 10_000, "the cursor is not advancing");
1711            if cursor == "0" {
1712                break;
1713            }
1714        }
1715
1716        seen.sort();
1717        seen.dedup();
1718        assert_eq!(seen.len(), 500, "every key once and only once");
1719        // And more than one call to get them, or the COUNT is being ignored and
1720        // the loop above proved nothing about resuming.
1721        assert!(calls > 1, "500 keys came back in one batch");
1722    }
1723
1724    #[test]
1725    fn a_scan_narrows_by_pattern_and_by_type() {
1726        let mut f = Fixture::new();
1727        f.run(&[b"SET", b"str", b"v"]);
1728        f.run(&[b"SADD", b"members", b"a"]);
1729        f.run(&[b"HSET", b"fields", b"f", b"v"]);
1730
1731        let all = |f: &mut Fixture, args: &[&[u8]]| {
1732            let mut out: Vec<String> = Vec::new();
1733            let mut cursor = "0".to_owned();
1734            loop {
1735                let mut line: Vec<&[u8]> = vec![b"SCAN", cursor.as_bytes()];
1736                line.extend_from_slice(args);
1737                let (next, keys) = scan_reply(&f.run(&line));
1738                out.extend(keys);
1739                cursor = next;
1740                if cursor == "0" {
1741                    break;
1742                }
1743            }
1744            out.sort();
1745            out
1746        };
1747
1748        assert_eq!(all(&mut f, &[]), ["fields", "members", "str"]);
1749        assert_eq!(all(&mut f, &[b"MATCH", b"*e*"]), ["fields", "members"]);
1750        assert_eq!(all(&mut f, &[b"TYPE", b"set"]), ["members"]);
1751        // Case insensitive, the same as Redis's own comparison.
1752        assert_eq!(all(&mut f, &[b"TYPE", b"HASH"]), ["fields"]);
1753        // A type nothing can hold is not an error, it just matches nothing.
1754        assert!(all(&mut f, &[b"TYPE", b"list"]).is_empty());
1755        assert!(all(&mut f, &[b"TYPE", b"banana"]).is_empty());
1756        // Both filters at once, and they are an and rather than an or.
1757        assert!(all(&mut f, &[b"MATCH", b"str*", b"TYPE", b"set"]).is_empty());
1758    }
1759
1760    #[test]
1761    fn a_scan_says_what_is_wrong_with_it() {
1762        let mut f = Fixture::new();
1763        assert_eq!(f.run(&[b"SCAN", b"nope"]), "-ERR invalid cursor\r\n");
1764        assert_eq!(f.run(&[b"SCAN", b"-1"]), "-ERR invalid cursor\r\n");
1765        assert_eq!(f.run(&[b"SCAN", b"0", b"MATCH"]), "-ERR syntax error\r\n");
1766        assert_eq!(
1767            f.run(&[b"SCAN", b"0", b"COUNT", b"0"]),
1768            "-ERR syntax error\r\n"
1769        );
1770        assert_eq!(
1771            f.run(&[b"SCAN", b"0", b"COUNT", b"x"]),
1772            "-ERR value is not an integer or out of range\r\n"
1773        );
1774        assert_eq!(
1775            f.run(&[b"SCAN", b"0", b"WAT", b"1"]),
1776            "-ERR syntax error\r\n"
1777        );
1778        // A cursor the client made up is a cursor. It resumes somewhere
1779        // arbitrary and answers whatever is there, which is what Redis does and
1780        // is the only behaviour that does not need the server to remember every
1781        // cursor it has handed out.
1782        assert!(f.run(&[b"SCAN", b"18446744073709551615"]).starts_with("*2"));
1783    }
1784
1785    #[test]
1786    fn keys_and_randomkey_look_at_the_whole_database() {
1787        let mut f = Fixture::new();
1788        assert_eq!(f.run(&[b"KEYS", b"*"]), "*0\r\n");
1789        assert_eq!(f.run(&[b"RANDOMKEY"]), "$-1\r\n");
1790
1791        for name in ["one", "two", "three"] {
1792            f.run(&[b"SET", name.as_bytes(), b"v"]);
1793        }
1794        assert_eq!(sorted(&f.run(&[b"KEYS", b"*"])), ["one", "three", "two"]);
1795        assert_eq!(sorted(&f.run(&[b"KEYS", b"t*"])), ["three", "two"]);
1796        assert_eq!(f.run(&[b"KEYS", b"nothing"]), "*0\r\n");
1797
1798        for _ in 0..50 {
1799            let got = f.run(&[b"RANDOMKEY"]);
1800            assert!(
1801                ["$3\r\none\r\n", "$3\r\ntwo\r\n", "$5\r\nthree\r\n"].contains(&got.as_str()),
1802                "got {got}"
1803            );
1804        }
1805    }
1806
1807    #[test]
1808    fn a_walk_does_not_answer_keys_that_have_expired() {
1809        let mut f = Fixture::new();
1810        f.run(&[b"SET", b"alive", b"v"]);
1811        f.run(&[b"SET", b"dead", b"v", b"PX", b"1"]);
1812        f.server.db(0).clock_mut().advance(2);
1813        assert_eq!(
1814            f.run(&[b"DBSIZE"]),
1815            ":2\r\n",
1816            "nothing has collected it yet"
1817        );
1818
1819        assert_eq!(f.run(&[b"KEYS", b"*"]), "*1\r\n$5\r\nalive\r\n");
1820        let (_, keys) = scan_reply(&f.run(&[b"SCAN", b"0", b"COUNT", b"1000"]));
1821        assert_eq!(keys, ["alive"]);
1822        for _ in 0..20 {
1823            assert_eq!(f.run(&[b"RANDOMKEY"]), "$5\r\nalive\r\n");
1824        }
1825        // The walk collected it on the way past, which is what makes DBSIZE
1826        // here answer what Redis answers once its own cycle has been round.
1827        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
1828    }
1829
1830    #[test]
1831    fn a_key_deadline_goes_on_and_comes_back_in_all_four_units() {
1832        let mut f = Fixture::new();
1833        f.run(&[b"SET", b"k", b"v"]);
1834        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "there and no deadline");
1835        assert_eq!(f.run(&[b"TTL", b"nosuch"]), ":-2\r\n", "not there at all");
1836
1837        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100"]), ":1\r\n");
1838        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1839        let ms = int(&f.run(&[b"PTTL", b"k"]));
1840        assert!((99_000..=100_000).contains(&ms), "got {ms}");
1841
1842        // The absolute pair, derived from the same one number the store kept.
1843        let at = int(&f.run(&[b"EXPIRETIME", b"k"]));
1844        let at_ms = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1845        assert_eq!(at, (at_ms + 500) / 1000);
1846        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
1847
1848        assert_eq!(f.run(&[b"PERSIST", b"k"]), ":1\r\n");
1849        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n");
1850        assert_eq!(
1851            f.run(&[b"PERSIST", b"k"]),
1852            ":0\r\n",
1853            "nothing to take off the second time"
1854        );
1855        assert_eq!(f.run(&[b"PERSIST", b"nosuch"]), ":0\r\n");
1856        assert_eq!(
1857            f.run(&[b"GET", b"k"]),
1858            "$1\r\nv\r\n",
1859            "and the value went through all of that untouched"
1860        );
1861    }
1862
1863    #[test]
1864    fn every_type_can_be_given_a_deadline_and_it_is_the_same_deadline() {
1865        let mut f = Fixture::new();
1866        f.run(&[b"SET", b"str", b"v"]);
1867        f.run(&[b"SADD", b"set", b"a", b"b"]);
1868        f.run(&[b"HSET", b"hash", b"f", b"v"]);
1869
1870        for key in [b"str".as_slice(), b"set", b"hash"] {
1871            assert_eq!(f.run(&[b"EXPIRE", key, b"100"]), ":1\r\n");
1872            assert_eq!(f.run(&[b"TTL", key]), ":100\r\n");
1873        }
1874        // The body is not touched by any of that, which is the whole reason the
1875        // deadline lives in the record and the body lives somewhere else.
1876        assert_eq!(f.run(&[b"SCARD", b"set"]), ":2\r\n");
1877        assert_eq!(f.run(&[b"HGET", b"hash", b"f"]), "$1\r\nv\r\n");
1878        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
1879    }
1880
1881    #[test]
1882    fn a_deadline_that_has_already_gone_deletes_the_key_now() {
1883        let mut f = Fixture::new();
1884        for key in [b"a".as_slice(), b"b", b"c", b"d"] {
1885            f.run(&[b"SET", key, b"v"]);
1886        }
1887        // Four ways of naming a moment that has passed, and all four are a
1888        // delete answering 1 rather than an error. Zero is a moment, minus one
1889        // is a moment, and the hash field commands refuse the negative one.
1890        assert_eq!(f.run(&[b"EXPIRE", b"a", b"0"]), ":1\r\n");
1891        assert_eq!(f.run(&[b"EXPIRE", b"b", b"-1"]), ":1\r\n");
1892        assert_eq!(f.run(&[b"EXPIREAT", b"c", b"1"]), ":1\r\n");
1893        assert_eq!(f.run(&[b"PEXPIREAT", b"d", b"1"]), ":1\r\n");
1894        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1895        assert_eq!(
1896            f.run(&[b"EXPIRE", b"a", b"100"]),
1897            ":0\r\n",
1898            "and the key really went, so there is nothing to put a deadline on"
1899        );
1900    }
1901
1902    #[test]
1903    fn the_four_conditions_decide_whether_the_deadline_moves() {
1904        let mut f = Fixture::new();
1905        f.run(&[b"SET", b"k", b"v"]);
1906
1907        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX"]), ":0\r\n");
1908        assert_eq!(f.run(&[b"TTL", b"k"]), ":-1\r\n", "and XX left it alone");
1909        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"GT"]), ":0\r\n");
1910        assert_eq!(
1911            f.run(&[b"EXPIRE", b"k", b"100", b"LT"]),
1912            ":1\r\n",
1913            "no deadline reads as infinitely far away, so LT passes where GT fails"
1914        );
1915
1916        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"NX"]), ":0\r\n");
1917        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"GT"]), ":0\r\n");
1918        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1919        assert_eq!(f.run(&[b"EXPIRE", b"k", b"50", b"LT"]), ":1\r\n");
1920        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"GT"]), ":1\r\n");
1921        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1922
1923        // The condition is answered before the past check, so this is a 0 and
1924        // the key survives. The other order would delete it.
1925        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"NX"]), ":0\r\n");
1926        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":1\r\n");
1927        assert_eq!(f.run(&[b"EXPIRE", b"k", b"0", b"XX"]), ":1\r\n");
1928        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n", "and XX let it through");
1929    }
1930
1931    #[test]
1932    fn the_conditions_are_a_set_and_not_a_keyword() {
1933        let mut f = Fixture::new();
1934        f.run(&[b"SET", b"k", b"v"]);
1935
1936        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"nx"]), ":1\r\n");
1937        assert_eq!(
1938            f.run(&[b"EXPIRE", b"k", b"100", b"nx", b"nx"]),
1939            ":0\r\n",
1940            "the same keyword twice means it once, and NX now has a deadline to fail on"
1941        );
1942
1943        // XX with LT is the one pair that is not either of them on its own: LT
1944        // alone would accept a key with no deadline and this does not.
1945        assert_eq!(f.run(&[b"EXPIRE", b"k", b"200", b"xx", b"gt"]), ":1\r\n");
1946        assert_eq!(f.run(&[b"TTL", b"k"]), ":200\r\n");
1947        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"gt", b"xx"]), ":0\r\n");
1948        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]), ":1\r\n");
1949        assert_eq!(f.run(&[b"TTL", b"k"]), ":100\r\n");
1950        f.run(&[b"PERSIST", b"k"]);
1951        assert_eq!(
1952            f.run(&[b"EXPIRE", b"k", b"100", b"XX", b"LT"]),
1953            ":0\r\n",
1954            "where LT on its own would have taken it"
1955        );
1956        assert_eq!(f.run(&[b"EXPIRE", b"k", b"100", b"LT"]), ":1\r\n");
1957    }
1958
1959    #[test]
1960    fn a_key_is_gone_once_its_moment_passes() {
1961        let mut f = Fixture::new();
1962        f.run(&[b"SET", b"k", b"v"]);
1963        f.run(&[b"EXPIRE", b"k", b"100"]);
1964
1965        let at = int(&f.run(&[b"PEXPIRETIME", b"k"]));
1966        f.server.set_clock_ms(at as u64 + 1);
1967        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
1968        assert_eq!(f.run(&[b"TTL", b"k"]), ":-2\r\n");
1969        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
1970        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
1971    }
1972
1973    #[test]
1974    fn the_expiry_commands_refuse_what_a_real_server_refuses() {
1975        let mut f = Fixture::new();
1976        f.run(&[b"SET", b"k", b"v"]);
1977        for (bad, want) in [
1978            (
1979                &[b"EXPIRE".as_slice(), b"k", b"soon"][..],
1980                "-ERR value is not an integer or out of range\r\n",
1981            ),
1982            (
1983                &[b"EXPIRE", b"k", b"100", b"MAYBE"],
1984                "-ERR Unsupported option MAYBE\r\n",
1985            ),
1986            (
1987                &[b"EXPIRE", b"k", b"100", b"NX", b"XX"],
1988                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1989            ),
1990            (
1991                &[b"EXPIRE", b"k", b"100", b"NX", b"GT"],
1992                "-ERR NX and XX, GT or LT options at the same time are not compatible\r\n",
1993            ),
1994            (
1995                &[b"EXPIRE", b"k", b"100", b"GT", b"LT", b"GT"],
1996                "-ERR GT and LT options at the same time are not compatible\r\n",
1997            ),
1998            // Seconds that overflow when multiplied into milliseconds. Every
1999            // message names the command it came from.
2000            (
2001                &[b"EXPIRE", b"k", b"9223372036854775807"],
2002                "-ERR invalid expire time in 'expire' command\r\n",
2003            ),
2004            (
2005                &[b"EXPIREAT", b"k", b"9223372036854775807"],
2006                "-ERR invalid expire time in 'expireat' command\r\n",
2007            ),
2008            (
2009                &[b"PEXPIRE", b"k", b"9223372036854775807"],
2010                "-ERR invalid expire time in 'pexpire' command\r\n",
2011            ),
2012        ] {
2013            assert_eq!(f.run(bad), want, "for {bad:?}");
2014        }
2015        assert_eq!(
2016            f.run(&[b"TTL", b"k"]),
2017            ":-1\r\n",
2018            "and none of those put a deadline on anything"
2019        );
2020
2021        // The one of the four that has no arithmetic to overflow. Redis takes
2022        // it and holds the number as given, and a record here holds forty six
2023        // bits, so it lands in the year 4199 instead. D-17.
2024        assert_eq!(
2025            f.run(&[b"PEXPIREAT", b"k", b"9223372036854775807"]),
2026            ":1\r\n"
2027        );
2028        assert_eq!(f.run(&[b"PEXPIRETIME", b"k"]), ":70368744177663\r\n");
2029    }
2030
2031    #[test]
2032    fn flushing_empties_this_database_or_every_one_of_them() {
2033        let mut f = Fixture::new();
2034        f.run(&[b"SELECT", b"0"]);
2035        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
2036        f.run(&[b"SELECT", b"1"]);
2037        f.run(&[b"SET", b"c", b"3"]);
2038        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
2039        // ASYNC and SYNC are both taken and neither changes anything, since the
2040        // keyspace is empty before the OK goes out either way.
2041        assert_eq!(f.run(&[b"FLUSHDB", b"async"]), "+OK\r\n");
2042        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2043        // Only database one was emptied.
2044        f.run(&[b"SELECT", b"0"]);
2045        assert_eq!(f.run(&[b"DBSIZE"]), ":2\r\n");
2046        assert_eq!(f.run(&[b"FLUSHALL", b"SYNC"]), "+OK\r\n");
2047        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2048        f.run(&[b"SELECT", b"1"]);
2049        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
2050        // Anything else after the name is a syntax error, and so is a third
2051        // argument even when the second one is a word we take.
2052        assert_eq!(f.run(&[b"FLUSHALL", b"nope"]), "-ERR syntax error\r\n");
2053        assert_eq!(
2054            f.run(&[b"FLUSHDB", b"sync", b"sync"]),
2055            "-ERR syntax error\r\n"
2056        );
2057    }
2058
2059    #[test]
2060    fn the_script_cache_and_the_library_set_answer_for_being_empty() {
2061        let mut f = Fixture::new();
2062        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH"]), "+OK\r\n");
2063        assert_eq!(f.run(&[b"SCRIPT", b"FLUSH", b"async"]), "+OK\r\n");
2064        assert_eq!(f.run(&[b"FUNCTION", b"FLUSH", b"SYNC"]), "+OK\r\n");
2065        // Nothing is cached, so nothing is there, one answer per hash asked
2066        // about.
2067        assert_eq!(
2068            f.run(&[b"SCRIPT", b"EXISTS", b"aaaa", b"bbbb"]),
2069            "*2\r\n:0\r\n:0\r\n"
2070        );
2071        assert_eq!(f.run(&[b"FUNCTION", b"LIST"]), "*0\r\n");
2072        assert_eq!(
2073            f.run(&[b"FUNCTION", b"LIST", b"LIBRARYNAME", b"x", b"WITHCODE"]),
2074            "*0\r\n"
2075        );
2076        assert_eq!(
2077            f.run(&[b"FUNCTION", b"DELETE", b"nosuch"]),
2078            "-ERR Library not found\r\n"
2079        );
2080
2081        // Redis's two messages here are its own, one per container, and one of
2082        // them reads like a typo.
2083        assert_eq!(
2084            f.run(&[b"SCRIPT", b"FLUSH", b"nope"]),
2085            "-ERR SCRIPT FLUSH only support SYNC|ASYNC option\r\n"
2086        );
2087        assert_eq!(
2088            f.run(&[b"FUNCTION", b"FLUSH", b"nope"]),
2089            "-ERR FUNCTION FLUSH only supports SYNC|ASYNC option\r\n"
2090        );
2091        // A second argument after the mode is the generic one instead, because
2092        // the count is checked before the word is looked at.
2093        assert_eq!(
2094            f.run(&[b"FUNCTION", b"FLUSH", b"sync", b"sync"]),
2095            "-ERR unknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.\r\n"
2096        );
2097        assert_eq!(
2098            f.run(&[b"FUNCTION", b"LIST", b"bogus"]),
2099            "-ERR Unknown argument bogus\r\n"
2100        );
2101        assert_eq!(
2102            f.run(&[b"SCRIPT", b"EXISTS"]),
2103            "-ERR wrong number of arguments for 'script|exists' command\r\n"
2104        );
2105
2106        // The ones that need an interpreter are not here, and say so rather
2107        // than answering OK to a load that loaded nothing.
2108        assert_eq!(
2109            f.run(&[b"SCRIPT", b"LOAD", b"return 1"]),
2110            "-ERR unknown subcommand 'LOAD'. Try SCRIPT HELP.\r\n"
2111        );
2112        assert_eq!(
2113            f.run(&[b"FUNCTION", b"STATS"]),
2114            "-ERR unknown subcommand 'STATS'. Try FUNCTION HELP.\r\n"
2115        );
2116    }
2117
2118    #[test]
2119    fn a_counter_is_an_integer_and_not_a_string_of_digits() {
2120        let mut f = Fixture::new();
2121        assert_eq!(f.run(&[b"INCR", b"c"]), ":1\r\n");
2122        assert_eq!(f.run(&[b"INCRBY", b"c", b"41"]), ":42\r\n");
2123        assert_eq!(f.run(&[b"DECRBY", b"c", b"2"]), ":40\r\n");
2124        // Read back as a string it is still an integer, written out as digits
2125        // only because somebody asked for them.
2126        assert_eq!(f.run(&[b"GET", b"c"]), "$2\r\n40\r\n");
2127        assert_eq!(f.run(&[b"INCRBYFLOAT", b"c", b"0.5"]), "$4\r\n40.5\r\n");
2128        // A counter that is not a number is the error the store raises and this
2129        // layer only spells, which is the whole point of the split.
2130        f.run(&[b"SET", b"k", b"hello"]);
2131        assert_eq!(
2132            f.run(&[b"INCR", b"k"]),
2133            "-ERR value is not an integer or out of range\r\n"
2134        );
2135        assert_eq!(
2136            f.run(&[b"INCRBYFLOAT", b"c", b"inf"]),
2137            "-ERR increment would produce NaN or Infinity\r\n"
2138        );
2139    }
2140
2141    /// Every one of these was read off a running 8.8. They are the answers a
2142    /// client library's own test suite checks, and the shapes are not
2143    /// guessable: `DIGEST` is hexadecimal in a bulk string, `MSETEX` is an
2144    /// integer, `INCREX` is a pair.
2145    #[test]
2146    fn the_newer_commands_reply_in_the_shapes_a_real_server_sends() {
2147        let mut f = Fixture::new();
2148        assert_eq!(f.run(&[b"SET", b"k", b"hello"]), "+OK\r\n");
2149        // The same digest a real 8.8 answers for the same five bytes, which is
2150        // what makes `IFDEQ` usable against a mixed deployment.
2151        assert_eq!(f.run(&[b"DIGEST", b"k"]), "$16\r\n9555e8555c62dcfd\r\n");
2152        assert_eq!(f.run(&[b"DIGEST", b"nosuch"]), "$-1\r\n");
2153        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"1"]), ":1\r\n");
2154        assert_eq!(f.run(&[b"MSETEX", b"1", b"a", b"2", b"NX"]), ":0\r\n");
2155        assert_eq!(f.run(&[b"GET", b"a"]), "$1\r\n1\r\n");
2156        assert_eq!(f.run(&[b"INCREX", b"n"]), "*2\r\n:1\r\n:1\r\n");
2157        assert_eq!(
2158            f.run(&[b"INCREX", b"n", b"BYINT", b"5", b"UBOUND", b"3"]),
2159            "*2\r\n:1\r\n:0\r\n",
2160            "a refused increment reports the value it left alone and applied nothing"
2161        );
2162        assert_eq!(
2163            f.run(&[
2164                b"INCREX",
2165                b"n",
2166                b"BYINT",
2167                b"5",
2168                b"UBOUND",
2169                b"3",
2170                b"SATURATE"
2171            ]),
2172            "*2\r\n:3\r\n:2\r\n"
2173        );
2174        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"2"]), ":0\r\n");
2175        assert_eq!(f.run(&[b"DELEX", b"a", b"IFEQ", b"1"]), ":1\r\n");
2176    }
2177
2178    #[test]
2179    fn the_same_answers_come_out_in_resp3_spelling() {
2180        let mut f = Fixture::new();
2181        assert!(f.run(&[b"HELLO", b"3"]).starts_with("%7\r\n"));
2182        assert_eq!(f.run(&[b"GET", b"nosuch"]), "_\r\n");
2183        // A float counter is a double on RESP3 and the digits in a bulk string
2184        // on RESP2, and `INCRBYFLOAT` is a bulk string on both.
2185        assert_eq!(
2186            f.run(&[b"INCREX", b"c", b"BYFLOAT", b"1.5"]),
2187            "*2\r\n,1.5\r\n,1.5\r\n"
2188        );
2189        assert_eq!(f.run(&[b"INCRBYFLOAT", b"f", b"2.5"]), "$3\r\n2.5\r\n");
2190        // `RESET` puts the protocol back, which is the part that is easy to
2191        // miss and leaves a pooled connection speaking the wrong one.
2192        assert_eq!(f.run(&[b"RESET"]), "+RESET\r\n");
2193        assert_eq!(f.run(&[b"GET", b"nosuch"]), "$-1\r\n");
2194    }
2195
2196    #[test]
2197    fn a_command_nobody_has_heard_of_is_an_error_and_not_a_closed_socket() {
2198        let mut f = Fixture::new();
2199        let (flow, reply) = f.flow(&[b"NOPE", b"a", b"b"]);
2200        assert_eq!(flow, Flow::Continue);
2201        assert_eq!(
2202            reply,
2203            "-ERR unknown command 'NOPE', with args beginning with: 'a' 'b' \r\n"
2204        );
2205        // A name with a line ending in it cannot write its own frame into the
2206        // stream, which is the reason the error writer maps them to spaces.
2207        let reply = f.run(&[b"NO\r\n+PONG\r\nPE"]);
2208        assert_eq!(reply.matches("\r\n").count(), 1);
2209    }
2210
2211    #[test]
2212    fn arity_is_checked_before_the_command_is() {
2213        let mut f = Fixture::new();
2214        assert_eq!(
2215            f.run(&[b"GET"]),
2216            "-ERR wrong number of arguments for 'get' command\r\n"
2217        );
2218        assert_eq!(
2219            f.run(&[b"MSET", b"k"]),
2220            "-ERR wrong number of arguments for 'mset' command\r\n"
2221        );
2222        // The table says `PING` takes one or more and a real server then
2223        // refuses three, which is the sort of thing that only shows up against
2224        // the real thing.
2225        assert_eq!(
2226            f.run(&[b"PING", b"a", b"b"]),
2227            "-ERR wrong number of arguments for 'ping' command\r\n"
2228        );
2229        assert_eq!(f.run(&[b"PING"]), "+PONG\r\n");
2230        assert_eq!(f.run(&[b"PING", b"hi"]), "$2\r\nhi\r\n");
2231        // `DELEX` takes two or four and nothing between.
2232        assert_eq!(
2233            f.run(&[b"DELEX", b"k", b"IFEQ"]),
2234            "-ERR wrong number of arguments for 'delex' command\r\n"
2235        );
2236    }
2237
2238    /// The option rules, all of them measured against 8.8 rather than read off
2239    /// the documentation. The surprising one is that `SET` accepts the same
2240    /// keyword twice and `INCREX` does not.
2241    #[test]
2242    fn the_option_combinations_are_the_ones_a_real_server_accepts() {
2243        let mut f = Fixture::new();
2244        let syntax = "-ERR syntax error\r\n";
2245        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"XX"]), syntax);
2246        assert_eq!(f.run(&[b"SET", b"k", b"v", b"NX", b"IFEQ", b"a"]), syntax);
2247        assert_eq!(
2248            f.run(&[b"SET", b"k", b"v", b"KEEPTTL", b"EX", b"5"]),
2249            syntax
2250        );
2251        assert_eq!(
2252            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"PX", b"5"]),
2253            syntax
2254        );
2255        assert_eq!(f.run(&[b"SET", b"k", b"v", b"PERSIST"]), syntax);
2256        // Twice is fine, and the last one wins.
2257        assert_eq!(
2258            f.run(&[b"SET", b"k", b"v", b"EX", b"5", b"EX", b"100"]),
2259            "+OK\r\n"
2260        );
2261        assert_eq!(f.run(&[b"SET", b"k", b"v", b"XX", b"XX"]), "+OK\r\n");
2262        assert_eq!(f.run(&[b"SET", b"k", b"v", b"GET", b"GET"]), "$1\r\nv\r\n");
2263        // `INCREX` refuses what `SET` allows.
2264        assert_eq!(
2265            f.run(&[b"INCREX", b"n", b"BYINT", b"1", b"BYINT", b"2"]),
2266            syntax
2267        );
2268        assert_eq!(
2269            f.run(&[b"INCREX", b"n", b"ENX"]),
2270            "-ERR ENX flag requires an expiration\r\n"
2271        );
2272        assert_eq!(
2273            f.run(&[b"INCREX", b"n", b"UBOUND", b"abc"]),
2274            "-ERR UBOUND is not an integer or out of range\r\n"
2275        );
2276        assert_eq!(
2277            f.run(&[b"INCREX", b"n", b"LBOUND", b"10", b"UBOUND", b"5"]),
2278            "-ERR LBOUND can't be greater than UBOUND\r\n"
2279        );
2280        assert_eq!(
2281            f.run(&[b"LCS", b"a", b"b", b"LEN", b"IDX"]),
2282            "-ERR If you want both the length and indexes, please just use IDX.\r\n"
2283        );
2284    }
2285
2286    /// Where the expiration rules bite. The one worth the test is `GETEX` on a
2287    /// key that is not there, which answers null without ever looking at the
2288    /// expiration it was given.
2289    #[test]
2290    fn the_expiry_rules_are_redis_own() {
2291        let mut f = Fixture::new();
2292        let bad = "-ERR invalid expire time in 'set' command\r\n";
2293        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"0"]), bad);
2294        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EX", b"-1"]), bad);
2295        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"0"]), bad);
2296        assert_eq!(
2297            f.run(&[b"SET", b"k", b"v", b"EX", b"9999999999999999"]),
2298            bad
2299        );
2300        assert_eq!(
2301            f.run(&[b"SET", b"k", b"v", b"PX", b"99999999999999999999"]),
2302            "-ERR value is not an integer or out of range\r\n"
2303        );
2304        assert_eq!(
2305            f.run(&[b"SETEX", b"k", b"0", b"v"]),
2306            "-ERR invalid expire time in 'setex' command\r\n"
2307        );
2308        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"0"]), "$-1\r\n");
2309        assert_eq!(f.run(&[b"GETEX", b"nosuch", b"EX", b"abc"]), "$-1\r\n");
2310        assert_eq!(
2311            f.run(&[b"GETEX", b"nosuch", b"KEEPTTL"]),
2312            "-ERR syntax error\r\n",
2313            "the option list is still checked before the key is looked up"
2314        );
2315        // A deadline in the past is accepted and the key goes with it.
2316        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2317        assert_eq!(f.run(&[b"SET", b"k", b"v", b"EXAT", b"1"]), "+OK\r\n");
2318        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2319    }
2320
2321    #[test]
2322    fn mset_takes_its_pairs_from_the_read_buffer() {
2323        let mut f = Fixture::new();
2324        assert_eq!(f.run(&[b"MSET", b"a", b"1", b"b", b"2"]), "+OK\r\n");
2325        assert_eq!(
2326            f.run(&[b"MGET", b"a", b"b", b"nosuch"]),
2327            "*3\r\n$1\r\n1\r\n$1\r\n2\r\n$-1\r\n"
2328        );
2329        assert_eq!(f.run(&[b"MSETNX", b"b", b"9", b"c", b"3"]), ":0\r\n");
2330        assert_eq!(f.run(&[b"MSETNX", b"c", b"3", b"d", b"4"]), ":1\r\n");
2331        assert_eq!(
2332            f.run(&[b"MSETEX", b"2", b"e", b"5"]),
2333            "-ERR wrong number of key-value pairs\r\n"
2334        );
2335        assert_eq!(
2336            f.run(&[b"MSETEX", b"0", b"e", b"5"]),
2337            "-ERR invalid numkeys value\r\n"
2338        );
2339        assert_eq!(
2340            f.run(&[b"MSETEX", b"abc", b"e", b"5"]),
2341            "-ERR invalid numkeys value\r\n"
2342        );
2343    }
2344
2345    #[test]
2346    fn lcs_answers_the_length_the_string_and_the_runs() {
2347        let mut f = Fixture::new();
2348        f.run(&[b"MSET", b"a", b"ohmytext", b"b", b"mynewtext"]);
2349        assert_eq!(f.run(&[b"LCS", b"a", b"b"]), "$6\r\nmytext\r\n");
2350        assert_eq!(f.run(&[b"LCS", b"a", b"b", b"LEN"]), ":6\r\n");
2351        assert_eq!(
2352            f.run(&[b"LCS", b"a", b"b", b"IDX", b"MINMATCHLEN", b"4"]),
2353            "*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"
2354        );
2355        // Without `IDX` the two options that only mean something with it are
2356        // accepted and ignored, which is what a real server does.
2357        assert_eq!(
2358            f.run(&[b"LCS", b"a", b"b", b"MINMATCHLEN", b"4", b"WITHMATCHLEN"]),
2359            "$6\r\nmytext\r\n"
2360        );
2361    }
2362
2363    #[test]
2364    fn select_moves_the_connection_and_the_databases_stay_apart() {
2365        let mut f = Fixture::new();
2366        f.run(&[b"SET", b"k", b"zero"]);
2367        assert_eq!(f.run(&[b"SELECT", b"4"]), "+OK\r\n");
2368        assert_eq!(f.run(&[b"GET", b"k"]), "$-1\r\n");
2369        f.run(&[b"SET", b"k", b"four"]);
2370        assert_eq!(f.run(&[b"SELECT", b"0"]), "+OK\r\n");
2371        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2372        assert_eq!(
2373            f.run(&[b"SELECT", b"99"]),
2374            "-ERR DB index is out of range\r\n"
2375        );
2376        assert_eq!(
2377            f.run(&[b"SELECT", b"-1"]),
2378            "-ERR DB index is out of range\r\n"
2379        );
2380        assert_eq!(
2381            f.run(&[b"SELECT", b"abc"]),
2382            "-ERR value is not an integer or out of range\r\n"
2383        );
2384        // `RESET` brings it back to zero.
2385        f.run(&[b"SELECT", b"4"]);
2386        f.run(&[b"RESET"]);
2387        assert_eq!(f.run(&[b"GET", b"k"]), "$4\r\nzero\r\n");
2388    }
2389
2390    #[test]
2391    fn hello_agrees_on_a_protocol_and_refuses_the_ones_that_do_not_exist() {
2392        let mut f = Fixture::new();
2393        let reply = f.run(&[b"HELLO"]);
2394        assert!(reply.starts_with("*14\r\n"), "{reply}");
2395        assert!(reply.contains("$5\r\nredis\r\n"), "{reply}");
2396        assert!(reply.contains("$5\r\n8.8.0\r\n"), "{reply}");
2397        assert!(
2398            reply.contains(":7\r\n"),
2399            "the connection id is in there: {reply}"
2400        );
2401        assert_eq!(
2402            f.run(&[b"HELLO", b"4"]),
2403            "-NOPROTO unsupported protocol version\r\n"
2404        );
2405        assert_eq!(
2406            f.run(&[b"HELLO", b"abc"]),
2407            "-ERR Protocol version is not an integer or out of range\r\n"
2408        );
2409        assert_eq!(
2410            f.run(&[b"HELLO", b"3", b"SETNAME"]),
2411            "-ERR Syntax error in HELLO option 'SETNAME'\r\n"
2412        );
2413        assert!(
2414            f.run(&[b"HELLO", b"3", b"SETNAME", b"bob"])
2415                .starts_with("%7\r\n")
2416        );
2417        assert_eq!(f.session.name(), b"bob");
2418        f.run(&[b"RESET"]);
2419        assert_eq!(f.session.name(), b"");
2420    }
2421
2422    #[test]
2423    fn command_describes_this_server_in_the_shape_a_driver_reads() {
2424        let mut f = Fixture::new();
2425        let count = format!(":{}\r\n", COMMANDS.len());
2426        assert_eq!(f.run(&[b"COMMAND", b"COUNT"]), count);
2427        let info = f.run(&[b"COMMAND", b"INFO", b"get"]);
2428        assert_eq!(
2429            info,
2430            "*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\
2431             *3\r\n+@read\r\n+@string\r\n+@fast\r\n*0\r\n*0\r\n*0\r\n"
2432        );
2433        // A null in the list, and the plain one: `$-1` and not `*-1`.
2434        assert_eq!(f.run(&[b"COMMAND", b"INFO", b"nosuch"]), "*1\r\n$-1\r\n");
2435        assert_eq!(
2436            f.run(&[b"COMMAND", b"LIST", b"FILTERBY", b"PATTERN", b"getr*"]),
2437            "*1\r\n$8\r\ngetrange\r\n"
2438        );
2439        assert_eq!(
2440            f.run(&[b"COMMAND", b"NOPE"]),
2441            "-ERR unknown subcommand 'NOPE'. Try COMMAND HELP.\r\n"
2442        );
2443    }
2444
2445    /// A cluster aware client asks this question and then routes on the
2446    /// answer, so `MSETEX`, whose keys are not where the table says, is the one
2447    /// that matters.
2448    #[test]
2449    fn command_getkeys_finds_the_keys_including_the_hidden_ones() {
2450        let mut f = Fixture::new();
2451        assert_eq!(
2452            f.run(&[b"COMMAND", b"GETKEYS", b"get", b"k"]),
2453            "*1\r\n$1\r\nk\r\n"
2454        );
2455        assert_eq!(
2456            f.run(&[b"COMMAND", b"GETKEYS", b"mset", b"a", b"1", b"b", b"2"]),
2457            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2458        );
2459        assert_eq!(
2460            f.run(&[
2461                b"COMMAND", b"GETKEYS", b"msetex", b"2", b"a", b"1", b"b", b"2"
2462            ]),
2463            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
2464        );
2465        assert_eq!(
2466            f.run(&[b"COMMAND", b"GETKEYS", b"ping"]),
2467            "-ERR The command has no key arguments\r\n"
2468        );
2469        assert_eq!(
2470            f.run(&[b"COMMAND", b"GETKEYS", b"set"]),
2471            "-ERR Invalid number of arguments specified for command\r\n"
2472        );
2473    }
2474
2475    #[test]
2476    fn config_answers_what_it_can_and_refuses_what_it_cannot() {
2477        let mut f = Fixture::new();
2478        assert_eq!(
2479            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2480            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n"
2481        );
2482        // A pattern matches more than one, and a setting two patterns both ask
2483        // for is still sent once.
2484        let both = f.run(&[b"CONFIG", b"GET", b"maxmemory*", b"maxmemory"]);
2485        assert!(both.starts_with("*6\r\n"), "{both}");
2486        assert_eq!(f.run(&[b"CONFIG", b"GET", b"nosuch"]), "*0\r\n");
2487        assert_eq!(f.run(&[b"CONFIG", b"SET", b"appendonly", b"no"]), "+OK\r\n");
2488        assert_eq!(
2489            f.run(&[b"CONFIG", b"SET", b"appendonly", b"yes"]),
2490            "-ERR CONFIG SET failed (possibly related to argument 'appendonly') - can't set immutable config\r\n"
2491        );
2492        assert_eq!(
2493            f.run(&[b"CONFIG", b"SET", b"nosuch", b"1"]),
2494            "-ERR Unknown option or number of arguments for CONFIG SET - 'nosuch'\r\n"
2495        );
2496        assert_eq!(
2497            f.run(&[b"CONFIG", b"GET"]),
2498            "-ERR wrong number of arguments for 'config|get' command\r\n"
2499        );
2500        // Too few arguments and an odd number of them are different
2501        // complaints, which is the sort of thing only the real server tells
2502        // you.
2503        assert_eq!(
2504            f.run(&[b"CONFIG", b"SET", b"appendonly"]),
2505            "-ERR wrong number of arguments for 'config|set' command\r\n"
2506        );
2507        assert_eq!(
2508            f.run(&[b"CONFIG", b"SET", b"appendonly", b"no", b"maxmemory"]),
2509            "-ERR syntax error\r\n"
2510        );
2511        assert_eq!(f.run(&[b"CONFIG", b"RESETSTAT"]), "+OK\r\n");
2512        assert_eq!(
2513            f.run(&[b"CONFIG", b"REWRITE"]),
2514            "-ERR The server is running without a config file\r\n"
2515        );
2516    }
2517
2518    #[test]
2519    fn the_eviction_policy_reads_back_what_was_written_to_it() {
2520        let mut f = Fixture::new();
2521        assert_eq!(
2522            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2523            "*2\r\n$16\r\nmaxmemory-policy\r\n$10\r\nnoeviction\r\n"
2524        );
2525        assert_eq!(
2526            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"AllKeys-LFU"]),
2527            "+OK\r\n",
2528            "the name is matched without regard to case, like every other one"
2529        );
2530        assert_eq!(
2531            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2532            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2533        );
2534        // And INFO agrees with CONFIG, which it did not when it was a literal.
2535        assert!(
2536            f.run(&[b"INFO", b"memory"])
2537                .contains("maxmemory_policy:allkeys-lfu"),
2538            "INFO and CONFIG disagree about the policy"
2539        );
2540        // The refusal names every legal value in the order the real server's
2541        // enum table lists them, because a client comparing the message compares
2542        // the whole string.
2543        assert_eq!(
2544            f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"garbage"]),
2545            "-ERR CONFIG SET failed (possibly related to argument 'maxmemory-policy') - argument(s) must be one of the following: volatile-lru, volatile-lfu, volatile-random, volatile-ttl, volatile-lrm, allkeys-lru, allkeys-lfu, allkeys-random, allkeys-lrm, noeviction\r\n"
2546        );
2547        // A bad pair leaves the good one in the same command alone, and the
2548        // policy is checked by the same pass that checks the numbers.
2549        assert_eq!(
2550            f.run(&[b"CONFIG", b"GET", b"maxmemory-policy"]),
2551            "*2\r\n$16\r\nmaxmemory-policy\r\n$11\r\nallkeys-lfu\r\n"
2552        );
2553        f.run(&[
2554            b"CONFIG",
2555            b"SET",
2556            b"hash-max-listpack-entries",
2557            b"7",
2558            b"maxmemory-policy",
2559            b"nonsense",
2560        ]);
2561        assert_eq!(
2562            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2563            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n"
2564        );
2565    }
2566
2567    #[test]
2568    fn the_three_eviction_numbers_read_back_too() {
2569        let mut f = Fixture::new();
2570        for (name, default, set) in [
2571            ("maxmemory-samples", "5", "12"),
2572            ("lfu-log-factor", "10", "3"),
2573            ("lfu-decay-time", "1", "60"),
2574        ] {
2575            let get = || {
2576                format!(
2577                    "*2\r\n${}\r\n{name}\r\n${}\r\n{default}\r\n",
2578                    name.len(),
2579                    default.len()
2580                )
2581            };
2582            assert_eq!(f.run(&[b"CONFIG", b"GET", name.as_bytes()]), get());
2583            assert_eq!(
2584                f.run(&[b"CONFIG", b"SET", name.as_bytes(), set.as_bytes()]),
2585                "+OK\r\n"
2586            );
2587            assert_eq!(
2588                f.run(&[b"CONFIG", b"GET", name.as_bytes()]),
2589                format!(
2590                    "*2\r\n${}\r\n{name}\r\n${}\r\n{set}\r\n",
2591                    name.len(),
2592                    set.len()
2593                )
2594            );
2595            // A number that is not a number is refused with the same sentence
2596            // every other number gets, which names the setting the client typed.
2597            assert_eq!(
2598                f.run(&[b"CONFIG", b"SET", name.as_bytes(), b"soon"]),
2599                format!(
2600                    "-ERR CONFIG SET failed (possibly related to argument '{name}') - argument couldn't be parsed into an integer\r\n"
2601                )
2602            );
2603        }
2604    }
2605
2606    #[test]
2607    fn the_memory_limit_reads_back_in_bytes_whatever_the_unit_was() {
2608        let mut f = Fixture::new();
2609        assert_eq!(
2610            f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2611            "*2\r\n$9\r\nmaxmemory\r\n$1\r\n0\r\n",
2612            "no limit is the default"
2613        );
2614        // The pairing is Redis's and it is a trap: the bare letter is a power of
2615        // ten and the one with the b is a power of two.
2616        for (typed, bytes) in [
2617            (&b"1024"[..], "1024"),
2618            (b"1k", "1000"),
2619            (b"1kb", "1024"),
2620            (b"1M", "1000000"),
2621            (b"1Mb", "1048576"),
2622            (b"1gb", "1073741824"),
2623            (b"100mb", "104857600"),
2624        ] {
2625            assert_eq!(f.run(&[b"CONFIG", b"SET", b"maxmemory", typed]), "+OK\r\n");
2626            assert_eq!(
2627                f.run(&[b"CONFIG", b"GET", b"maxmemory"]),
2628                format!("*2\r\n$9\r\nmaxmemory\r\n${}\r\n{bytes}\r\n", bytes.len()),
2629                "set {}",
2630                String::from_utf8_lossy(typed)
2631            );
2632        }
2633        assert!(
2634            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2635            "the report agrees with the setting"
2636        );
2637
2638        // A unit nobody has heard of, and a negative number, which is not a very
2639        // large one however it is spelled.
2640        for bad in [&b"1tb"[..], b"-1", b"", b"lots"] {
2641            assert_eq!(
2642                f.run(&[b"CONFIG", b"SET", b"maxmemory", bad]),
2643                "-ERR CONFIG SET failed (possibly related to argument 'maxmemory') - argument must be a memory value\r\n",
2644                "refused {}",
2645                String::from_utf8_lossy(bad)
2646            );
2647        }
2648        assert!(
2649            f.run(&[b"INFO", b"memory"]).contains("maxmemory:104857600"),
2650            "and the refusal left the old one alone"
2651        );
2652    }
2653
2654    #[test]
2655    fn a_write_is_refused_when_there_is_no_room_and_nothing_to_evict() {
2656        let mut f = Fixture::new();
2657        f.run(&[b"SET", b"here", b"already"]);
2658        // A byte, which is under what an empty server holds, so nothing this
2659        // command could do would get it under. The default policy is
2660        // `noeviction`, so nothing is what it does.
2661        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1"]);
2662        assert_eq!(
2663            f.run(&[b"SET", b"k", b"v"]),
2664            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2665        );
2666        assert_eq!(
2667            f.run(&[b"LPUSH", b"l", b"v"]),
2668            "-OOM command not allowed when used memory > 'maxmemory'.\r\n"
2669        );
2670        // Reading is allowed, and so is the one thing that would help.
2671        assert_eq!(f.run(&[b"GET", b"here"]), "$7\r\nalready\r\n");
2672        assert_eq!(f.run(&[b"DEL", b"here"]), ":1\r\n");
2673        assert!(f.run(&[b"INFO", b"stats"]).contains("evicted_keys:0"));
2674
2675        // Taking the limit away lets the write through again.
2676        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2677        assert_eq!(f.run(&[b"SET", b"k", b"v"]), "+OK\r\n");
2678    }
2679
2680    #[test]
2681    fn an_allkeys_policy_makes_room_instead_of_refusing() {
2682        let mut f = Fixture::new();
2683        let val = vec![b'v'; 256];
2684        for i in 0..24000u32 {
2685            let k = format!("key:{i:08}");
2686            f.run(&[b"SET", k.as_bytes(), &val]);
2687        }
2688        let full = f.server.memory_bytes();
2689        assert!(
2690            full > 3 * 1024 * 1024,
2691            "the arena is several segments: {full}"
2692        );
2693
2694        // Two megabytes under what it is holding, which is one segment's worth,
2695        // so getting there means giving a whole segment back and not just
2696        // dropping a few records.
2697        let limit = full - 2 * 1024 * 1024;
2698        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lru"]);
2699        f.run(&[
2700            b"CONFIG",
2701            b"SET",
2702            b"maxmemory",
2703            limit.to_string().as_bytes(),
2704        ]);
2705
2706        // Writes keep working the whole way down. The budget means one command
2707        // does not do it all, so this runs until the server has settled and
2708        // checks that nothing was refused on the way.
2709        for i in 0..2000u32 {
2710            let k = format!("new:{i:08}");
2711            assert_eq!(
2712                f.run(&[b"SET", k.as_bytes(), &val]),
2713                "+OK\r\n",
2714                "write {i} was refused"
2715            );
2716            f.server.refresh_memory();
2717            if f.server.memory_bytes() <= limit {
2718                break;
2719            }
2720        }
2721        assert!(
2722            f.server.memory_bytes() <= limit,
2723            "it never got under: {} against {limit}",
2724            f.server.memory_bytes()
2725        );
2726        let info = f.run(&[b"INFO", b"stats"]);
2727        assert!(!info.contains("evicted_keys:0"), "{info}");
2728        assert!(
2729            f.run(&[b"DBSIZE"]) != ":0\r\n",
2730            "and it did not empty the database to get there"
2731        );
2732    }
2733
2734    #[test]
2735    fn the_running_total_and_the_walk_agree_on_a_mixed_keyspace() {
2736        // The limit is judged against a number kept as the collections move,
2737        // rather than found by asking all of them, and the two have to be the
2738        // same number or the limit is enforced against a fiction. This does the
2739        // things that move it, which is growing a collection, shrinking one,
2740        // changing its representation, deleting it and reusing its slot, across
2741        // all five types, and checks the two against each other as it goes.
2742        let mut f = Fixture::new();
2743        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2744        let big = vec![b'v'; 200];
2745
2746        for i in 0..400u32 {
2747            let n = i.to_string();
2748            let n = n.as_bytes();
2749            f.run(&[b"SADD", b"s", n]);
2750            f.run(&[b"SADD", b"s2", &big]);
2751            f.run(&[b"HSET", b"h", n, &big]);
2752            f.run(&[b"RPUSH", b"l", &big]);
2753            f.run(&[b"ZADD", b"z", n, n]);
2754            f.run(&[b"ARSET", b"a", n, &big]);
2755            if i % 7 == 0 {
2756                f.run(&[b"SREM", b"s", n]);
2757                f.run(&[b"HDEL", b"h", n]);
2758                f.run(&[b"LPOP", b"l"]);
2759                f.run(&[b"ZREM", b"z", n]);
2760                f.run(&[b"ARDEL", b"a", n]);
2761            }
2762            if i % 53 == 0 {
2763                // Every type deleted and made again, so a slot goes on the free
2764                // list and comes back holding something else.
2765                f.run(&[b"DEL", b"s2"]);
2766            }
2767            assert_eq!(
2768                f.server.settled_memory(),
2769                f.server.memory_bytes(),
2770                "after round {i}"
2771            );
2772        }
2773
2774        // The run has to have built something, or the two numbers agreeing is
2775        // two zeroes agreeing.
2776        assert_eq!(f.run(&[b"DBSIZE"]), ":6\r\n");
2777        assert!(
2778            f.server.memory_bytes() > 512 * 1024,
2779            "{}",
2780            f.server.memory_bytes()
2781        );
2782
2783        // And it survives the collections going away entirely.
2784        f.run(&[b"FLUSHALL"]);
2785        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2786    }
2787
2788    #[test]
2789    fn taking_the_limit_away_stops_the_counting_and_putting_it_back_starts_again() {
2790        // A server with no limit does not keep the running total, so setting a
2791        // limit on a database that is already full has to start it from a walk.
2792        // If it did not, the first reading would be zero and the server would
2793        // think it had all the room in the world.
2794        let mut f = Fixture::new();
2795        for i in 0..200u32 {
2796            let n = i.to_string();
2797            f.run(&[b"SADD", b"s", n.as_bytes()]);
2798            f.run(&[b"HSET", b"h", n.as_bytes(), b"value"]);
2799        }
2800        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2801        assert_eq!(f.server.settled_memory(), f.server.memory_bytes());
2802
2803        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"0"]);
2804        for i in 200..400u32 {
2805            let n = i.to_string();
2806            f.run(&[b"SADD", b"s", n.as_bytes()]);
2807        }
2808        f.run(&[b"CONFIG", b"SET", b"maxmemory", b"1gb"]);
2809        assert_eq!(
2810            f.server.settled_memory(),
2811            f.server.memory_bytes(),
2812            "the writes it was not watching are in the number it started from"
2813        );
2814    }
2815
2816    #[test]
2817    fn evicted_keys_and_expired_keys_are_different_numbers() {
2818        let mut f = Fixture::new();
2819        // Nothing has been evicted and nothing can be under the default policy,
2820        // so this stays at zero while the other one moves.
2821        f.run(&[b"SET", b"gone", b"v", b"PX", b"1"]);
2822        f.server.db(0).clock_mut().advance(20);
2823        f.run(&[b"GET", b"gone"]);
2824        let info = f.run(&[b"INFO", b"stats"]);
2825        assert!(info.contains("expired_keys:1"), "{info}");
2826        assert!(info.contains("evicted_keys:0"), "{info}");
2827    }
2828
2829    #[test]
2830    fn the_object_subcommands_follow_the_policy() {
2831        let mut f = Fixture::new();
2832        f.run(&[b"SET", b"s", b"v"]);
2833        // Under the default the clock is kept and the counter is not, and under
2834        // an LFU policy it is the other way round. Each subcommand refuses on
2835        // the side where its reading of the three bytes means nothing.
2836        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2837        assert!(
2838            f.run(&[b"OBJECT", b"FREQ", b"s"])
2839                .starts_with("-ERR An LFU maxmemory policy is not selected"),
2840        );
2841
2842        f.run(&[b"CONFIG", b"SET", b"maxmemory-policy", b"allkeys-lfu"]);
2843        assert!(
2844            f.run(&[b"OBJECT", b"IDLETIME", b"s"])
2845                .starts_with("-ERR An LFU maxmemory policy is selected"),
2846        );
2847        // The key was written under a clock policy, so what comes back is that
2848        // clock read as a counter. It is a number and not an error, which is the
2849        // point: switching at runtime does not invalidate anything, it only makes
2850        // the old field mean something else until the key is used again.
2851        assert!(
2852            f.run(&[b"OBJECT", b"FREQ", b"s"]).starts_with(':'),
2853            "FREQ should answer under an LFU policy"
2854        );
2855    }
2856
2857    #[test]
2858    fn object_says_which_rung_of_the_ladder_a_key_is_on() {
2859        let mut f = Fixture::new();
2860        f.run(&[b"SET", b"s", b"hello"]);
2861        f.run(&[b"SET", b"n", b"123"]);
2862        f.run(&[b"SADD", b"si", b"1", b"2", b"3"]);
2863        f.run(&[b"SADD", b"ss", b"a", b"b"]);
2864        f.run(&[b"HSET", b"h", b"f", b"v"]);
2865        for (key, want) in [
2866            (b"s".as_slice(), "embstr"),
2867            (b"n", "int"),
2868            (b"si", "intset"),
2869            (b"ss", "listpack"),
2870            (b"h", "listpack"),
2871        ] {
2872            let reply = f.run(&[b"OBJECT", b"ENCODING", key]);
2873            assert_eq!(reply, format!("${}\r\n{want}\r\n", want.len()));
2874        }
2875
2876        // A field deadline widens the blob rather than promoting it, and this
2877        // is the only place a client can see that happen.
2878        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"f"]);
2879        assert_eq!(
2880            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2881            "$10\r\nlistpackex\r\n"
2882        );
2883
2884        assert_eq!(f.run(&[b"OBJECT", b"REFCOUNT", b"s"]), ":1\r\n");
2885        assert_eq!(f.run(&[b"OBJECT", b"IDLETIME", b"s"]), ":0\r\n");
2886        assert!(f.run(&[b"OBJECT", b"HELP"]).starts_with("*14\r\n+OBJECT "));
2887    }
2888
2889    #[test]
2890    fn object_answers_nil_for_a_key_that_is_not_there() {
2891        let mut f = Fixture::new();
2892        for sub in [b"ENCODING".as_slice(), b"REFCOUNT", b"IDLETIME", b"FREQ"] {
2893            assert_eq!(
2894                f.run(&[b"OBJECT", sub, b"nokey"]),
2895                "$-1\r\n",
2896                "a nil and not an error, which is what 8.10.1 does"
2897            );
2898        }
2899        // And the key is looked up before FREQ has its complaint, so the
2900        // complaint only reaches a key that exists.
2901        f.run(&[b"SET", b"s", b"v"]);
2902        assert!(
2903            f.run(&[b"OBJECT", b"FREQ", b"s"])
2904                .starts_with("-ERR An LFU maxmemory policy is not"),
2905        );
2906        assert_eq!(
2907            f.run(&[b"OBJECT", b"NOPE", b"s"]),
2908            "-ERR unknown subcommand 'NOPE'. Try OBJECT HELP.\r\n"
2909        );
2910        assert_eq!(
2911            f.run(&[b"OBJECT", b"ENCODING"]),
2912            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2913        );
2914        assert_eq!(
2915            f.run(&[b"OBJECT", b"ENCODING", b"s", b"extra"]),
2916            "-ERR wrong number of arguments for 'object|encoding' command\r\n"
2917        );
2918        assert_eq!(
2919            f.run(&[b"OBJECT"]),
2920            "-ERR wrong number of arguments for 'object' command\r\n"
2921        );
2922    }
2923
2924    #[test]
2925    fn config_moves_the_ladder_and_object_encoding_agrees() {
2926        let mut f = Fixture::new();
2927        assert_eq!(
2928            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2929            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
2930            "512 and not the 128 everyone remembers, which is what 8.10.1 says"
2931        );
2932        // The old spelling is the same number under a different name, and a
2933        // glob that catches both sends both.
2934        assert_eq!(
2935            f.run(&[b"CONFIG", b"GET", b"hash-max-ziplist-entries"]),
2936            "*2\r\n$24\r\nhash-max-ziplist-entries\r\n$3\r\n512\r\n"
2937        );
2938        assert!(
2939            f.run(&[b"CONFIG", b"GET", b"hash-max-*"])
2940                .starts_with("*8\r\n")
2941        );
2942        assert!(
2943            f.run(&[b"CONFIG", b"GET", b"set-max-*"])
2944                .starts_with("*6\r\n")
2945        );
2946
2947        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2", b"c", b"3"]);
2948        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"h"]), "$8\r\nlistpack\r\n");
2949
2950        assert_eq!(
2951            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"2"]),
2952            "+OK\r\n",
2953            "written under the old name and read back under the new one"
2954        );
2955        assert_eq!(
2956            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2957            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$1\r\n2\r\n"
2958        );
2959        assert_eq!(
2960            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
2961            "$8\r\nlistpack\r\n",
2962            "the hash that already exists is left exactly where it was"
2963        );
2964        f.run(&[b"HSET", b"h2", b"a", b"1", b"b", b"2", b"c", b"3"]);
2965        assert_eq!(
2966            f.run(&[b"OBJECT", b"ENCODING", b"h2"]),
2967            "$9\r\nhashtable\r\n",
2968            "and the next one built goes straight to a table"
2969        );
2970
2971        // The set has three of these and all three move.
2972        f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"2"]);
2973        f.run(&[b"SADD", b"s", b"1", b"2", b"3"]);
2974        assert_eq!(f.run(&[b"OBJECT", b"ENCODING", b"s"]), "$8\r\nlistpack\r\n");
2975        f.run(&[b"CONFIG", b"SET", b"set-max-listpack-value", b"2"]);
2976        f.run(&[b"SADD", b"s2", b"abcdefgh"]);
2977        assert_eq!(
2978            f.run(&[b"OBJECT", b"ENCODING", b"s2"]),
2979            "$9\r\nhashtable\r\n"
2980        );
2981    }
2982
2983    #[test]
2984    fn config_set_takes_all_of_the_ladder_or_none_of_it() {
2985        let mut f = Fixture::new();
2986        assert_eq!(
2987            f.run(&[
2988                b"CONFIG",
2989                b"SET",
2990                b"hash-max-listpack-entries",
2991                b"7",
2992                b"set-max-listpack-entries",
2993                b"abc"
2994            ]),
2995            "-ERR CONFIG SET failed (possibly related to argument 'set-max-listpack-entries') - argument couldn't be parsed into an integer\r\n"
2996        );
2997        assert_eq!(
2998            f.run(&[b"CONFIG", b"GET", b"hash-max-listpack-entries"]),
2999            "*2\r\n$25\r\nhash-max-listpack-entries\r\n$3\r\n512\r\n",
3000            "the pair in front of the bad one did not go in"
3001        );
3002        // The name in the complaint is the one that was typed, so the old
3003        // spelling comes back as the old spelling.
3004        assert_eq!(
3005            f.run(&[b"CONFIG", b"SET", b"hash-max-ziplist-entries", b"abc"]),
3006            "-ERR CONFIG SET failed (possibly related to argument 'hash-max-ziplist-entries') - argument couldn't be parsed into an integer\r\n"
3007        );
3008        assert_eq!(
3009            f.run(&[b"CONFIG", b"SET", b"set-max-intset-entries", b"-1"]),
3010            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument must be between 0 and 9223372036854775807 inclusive\r\n"
3011        );
3012        // A number past what an i64 holds is the parse complaint and not the
3013        // range one, which is upstream reading it before it checks it.
3014        assert_eq!(
3015            f.run(&[
3016                b"CONFIG",
3017                b"SET",
3018                b"set-max-intset-entries",
3019                b"99999999999999999999"
3020            ]),
3021            "-ERR CONFIG SET failed (possibly related to argument 'set-max-intset-entries') - argument couldn't be parsed into an integer\r\n"
3022        );
3023        assert_eq!(
3024            f.run(&[
3025                b"CONFIG",
3026                b"SET",
3027                b"set-max-intset-entries",
3028                b"9223372036854775807"
3029            ]),
3030            "+OK\r\n"
3031        );
3032    }
3033
3034    #[test]
3035    fn a_setting_moved_on_one_database_moved_on_all_of_them() {
3036        let mut f = Fixture::new();
3037        f.run(&[b"CONFIG", b"SET", b"hash-max-listpack-entries", b"1"]);
3038        f.run(&[b"SELECT", b"3"]);
3039        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3040        assert_eq!(
3041            f.run(&[b"OBJECT", b"ENCODING", b"h"]),
3042            "$9\r\nhashtable\r\n",
3043            "these are one server wide number in Redis, whatever a Keyspace carries"
3044        );
3045    }
3046
3047    #[test]
3048    fn info_reports_the_numbers_it_can_stand_behind() {
3049        let mut f = Fixture::new();
3050        f.run(&[b"MSET", b"a", b"1", b"b", b"2"]);
3051        let all = f.run(&[b"INFO"]);
3052        assert!(all.contains("redis_version:8.8.0"), "{all}");
3053        assert!(
3054            all.contains(concat!("yo_version:", env!("CARGO_PKG_VERSION"))),
3055            "{all}"
3056        );
3057        assert!(all.contains("db0:keys=2,expires=0,avg_ttl=0"), "{all}");
3058        assert!(all.contains("role:master"), "{all}");
3059        // One section is one section.
3060        let clients = f.run(&[b"INFO", b"clients"]);
3061        assert!(clients.contains("connected_clients:0"), "{clients}");
3062        assert!(!clients.contains("redis_version"), "{clients}");
3063        assert_eq!(f.run(&[b"INFO", b"nosuch"]), "$0\r\n\r\n");
3064    }
3065
3066    /// A cache that writes with a deadline and never reads back used to hold
3067    /// every key it had ever written, because lazy expiry needs somebody to walk
3068    /// past a key before it can reclaim it and nobody ever did.
3069    #[test]
3070    fn the_active_sweep_reclaims_keys_no_client_comes_back_for() {
3071        let mut f = Fixture::new();
3072        for i in 0..3_000u32 {
3073            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3074        }
3075        for i in 0..1_000u32 {
3076            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3077        }
3078        assert_eq!(f.run(&[b"DBSIZE"]), ":4000\r\n");
3079        f.advance(100);
3080        assert_eq!(
3081            f.run(&[b"DBSIZE"]),
3082            ":4000\r\n",
3083            "DBSIZE counts records and nothing has read past the dead ones yet"
3084        );
3085
3086        // What the shard loop does, one slice at a time.
3087        let mut spent = 0;
3088        for _ in 0..2_000 {
3089            spent += f.server.expire_step(4096);
3090            if f.run(&[b"DBSIZE"]) == ":1000\r\n" {
3091                break;
3092            }
3093        }
3094        assert_eq!(f.run(&[b"DBSIZE"]), ":1000\r\n", "spent {spent} looks");
3095        assert!(f.run(&[b"INFO", b"stats"]).contains("expired_keys:3000"));
3096        for i in 0..1_000u32 {
3097            assert_eq!(
3098                f.run(&[b"GET", format!("k{i}").as_bytes()]),
3099                "$1\r\nv\r\n",
3100                "it took a key that had no deadline"
3101            );
3102        }
3103    }
3104
3105    #[test]
3106    fn a_sweep_of_a_server_with_no_deadlines_anywhere_costs_nothing() {
3107        let mut f = Fixture::new();
3108        for i in 0..2_000u32 {
3109            f.run(&[b"SET", format!("k{i}").as_bytes(), b"v"]);
3110        }
3111        assert_eq!(f.server.expire_step(4096), 0);
3112        // And one database having them does not make the other fifteen pay.
3113        f.run(&[b"SELECT", b"3"]);
3114        f.run(&[b"SET", b"x", b"v", b"PX", b"50"]);
3115        f.advance(100);
3116        for _ in 0..64 {
3117            f.server.expire_step(4096);
3118        }
3119        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3120        f.run(&[b"SELECT", b"0"]);
3121        assert_eq!(f.run(&[b"DBSIZE"]), ":2000\r\n");
3122        assert_eq!(f.server.expire_step(4096), 0, "and it is quiet again");
3123    }
3124
3125    /// The gate, which is what stops a maintenance slice that runs every hundred
3126    /// nanoseconds from drawing a sample every hundred nanoseconds.
3127    #[test]
3128    fn the_sweep_the_loop_calls_runs_at_most_once_a_millisecond() {
3129        let mut f = Fixture::new();
3130        for i in 0..500u32 {
3131            f.run(&[b"SET", format!("d{i}").as_bytes(), b"v", b"PX", b"50"]);
3132        }
3133        f.advance(100);
3134        let at = f.server.db(0).clock().now_ms();
3135        f.server.set_clock_ms(at);
3136        // A small budget, so that one slice cannot finish the job and a second
3137        // one having nothing to do would mean the gate and not an empty
3138        // database.
3139        assert!(f.server.expire_slice(8) > 0, "the first one works");
3140        for _ in 0..1_000 {
3141            assert_eq!(
3142                f.server.expire_slice(8),
3143                0,
3144                "the millisecond has not moved and neither should this"
3145            );
3146        }
3147        assert!(
3148            f.server.db(0).expires() > 400,
3149            "there is plenty left to take"
3150        );
3151        f.server.set_clock_ms(at + 1);
3152        assert!(f.server.expire_slice(8) > 0, "and then it goes again");
3153    }
3154
3155    /// `expires=` used to be a hardcoded zero, which meant a dashboard watching
3156    /// how much of a cache is volatile was reading a constant.
3157    #[test]
3158    fn info_keyspace_counts_the_keys_that_have_a_deadline() {
3159        let mut f = Fixture::new();
3160        f.run(&[b"MSET", b"a", b"1", b"b", b"2", b"c", b"3"]);
3161        assert!(
3162            f.run(&[b"INFO", b"keyspace"])
3163                .contains("db0:keys=3,expires=0"),
3164            "none of them has one yet"
3165        );
3166        f.run(&[b"EXPIRE", b"a", b"1000"]);
3167        f.run(&[b"EXPIRE", b"b", b"1000"]);
3168        let two = f.run(&[b"INFO", b"keyspace"]);
3169        assert!(two.contains("db0:keys=3,expires=2"), "{two}");
3170        f.run(&[b"PERSIST", b"a"]);
3171        f.run(&[b"DEL", b"b"]);
3172        let none = f.run(&[b"INFO", b"keyspace"]);
3173        assert!(none.contains("db0:keys=2,expires=0"), "{none}");
3174
3175        // Each database answers for itself, the way Redis reports it.
3176        f.run(&[b"SELECT", b"1"]);
3177        f.run(&[b"SET", b"x", b"1", b"EX", b"1000"]);
3178        let both = f.run(&[b"INFO", b"keyspace"]);
3179        assert!(both.contains("db0:keys=2,expires=0"), "{both}");
3180        assert!(both.contains("db1:keys=1,expires=1"), "{both}");
3181    }
3182
3183    #[cfg(unix)]
3184    #[test]
3185    fn info_cpu_reports_processor_time_that_was_really_measured() {
3186        let mut f = Fixture::new();
3187        let cpu = f.run(&[b"INFO", b"cpu"]);
3188        assert!(cpu.contains("# CPU"), "{cpu}");
3189        // Redis's unit/info-command asks for this one by name in three tests.
3190        assert!(cpu.contains("used_cpu_user:"), "{cpu}");
3191        assert!(cpu.contains("used_cpu_sys:"), "{cpu}");
3192        assert!(cpu.contains("used_cpu_user_children:0.000000"), "{cpu}");
3193        assert!(!cpu.contains("redis_version"), "{cpu}");
3194
3195        // It is a measurement and not a constant, so it goes up when work
3196        // happens. A tight loop rather than a sleep, because sleeping is the
3197        // one thing that does not move this number.
3198        let before = used_cpu_user(&cpu);
3199        let mut n = 0u64;
3200        let mut rounds = 0;
3201        while used_cpu_user(&f.run(&[b"INFO", b"cpu"])) <= before {
3202            for i in 0..1_000_000u64 {
3203                n = n.wrapping_add(i.wrapping_mul(i));
3204            }
3205            rounds += 1;
3206            // A bound rather than a spin, so a platform where this number does
3207            // not move fails here instead of hanging. Even a clock with whole
3208            // millisecond granularity gets there in the first round or two.
3209            assert!(rounds < 1_000, "cpu time never moved, n is {n}");
3210        }
3211    }
3212
3213    /// Pull `used_cpu_user` back out of an `INFO cpu` reply.
3214    #[cfg(unix)]
3215    fn used_cpu_user(info: &str) -> f64 {
3216        info.lines()
3217            .find_map(|l| l.strip_prefix("used_cpu_user:"))
3218            .expect("no used_cpu_user in the reply")
3219            .trim()
3220            .parse()
3221            .expect("used_cpu_user is not a number")
3222    }
3223
3224    /// The safety net under the rule that a body checks its arguments before
3225    /// it writes anything. `MGET` writes its array header first and then reads
3226    /// each key, so if a later argument could fail the header would already be
3227    /// out. Nothing in the string group does that today and this is what would
3228    /// catch the first one that did.
3229    #[test]
3230    fn a_command_that_fails_leaves_nothing_half_written() {
3231        let mut f = Fixture::new();
3232        let reply = f.run(&[b"SETRANGE", b"k", b"-1", b"x"]);
3233        assert_eq!(reply, "-ERR offset is out of range\r\n");
3234        assert!(!reply.contains(':'), "no integer went out in front of it");
3235    }
3236
3237    #[test]
3238    fn quit_answers_first_and_closes_after() {
3239        let mut f = Fixture::new();
3240        let (flow, reply) = f.flow(&[b"QUIT"]);
3241        assert_eq!(reply, "+OK\r\n");
3242        assert_eq!(flow, Flow::Close);
3243    }
3244
3245    #[test]
3246    fn the_command_counter_counts_every_command_including_the_bad_ones() {
3247        let mut f = Fixture::new();
3248        f.run(&[b"PING"]);
3249        f.run(&[b"NOPE"]);
3250        f.run(&[b"GET"]);
3251        assert_eq!(f.server.stats.commands, 3);
3252    }
3253
3254    #[test]
3255    fn a_set_goes_from_bytes_to_bytes() {
3256        let mut f = Fixture::new();
3257        assert_eq!(f.run(&[b"SADD", b"s", b"a", b"b", b"c"]), ":3\r\n");
3258        assert_eq!(f.run(&[b"SADD", b"s", b"b", b"d"]), ":1\r\n");
3259        assert_eq!(f.run(&[b"SCARD", b"s"]), ":4\r\n");
3260        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"a"]), ":1\r\n");
3261        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"z"]), ":0\r\n");
3262        assert_eq!(f.run(&[b"TYPE", b"s"]), "+set\r\n");
3263        assert_eq!(
3264            f.run(&[b"SMISMEMBER", b"s", b"a", b"z", b"d"]),
3265            "*3\r\n:1\r\n:0\r\n:1\r\n"
3266        );
3267        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"z"]), ":1\r\n");
3268        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3269    }
3270
3271    #[test]
3272    fn a_set_command_at_a_key_that_is_not_there_answers_empty() {
3273        let mut f = Fixture::new();
3274        assert_eq!(f.run(&[b"SCARD", b"nope"]), ":0\r\n");
3275        assert_eq!(f.run(&[b"SISMEMBER", b"nope", b"a"]), ":0\r\n");
3276        assert_eq!(f.run(&[b"SREM", b"nope", b"a"]), ":0\r\n");
3277        assert_eq!(f.run(&[b"SMEMBERS", b"nope"]), "*0\r\n");
3278        assert_eq!(
3279            f.run(&[b"SMISMEMBER", b"nope", b"a", b"b"]),
3280            "*2\r\n:0\r\n:0\r\n"
3281        );
3282        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n", "and made nothing");
3283    }
3284
3285    #[test]
3286    fn smembers_answers_a_set_on_resp3_and_an_array_on_resp2() {
3287        // Not cosmetic. A RESP3 client that gets a `~` hands the caller a set
3288        // and one that gets a `*` hands it a list, without either of them being
3289        // told which command was sent.
3290        let mut f = Fixture::new();
3291        f.run(&[b"SADD", b"s", b"one"]);
3292        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$3\r\none\r\n");
3293
3294        f.run(&[b"HELLO", b"3"]);
3295        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "~1\r\n$3\r\none\r\n");
3296    }
3297
3298    #[test]
3299    fn an_integer_member_comes_back_as_the_digits_it_never_stored() {
3300        // An intset holds the number, so these digits exist for the first time
3301        // in the reply buffer.
3302        let mut f = Fixture::new();
3303        f.run(&[b"SADD", b"s", b"42"]);
3304        assert_eq!(f.run(&[b"SMEMBERS", b"s"]), "*1\r\n$2\r\n42\r\n");
3305        assert_eq!(f.run(&[b"SISMEMBER", b"s", b"42"]), ":1\r\n");
3306        assert_eq!(
3307            f.run(&[b"SISMEMBER", b"s", b"042"]),
3308            ":0\r\n",
3309            "the member is the bytes and not the number they parse to"
3310        );
3311    }
3312
3313    #[test]
3314    fn the_wrong_command_at_the_wrong_type_says_so_both_ways() {
3315        let mut f = Fixture::new();
3316        f.run(&[b"SET", b"str", b"v"]);
3317        f.run(&[b"SADD", b"set", b"a"]);
3318
3319        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3320        assert_eq!(f.run(&[b"SADD", b"str", b"a"]), wrong);
3321        assert_eq!(f.run(&[b"SCARD", b"str"]), wrong);
3322        assert_eq!(f.run(&[b"SMEMBERS", b"str"]), wrong);
3323        assert_eq!(f.run(&[b"SMISMEMBER", b"str", b"a"]), wrong);
3324        assert_eq!(f.run(&[b"GET", b"set"]), wrong);
3325        assert_eq!(f.run(&[b"APPEND", b"set", b"x"]), wrong);
3326        assert_eq!(f.run(&[b"INCR", b"set"]), wrong);
3327        assert_eq!(f.run(&[b"STRLEN", b"set"]), wrong);
3328
3329        // MGET is the one that does not, because Redis gives nil for the odd
3330        // key out rather than failing the good keys next to it.
3331        assert_eq!(
3332            f.run(&[b"MGET", b"str", b"set", b"nope"]),
3333            "*3\r\n$1\r\nv\r\n$-1\r\n$-1\r\n"
3334        );
3335        // And plain SET overwrites any type, which takes the body with it.
3336        assert_eq!(f.run(&[b"SET", b"set", b"now a string"]), "+OK\r\n");
3337        assert_eq!(f.run(&[b"TYPE", b"set"]), "+string\r\n");
3338    }
3339
3340    #[test]
3341    fn a_wrongtype_leaves_nothing_half_written() {
3342        // SMISMEMBER writes an array header and then one reply per member, so
3343        // it is the first command in the server that could get a header out in
3344        // front of an error if it checked its key in the wrong order.
3345        let mut f = Fixture::new();
3346        f.run(&[b"SET", b"k", b"v"]);
3347        let reply = f.run(&[b"SMISMEMBER", b"k", b"a", b"b"]);
3348        assert!(reply.starts_with("-WRONGTYPE"), "got {reply}");
3349        assert!(!reply.contains('*'), "an array header went out in front");
3350    }
3351
3352    #[test]
3353    fn emptying_a_set_takes_the_key_with_it() {
3354        let mut f = Fixture::new();
3355        f.run(&[b"SADD", b"s", b"a", b"b"]);
3356        assert_eq!(f.run(&[b"DBSIZE"]), ":1\r\n");
3357        assert_eq!(f.run(&[b"SREM", b"s", b"a", b"b"]), ":2\r\n");
3358        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3359        assert_eq!(f.run(&[b"TYPE", b"s"]), "+none\r\n");
3360        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
3361    }
3362
3363    /// Pull the cursor and the members out of one `SSCAN` reply.
3364    ///
3365    /// Crude on purpose. A test that walked a set through a real client would
3366    /// be testing the client, and what these tests are about is the shape of
3367    /// the bytes and the fact that a walk sees every member once.
3368    fn split_scan(reply: &str) -> (String, Vec<String>) {
3369        let mut lines = reply.split("\r\n");
3370        assert_eq!(lines.next(), Some("*2"), "got {reply}");
3371        lines.next().expect("the cursor header");
3372        let cursor = lines.next().expect("the cursor").to_owned();
3373        let header = lines.next().expect("the member header");
3374        let n: usize = header[1..].parse().expect("a member count");
3375        let mut members = Vec::with_capacity(n);
3376        for _ in 0..n {
3377            lines.next().expect("a member header");
3378            members.push(lines.next().expect("a member").to_owned());
3379        }
3380        (cursor, members)
3381    }
3382
3383    #[test]
3384    fn popping_takes_a_member_off_the_set_and_hands_it_back() {
3385        let mut f = Fixture::new();
3386        f.run(&[b"SADD", b"s", b"a", b"b", b"c", b"d"]);
3387
3388        let one = f.run(&[b"SPOP", b"s"]);
3389        assert!(
3390            ["$1\r\na\r\n", "$1\r\nb\r\n", "$1\r\nc\r\n", "$1\r\nd\r\n"].contains(&one.as_str()),
3391            "got {one}"
3392        );
3393        assert_eq!(f.run(&[b"SCARD", b"s"]), ":3\r\n");
3394
3395        // A count takes that many, and the last one takes the key with it.
3396        let (_, rest) = ("", f.run(&[b"SPOP", b"s", b"3"]));
3397        assert!(rest.starts_with("*3\r\n"), "got {rest}");
3398        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":0\r\n");
3399        // And a pop at a key that is not there is a nil, not an empty bulk.
3400        assert_eq!(f.run(&[b"SPOP", b"s"]), "$-1\r\n");
3401        assert_eq!(f.run(&[b"SPOP", b"s", b"2"]), "*0\r\n");
3402    }
3403
3404    #[test]
3405    fn the_two_draws_disagree_about_the_reply_type_and_they_are_right_to() {
3406        // The one place in the server where the reply type carries something
3407        // the command name does not. SPOP's members are distinct so a RESP3
3408        // client can build a set out of them. SRANDMEMBER with a negative count
3409        // can hand back the same member three times, and a set would lose two.
3410        let mut f = Fixture::new();
3411        f.run(&[b"HELLO", b"3"]);
3412        f.run(&[b"SADD", b"s", b"a", b"b", b"c"]);
3413
3414        assert!(f.run(&[b"SPOP", b"s", b"2"]).starts_with("~2\r\n"));
3415        // And a positive count is an array too, since Redis makes it one.
3416        assert!(f.run(&[b"SRANDMEMBER", b"s", b"1"]).starts_with("*1\r\n"));
3417
3418        // A negative count against a set of one is where the difference bites:
3419        // the same member three times, which is a three element reply and would
3420        // have been a one element reply if it had gone out as a set.
3421        f.run(&[b"SADD", b"one", b"z"]);
3422        assert_eq!(
3423            f.run(&[b"SRANDMEMBER", b"one", b"-3"]),
3424            "*3\r\n$1\r\nz\r\n$1\r\nz\r\n$1\r\nz\r\n"
3425        );
3426    }
3427
3428    #[test]
3429    fn drawing_a_member_removes_nothing_and_says_nil_at_a_missing_key() {
3430        let mut f = Fixture::new();
3431        f.run(&[b"SADD", b"s", b"only"]);
3432        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3433        assert_eq!(f.run(&[b"SRANDMEMBER", b"s"]), "$4\r\nonly\r\n");
3434        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n");
3435
3436        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope"]), "$-1\r\n");
3437        // The count form answers an empty array rather than a nil, which is the
3438        // pair of answers Redis gives and is not the pair it looks like.
3439        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"3"]), "*0\r\n");
3440        assert_eq!(f.run(&[b"SRANDMEMBER", b"nope", b"-3"]), "*0\r\n");
3441        // Asking for more than is there answers all of it once and not padding.
3442        assert_eq!(f.run(&[b"SRANDMEMBER", b"s", b"9"]), "*1\r\n$4\r\nonly\r\n");
3443    }
3444
3445    #[test]
3446    fn a_pop_count_that_is_not_a_positive_number_says_so() {
3447        let mut f = Fixture::new();
3448        f.run(&[b"SADD", b"s", b"a"]);
3449        let bad = "-ERR value is out of range, must be positive\r\n";
3450        assert_eq!(f.run(&[b"SPOP", b"s", b"-1"]), bad);
3451        assert_eq!(f.run(&[b"SPOP", b"s", b"abc"]), bad);
3452        assert_eq!(f.run(&[b"SCARD", b"s"]), ":1\r\n", "and took nothing");
3453        // Zero is allowed and is a real answer rather than an error.
3454        assert_eq!(f.run(&[b"SPOP", b"s", b"0"]), "*0\r\n");
3455        assert_eq!(f.run(&[b"EXISTS", b"s"]), ":1\r\n");
3456    }
3457
3458    #[test]
3459    fn a_scan_walks_a_set_of_any_size_exactly_once() {
3460        let mut f = Fixture::new();
3461        let members: Vec<Vec<u8>> = (0..300).map(|i| format!("m{i}").into_bytes()).collect();
3462        let args: Vec<&[u8]> = [&b"SADD"[..], &b"s"[..]]
3463            .into_iter()
3464            .chain(members.iter().map(Vec::as_slice))
3465            .collect();
3466        f.run(&args);
3467
3468        let mut seen = Vec::new();
3469        let mut cursor = "0".to_owned();
3470        loop {
3471            let reply = f.run(&[b"SSCAN", b"s", cursor.as_bytes()]);
3472            let (next, got) = split_scan(&reply);
3473            seen.extend(got);
3474            cursor = next;
3475            if cursor == "0" {
3476                break;
3477            }
3478        }
3479        seen.sort();
3480        seen.dedup();
3481        assert_eq!(seen.len(), 300, "a walk saw a member twice or missed one");
3482
3483        // A set small enough to be a listpack answers in one call whatever
3484        // cursor it was handed, which is what Redis does for that encoding.
3485        f.run(&[b"SADD", b"small", b"a", b"b", b"c"]);
3486        let (cursor, got) = split_scan(&f.run(&[b"SSCAN", b"small", b"0", b"COUNT", b"1"]));
3487        assert_eq!(cursor, "0");
3488        assert_eq!(got.len(), 3);
3489        // And a key that is not there is a finished scan of nothing.
3490        assert_eq!(f.run(&[b"SSCAN", b"nope", b"0"]), "*2\r\n$1\r\n0\r\n*0\r\n");
3491    }
3492
3493    #[test]
3494    fn a_scan_takes_match_and_count_and_refuses_anything_else() {
3495        let mut f = Fixture::new();
3496        f.run(&[b"SADD", b"s", b"aa", b"ab", b"ba", b"12", b"13"]);
3497
3498        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"a*"]));
3499        let mut got = got;
3500        got.sort();
3501        assert_eq!(got, ["aa", "ab"]);
3502
3503        // An integer member has no digits stored anywhere, so MATCH is the one
3504        // place a scan pays to write some.
3505        let (_, got) = split_scan(&f.run(&[b"SSCAN", b"s", b"0", b"MATCH", b"1?"]));
3506        let mut got = got;
3507        got.sort();
3508        assert_eq!(got, ["12", "13"]);
3509
3510        assert_eq!(f.run(&[b"SSCAN", b"s", b"abc"]), "-ERR invalid cursor\r\n");
3511        assert_eq!(f.run(&[b"SSCAN", b"s", b"-1"]), "-ERR invalid cursor\r\n");
3512        assert_eq!(
3513            f.run(&[b"SSCAN", b"s", b"0", b"NOPE", b"1"]),
3514            "-ERR syntax error\r\n"
3515        );
3516        // A count under one is a syntax error and not a range error, which is
3517        // the odder of Redis's two answers and the reason it is copied exactly.
3518        assert_eq!(
3519            f.run(&[b"SSCAN", b"s", b"0", b"COUNT", b"0"]),
3520            "-ERR syntax error\r\n"
3521        );
3522    }
3523
3524    #[test]
3525    fn moving_a_member_takes_it_off_one_set_and_puts_it_on_another() {
3526        let mut f = Fixture::new();
3527        f.run(&[b"SADD", b"src", b"a", b"b"]);
3528        f.run(&[b"SADD", b"dst", b"c"]);
3529
3530        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"a"]), ":1\r\n");
3531        assert_eq!(f.run(&[b"SISMEMBER", b"src", b"a"]), ":0\r\n");
3532        assert_eq!(f.run(&[b"SISMEMBER", b"dst", b"a"]), ":1\r\n");
3533        // A member that is not in the source is a zero and moves nothing.
3534        assert_eq!(f.run(&[b"SMOVE", b"src", b"dst", b"zz"]), ":0\r\n");
3535        assert_eq!(f.run(&[b"SCARD", b"dst"]), ":2\r\n");
3536
3537        // A destination that does not exist gets made, and a source that runs
3538        // out goes away.
3539        assert_eq!(f.run(&[b"SMOVE", b"src", b"fresh", b"b"]), ":1\r\n");
3540        assert_eq!(f.run(&[b"EXISTS", b"src"]), ":0\r\n");
3541        assert_eq!(f.run(&[b"SMEMBERS", b"fresh"]), "*1\r\n$1\r\nb\r\n");
3542    }
3543
3544    #[test]
3545    fn moving_checks_the_types_in_the_order_redis_checks_them() {
3546        // Not the order it looks like it should be. A source that is not there
3547        // answers zero without ever looking at the destination, so this is a
3548        // zero and not a WRONGTYPE even though the destination is a string.
3549        let mut f = Fixture::new();
3550        f.run(&[b"SET", b"str", b"v"]);
3551        f.run(&[b"SADD", b"set", b"a"]);
3552
3553        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3554        assert_eq!(f.run(&[b"SMOVE", b"nope", b"str", b"a"]), ":0\r\n");
3555        assert_eq!(f.run(&[b"SMOVE", b"str", b"set", b"a"]), wrong);
3556        assert_eq!(f.run(&[b"SMOVE", b"set", b"str", b"a"]), wrong);
3557        assert_eq!(f.run(&[b"SPOP", b"str"]), wrong);
3558        assert_eq!(f.run(&[b"SRANDMEMBER", b"str"]), wrong);
3559        assert_eq!(f.run(&[b"SSCAN", b"str", b"0"]), wrong);
3560        assert_eq!(
3561            f.run(&[b"SISMEMBER", b"set", b"a"]),
3562            ":1\r\n",
3563            "and none of that moved anything"
3564        );
3565    }
3566
3567    #[test]
3568    fn a_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
3569        // SSCAN writes an outer array header before it walks, so it is the
3570        // command most likely to get bytes out in front of an error.
3571        let mut f = Fixture::new();
3572        f.run(&[b"SADD", b"s", b"a"]);
3573        for bad in [
3574            &[b"SSCAN".as_slice(), b"s", b"abc"][..],
3575            &[b"SSCAN".as_slice(), b"s", b"0", b"COUNT", b"nope"][..],
3576            &[b"SSCAN".as_slice(), b"s", b"0", b"MATCH"][..],
3577        ] {
3578            let reply = f.run(bad);
3579            assert!(reply.starts_with("-ERR"), "got {reply}");
3580            assert!(!reply.contains('*'), "an array header went out in front");
3581        }
3582    }
3583
3584    #[test]
3585    fn a_hash_writes_reads_and_deletes_its_fields() {
3586        let mut f = Fixture::new();
3587        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]), ":2\r\n");
3588        assert_eq!(f.run(&[b"HSET", b"h", b"a", b"9"]), ":0\r\n", "a was there");
3589        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
3590        assert_eq!(f.run(&[b"HGET", b"h", b"nope"]), "$-1\r\n");
3591        assert_eq!(f.run(&[b"HGET", b"nokey", b"a"]), "$-1\r\n");
3592        assert_eq!(f.run(&[b"HLEN", b"h"]), ":2\r\n");
3593        assert_eq!(f.run(&[b"HEXISTS", b"h", b"a"]), ":1\r\n");
3594        assert_eq!(f.run(&[b"HEXISTS", b"h", b"nope"]), ":0\r\n");
3595        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"a"]), ":1\r\n");
3596        assert_eq!(f.run(&[b"HSTRLEN", b"h", b"nope"]), ":0\r\n");
3597
3598        // The value the client sent is `9`, so HGET h b must not find the `2`
3599        // that is a value. A search with a step of one would have.
3600        assert_eq!(f.run(&[b"HGET", b"h", b"2"]), "$-1\r\n");
3601
3602        assert_eq!(f.run(&[b"HDEL", b"h", b"a", b"nope"]), ":1\r\n");
3603        assert_eq!(f.run(&[b"HDEL", b"h", b"b"]), ":1\r\n");
3604        assert_eq!(
3605            f.run(&[b"EXISTS", b"h"]),
3606            ":0\r\n",
3607            "and losing the last field lost the key"
3608        );
3609    }
3610
3611    #[test]
3612    fn hgetall_answers_a_map_on_resp3_and_the_same_pairs_flat_on_resp2() {
3613        let mut f = Fixture::new();
3614        f.run(&[b"HSET", b"h", b"a", b"1"]);
3615        assert_eq!(f.run(&[b"HGETALL", b"h"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
3616        assert_eq!(f.run(&[b"HGETALL", b"nokey"]), "*0\r\n");
3617        assert_eq!(f.run(&[b"HKEYS", b"h"]), "*1\r\n$1\r\na\r\n");
3618        assert_eq!(f.run(&[b"HVALS", b"h"]), "*1\r\n$1\r\n1\r\n");
3619        assert_eq!(f.run(&[b"HKEYS", b"nokey"]), "*0\r\n");
3620
3621        f.run(&[b"HELLO", b"3"]);
3622        assert_eq!(f.run(&[b"HGETALL", b"h"]), "%1\r\n$1\r\na\r\n$1\r\n1\r\n");
3623        assert_eq!(
3624            f.run(&[b"HGETALL", b"nokey"]),
3625            "%0\r\n",
3626            "a missing key is the empty hash and never a nil"
3627        );
3628        assert_eq!(
3629            f.run(&[b"HKEYS", b"h"]),
3630            "*1\r\n$1\r\na\r\n",
3631            "and the two that answer one side stay arrays"
3632        );
3633    }
3634
3635    #[test]
3636    fn hmget_answers_once_per_field_and_hmset_answers_ok() {
3637        let mut f = Fixture::new();
3638        assert_eq!(f.run(&[b"HMSET", b"h", b"a", b"1", b"c", b"3"]), "+OK\r\n");
3639        assert_eq!(
3640            f.run(&[b"HMGET", b"h", b"a", b"b", b"c"]),
3641            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n",
3642            "the reply is positional, so b is a nil and not a gap"
3643        );
3644        assert_eq!(
3645            f.run(&[b"HMGET", b"nokey", b"a", b"b"]),
3646            "*2\r\n$-1\r\n$-1\r\n",
3647            "and a missing key is all nils rather than an empty array"
3648        );
3649
3650        assert_eq!(f.run(&[b"HSETNX", b"h", b"a", b"9"]), ":0\r\n");
3651        assert_eq!(f.run(&[b"HSETNX", b"h", b"z", b"9"]), ":1\r\n");
3652        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
3653    }
3654
3655    #[test]
3656    fn a_hash_counts_up_and_says_so_when_it_cannot() {
3657        let mut f = Fixture::new();
3658        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"5"]), ":5\r\n");
3659        assert_eq!(f.run(&[b"HINCRBY", b"h", b"n", b"-7"]), ":-2\r\n");
3660        assert_eq!(f.run(&[b"HGET", b"h", b"n"]), "$2\r\n-2\r\n");
3661        assert_eq!(
3662            f.run(&[b"HINCRBYFLOAT", b"h", b"f", b"10.5"]),
3663            "$4\r\n10.5\r\n",
3664            "a bulk string and not a double, on both protocols"
3665        );
3666
3667        f.run(&[b"HSET", b"h", b"s", b"words"]);
3668        let bad = f.run(&[b"HINCRBY", b"h", b"s", b"1"]);
3669        assert!(
3670            bad.starts_with("-ERR hash value is not an integer"),
3671            "{bad}"
3672        );
3673        let bad = f.run(&[b"HINCRBY", b"h", b"n", b"nope"]);
3674        assert!(
3675            bad.starts_with("-ERR value is not an integer"),
3676            "a bad argument is not yet a hash value, {bad}"
3677        );
3678        assert_eq!(
3679            f.run(&[b"HGET", b"h", b"s"]),
3680            "$5\r\nwords\r\n",
3681            "and neither of them wrote anything"
3682        );
3683    }
3684
3685    #[test]
3686    fn a_hash_scan_walks_every_pair_once_and_novalues_drops_half_of_it() {
3687        let mut f = Fixture::new();
3688        for i in 0..500 {
3689            let field = format!("field-{i}");
3690            let value = format!("value-{i}");
3691            f.run(&[b"HSET", b"h", field.as_bytes(), value.as_bytes()]);
3692        }
3693
3694        let mut seen: Vec<String> = Vec::new();
3695        let mut cursor = "0".to_owned();
3696        loop {
3697            let reply = f.run(&[b"HSCAN", b"h", cursor.as_bytes(), b"COUNT", b"32"]);
3698            let (next, items) = scan_reply(&reply);
3699            assert_eq!(items.len() % 2, 0, "a pair went out half written");
3700            for pair in items.chunks(2) {
3701                assert_eq!(
3702                    pair[0].strip_prefix("field-"),
3703                    pair[1].strip_prefix("value-"),
3704                    "a field came back with someone else's value"
3705                );
3706                seen.push(pair[0].clone());
3707            }
3708            cursor = next;
3709            if cursor == "0" {
3710                break;
3711            }
3712        }
3713        seen.sort();
3714        seen.dedup();
3715        assert_eq!(seen.len(), 500, "every field once and only once");
3716
3717        let (_, items) = scan_reply(&f.run(&[b"HSCAN", b"h", b"0", b"NOVALUES", b"COUNT", b"32"]));
3718        assert!(
3719            items.iter().all(|s| s.starts_with("field-")),
3720            "NOVALUES still sent the values"
3721        );
3722
3723        let (_, one) = scan_reply(&f.run(&[
3724            b"HSCAN",
3725            b"h",
3726            b"0",
3727            b"MATCH",
3728            b"field-499",
3729            b"COUNT",
3730            b"1000",
3731        ]));
3732        assert_eq!(one, ["field-499", "value-499"], "MATCH is on the field");
3733    }
3734
3735    #[test]
3736    fn hrandfield_draws_what_it_was_asked_for_and_nests_values_on_resp3() {
3737        let mut f = Fixture::new();
3738        f.run(&[b"HSET", b"h", b"a", b"1"]);
3739        assert_eq!(f.run(&[b"HRANDFIELD", b"h"]), "$1\r\na\r\n");
3740        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey"]), "$-1\r\n");
3741        assert_eq!(f.run(&[b"HRANDFIELD", b"nokey", b"3"]), "*0\r\n");
3742        assert_eq!(
3743            f.run(&[b"HRANDFIELD", b"h", b"3"]),
3744            "*1\r\n$1\r\na\r\n",
3745            "a positive count is capped at the size of the hash"
3746        );
3747        assert_eq!(
3748            f.run(&[b"HRANDFIELD", b"h", b"-3"]),
3749            "*3\r\n$1\r\na\r\n$1\r\na\r\n$1\r\na\r\n",
3750            "and a negative one repeats itself"
3751        );
3752        assert_eq!(
3753            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3754            "*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3755            "flat on RESP2"
3756        );
3757
3758        f.run(&[b"HELLO", b"3"]);
3759        assert_eq!(
3760            f.run(&[b"HRANDFIELD", b"h", b"1", b"WITHVALUES"]),
3761            "*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n",
3762            "and nested on RESP3, but still an array and never a map"
3763        );
3764    }
3765
3766    #[test]
3767    fn every_hash_command_says_wrongtype_and_writes_nothing() {
3768        let mut f = Fixture::new();
3769        f.run(&[b"SET", b"str", b"v"]);
3770        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
3771
3772        for cmd in [
3773            &[b"HSET".as_slice(), b"str", b"f", b"v"][..],
3774            &[b"HMSET".as_slice(), b"str", b"f", b"v"][..],
3775            &[b"HSETNX".as_slice(), b"str", b"f", b"v"][..],
3776            &[b"HGET".as_slice(), b"str", b"f"][..],
3777            &[b"HMGET".as_slice(), b"str", b"f"][..],
3778            &[b"HDEL".as_slice(), b"str", b"f"][..],
3779            &[b"HLEN".as_slice(), b"str"][..],
3780            &[b"HEXISTS".as_slice(), b"str", b"f"][..],
3781            &[b"HSTRLEN".as_slice(), b"str", b"f"][..],
3782            &[b"HGETALL".as_slice(), b"str"][..],
3783            &[b"HKEYS".as_slice(), b"str"][..],
3784            &[b"HVALS".as_slice(), b"str"][..],
3785            &[b"HINCRBY".as_slice(), b"str", b"f", b"1"][..],
3786            &[b"HINCRBYFLOAT".as_slice(), b"str", b"f", b"1"][..],
3787            &[b"HRANDFIELD".as_slice(), b"str"][..],
3788            &[b"HRANDFIELD".as_slice(), b"str", b"2"][..],
3789            &[b"HSCAN".as_slice(), b"str", b"0"][..],
3790        ] {
3791            let reply = f.run(cmd);
3792            assert_eq!(reply, wrong, "{:?}", cmd[0]);
3793        }
3794        assert_eq!(
3795            f.run(&[b"GET", b"str"]),
3796            "$1\r\nv\r\n",
3797            "and none of them touched the value"
3798        );
3799    }
3800
3801    #[test]
3802    fn a_hash_scan_leaves_nothing_half_written_when_its_arguments_are_wrong() {
3803        let mut f = Fixture::new();
3804        f.run(&[b"HSET", b"h", b"f", b"v"]);
3805        for bad in [
3806            &[b"HSCAN".as_slice(), b"h", b"abc"][..],
3807            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"nope"][..],
3808            &[b"HSCAN".as_slice(), b"h", b"0", b"COUNT", b"0"][..],
3809            &[b"HSCAN".as_slice(), b"h", b"0", b"MATCH"][..],
3810        ] {
3811            let reply = f.run(bad);
3812            assert!(reply.starts_with("-ERR"), "got {reply}");
3813            assert!(!reply.contains('*'), "an array header went out in front");
3814        }
3815    }
3816
3817    #[test]
3818    fn a_field_deadline_goes_on_and_comes_back_in_all_four_units() {
3819        let mut f = Fixture::new();
3820        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3821        assert_eq!(
3822            f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]),
3823            "*1\r\n:1\r\n"
3824        );
3825        assert_eq!(
3826            f.run(&[b"HTTL", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3827            "*3\r\n:100\r\n:-1\r\n:-2\r\n",
3828            "one answer per field, and the two sentinels are TTL's own"
3829        );
3830
3831        // The same deadline in the other three units, all of them derived from
3832        // the one number the store kept.
3833        let ms = int_reply(&f.run(&[b"HPTTL", b"h", b"FIELDS", b"1", b"a"]));
3834        assert!((99_000..=100_000).contains(&ms), "got {ms}");
3835        let at = int_reply(&f.run(&[b"HEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3836        let at_ms = int_reply(&f.run(&[b"HPEXPIRETIME", b"h", b"FIELDS", b"1", b"a"]));
3837        assert_eq!(at, at_ms.div_euclid(1000) + i64::from(at_ms % 1000 != 0));
3838        assert!(at_ms > 1_700_000_000_000, "an absolute moment, got {at_ms}");
3839
3840        assert_eq!(
3841            f.run(&[b"HPERSIST", b"h", b"FIELDS", b"3", b"a", b"b", b"nope"]),
3842            "*3\r\n:1\r\n:-1\r\n:-2\r\n",
3843            "one for the deadline taken off, and it does not say what it was"
3844        );
3845        assert_eq!(
3846            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3847            "*1\r\n:-1\r\n"
3848        );
3849        assert_eq!(
3850            f.run(&[b"HGET", b"h", b"a"]),
3851            "$1\r\n1\r\n",
3852            "and the field is still there with the value it had"
3853        );
3854    }
3855
3856    #[test]
3857    fn a_deadline_that_has_already_gone_deletes_the_field_now() {
3858        let mut f = Fixture::new();
3859        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3860        assert_eq!(
3861            f.run(&[b"HEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"a"]),
3862            "*1\r\n:2\r\n",
3863            "two, and not one, because nothing was stored"
3864        );
3865        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3866        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3867
3868        assert_eq!(
3869            f.run(&[b"HPEXPIREAT", b"h", b"1", b"FIELDS", b"1", b"b"]),
3870            "*1\r\n:2\r\n"
3871        );
3872        assert_eq!(
3873            f.run(&[b"EXISTS", b"h"]),
3874            ":0\r\n",
3875            "and the last field going took the key with it"
3876        );
3877
3878        // Zero is a delete and not an error, where minus one is an error. That
3879        // is Redis's split and it is easy to get backwards.
3880        f.run(&[b"HSET", b"h", b"a", b"1"]);
3881        assert_eq!(
3882            f.run(&[b"HEXPIRE", b"h", b"0", b"FIELDS", b"1", b"a"]),
3883            "*1\r\n:2\r\n"
3884        );
3885    }
3886
3887    #[test]
3888    fn a_field_is_gone_once_its_moment_passes() {
3889        let mut f = Fixture::new();
3890        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
3891        assert_eq!(
3892            f.run(&[b"HPEXPIRE", b"h", b"20", b"FIELDS", b"1", b"a"]),
3893            "*1\r\n:1\r\n"
3894        );
3895        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n", "not yet");
3896
3897        // Time moves once per turn of the event loop and nowhere else, so a
3898        // test moves it by hand rather than by sleeping. There is nothing to
3899        // sleep for: the deadline is a number and so is the clock.
3900        f.server.db(0).clock_mut().advance(60);
3901        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
3902        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$-1\r\n");
3903        assert_eq!(
3904            f.run(&[b"HGETALL", b"h"]),
3905            "*2\r\n$1\r\nb\r\n$1\r\n2\r\n",
3906            "and the walks do not hand back a field that has expired"
3907        );
3908    }
3909
3910    #[test]
3911    fn a_missing_key_answers_the_no_field_sentinel_for_every_field() {
3912        let mut f = Fixture::new();
3913        for cmd in [
3914            &[
3915                b"HEXPIRE".as_slice(),
3916                b"nokey",
3917                b"100",
3918                b"FIELDS",
3919                b"2",
3920                b"a",
3921                b"b",
3922            ][..],
3923            &[b"HTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3924            &[b"HPTTL".as_slice(), b"nokey", b"FIELDS", b"2", b"a", b"b"][..],
3925            &[
3926                b"HEXPIRETIME".as_slice(),
3927                b"nokey",
3928                b"FIELDS",
3929                b"2",
3930                b"a",
3931                b"b",
3932            ][..],
3933            &[
3934                b"HPERSIST".as_slice(),
3935                b"nokey",
3936                b"FIELDS",
3937                b"2",
3938                b"a",
3939                b"b",
3940            ][..],
3941        ] {
3942            assert_eq!(f.run(cmd), "*2\r\n:-2\r\n:-2\r\n", "{:?}", cmd[0]);
3943        }
3944    }
3945
3946    #[test]
3947    fn writing_a_field_clears_the_deadline_that_was_on_it() {
3948        let mut f = Fixture::new();
3949        f.run(&[b"HSET", b"h", b"a", b"1"]);
3950        f.run(&[b"HEXPIRE", b"h", b"100", b"FIELDS", b"1", b"a"]);
3951        f.run(&[b"HSET", b"h", b"a", b"2"]);
3952        assert_eq!(
3953            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3954            "*1\r\n:-1\r\n",
3955            "Redis has done this since 7.4, and it is why HGETEX exists"
3956        );
3957    }
3958
3959    #[test]
3960    fn the_four_conditions_reach_the_store_the_way_they_were_written() {
3961        let mut f = Fixture::new();
3962        f.run(&[b"HSET", b"h", b"a", b"1"]);
3963        assert_eq!(
3964            f.run(&[b"HEXPIRE", b"h", b"100", b"XX", b"FIELDS", b"1", b"a"]),
3965            "*1\r\n:0\r\n",
3966            "XX on a field with no deadline changes nothing"
3967        );
3968        assert_eq!(
3969            f.run(&[b"HEXPIRE", b"h", b"100", b"NX", b"FIELDS", b"1", b"a"]),
3970            "*1\r\n:1\r\n"
3971        );
3972        assert_eq!(
3973            f.run(&[b"HEXPIRE", b"h", b"200", b"NX", b"FIELDS", b"1", b"a"]),
3974            "*1\r\n:0\r\n",
3975            "and NX will not move one that is already there"
3976        );
3977        assert_eq!(
3978            f.run(&[b"HEXPIRE", b"h", b"50", b"GT", b"FIELDS", b"1", b"a"]),
3979            "*1\r\n:0\r\n"
3980        );
3981        assert_eq!(
3982            f.run(&[b"HEXPIRE", b"h", b"500", b"GT", b"FIELDS", b"1", b"a"]),
3983            "*1\r\n:1\r\n"
3984        );
3985        assert_eq!(
3986            f.run(&[b"HEXPIRE", b"h", b"50", b"LT", b"FIELDS", b"1", b"a"]),
3987            "*1\r\n:1\r\n"
3988        );
3989        assert_eq!(
3990            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
3991            "*1\r\n:50\r\n"
3992        );
3993    }
3994
3995    #[test]
3996    fn the_field_ttl_family_leaves_nothing_half_written_on_a_bad_argument() {
3997        let mut f = Fixture::new();
3998        f.run(&[b"HSET", b"h", b"a", b"1"]);
3999        for (bad, want) in [
4000            (
4001                &[b"HEXPIRE".as_slice(), b"h", b"-1", b"FIELDS", b"1", b"a"][..],
4002                "-ERR invalid expire time, must be >= 0",
4003            ),
4004            (
4005                &[
4006                    b"HEXPIRE".as_slice(),
4007                    b"h",
4008                    b"9999999999999999",
4009                    b"FIELDS",
4010                    b"1",
4011                    b"a",
4012                ][..],
4013                "-ERR invalid expire time in 'hexpire' command",
4014            ),
4015            (
4016                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELD", b"1", b"a"][..],
4017                "-ERR wrong number of arguments for 'hexpire' command",
4018            ),
4019            (
4020                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"0", b"a"][..],
4021                "-ERR Parameter `numFields` should be greater than 0",
4022            ),
4023            (
4024                &[b"HEXPIRE".as_slice(), b"h", b"100", b"FIELDS", b"2", b"a"][..],
4025                "-ERR wrong number of arguments",
4026            ),
4027            (
4028                &[b"HTTL".as_slice(), b"h", b"FIELDS", b"3", b"a", b"b"][..],
4029                "-ERR wrong number of arguments",
4030            ),
4031        ] {
4032            let reply = f.run(bad);
4033            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4034            assert!(!reply.contains('*'), "an array header went out in front");
4035        }
4036        assert_eq!(
4037            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4038            "*1\r\n:-1\r\n",
4039            "and not one of them put a deadline on anything"
4040        );
4041    }
4042
4043    #[test]
4044    fn every_field_ttl_command_says_wrongtype_and_writes_nothing() {
4045        let mut f = Fixture::new();
4046        f.run(&[b"SET", b"str", b"v"]);
4047        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4048
4049        for cmd in [
4050            &[b"HEXPIRE".as_slice(), b"str", b"100", b"FIELDS", b"1", b"f"][..],
4051            &[
4052                b"HPEXPIRE".as_slice(),
4053                b"str",
4054                b"100",
4055                b"FIELDS",
4056                b"1",
4057                b"f",
4058            ][..],
4059            &[
4060                b"HEXPIREAT".as_slice(),
4061                b"str",
4062                b"9999999999",
4063                b"FIELDS",
4064                b"1",
4065                b"f",
4066            ][..],
4067            &[
4068                b"HPEXPIREAT".as_slice(),
4069                b"str",
4070                b"9999999999999",
4071                b"FIELDS",
4072                b"1",
4073                b"f",
4074            ][..],
4075            &[b"HTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4076            &[b"HPTTL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4077            &[b"HEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4078            &[b"HPEXPIRETIME".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4079            &[b"HPERSIST".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4080        ] {
4081            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4082        }
4083        assert_eq!(
4084            f.run(&[b"GET", b"str"]),
4085            "$1\r\nv\r\n",
4086            "and none of them touched the value"
4087        );
4088    }
4089
4090    #[test]
4091    fn hgetdel_hands_the_value_out_and_then_takes_the_field() {
4092        let mut f = Fixture::new();
4093        f.run(&[b"HSET", b"h", b"a", b"1", b"b", b"2"]);
4094        assert_eq!(
4095            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"2", b"a", b"nope"]),
4096            "*2\r\n$1\r\n1\r\n$-1\r\n",
4097            "positional, so the field that was not there is a nil in its place"
4098        );
4099        assert_eq!(f.run(&[b"HLEN", b"h"]), ":1\r\n");
4100        assert_eq!(
4101            f.run(&[b"HGETDEL", b"nokey", b"FIELDS", b"1", b"a"]),
4102            "*1\r\n$-1\r\n"
4103        );
4104        assert_eq!(
4105            f.run(&[b"HGETDEL", b"h", b"FIELDS", b"1", b"b"]),
4106            "*1\r\n$1\r\n2\r\n"
4107        );
4108        assert_eq!(
4109            f.run(&[b"EXISTS", b"h"]),
4110            ":0\r\n",
4111            "and the last field took the key"
4112        );
4113    }
4114
4115    #[test]
4116    fn hgetex_reads_and_moves_the_deadline_in_one_command() {
4117        let mut f = Fixture::new();
4118        f.run(&[b"HSET", b"h", b"a", b"1"]);
4119        assert_eq!(
4120            f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]),
4121            "*1\r\n$1\r\n1\r\n"
4122        );
4123        assert_eq!(
4124            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4125            "*1\r\n:-1\r\n",
4126            "no option means leave it alone, which is the one place this is not GETEX"
4127        );
4128
4129        f.run(&[b"HGETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a"]);
4130        assert_eq!(
4131            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4132            "*1\r\n:100\r\n"
4133        );
4134        f.run(&[b"HGETEX", b"h", b"FIELDS", b"1", b"a"]);
4135        assert_eq!(
4136            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4137            "*1\r\n:100\r\n",
4138            "and a plain read really does leave it alone"
4139        );
4140        assert_eq!(
4141            f.run(&[b"HGETEX", b"h", b"PERSIST", b"FIELDS", b"1", b"a"]),
4142            "*1\r\n$1\r\n1\r\n"
4143        );
4144        assert_eq!(
4145            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4146            "*1\r\n:-1\r\n"
4147        );
4148
4149        assert_eq!(
4150            f.run(&[b"HGETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a"]),
4151            "*1\r\n$1\r\n1\r\n",
4152            "the value goes out before the deadline that has already gone is applied"
4153        );
4154        assert_eq!(f.run(&[b"EXISTS", b"h"]), ":0\r\n");
4155        assert_eq!(
4156            f.run(&[b"HGETEX", b"nokey", b"EX", b"100", b"FIELDS", b"1", b"a"]),
4157            "*1\r\n$-1\r\n"
4158        );
4159    }
4160
4161    #[test]
4162    fn hsetex_writes_all_of_it_or_none_of_it() {
4163        let mut f = Fixture::new();
4164        assert_eq!(
4165            f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"1"]),
4166            ":1\r\n"
4167        );
4168        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4169        assert_eq!(
4170            f.run(&[
4171                b"HSETEX", b"h", b"FNX", b"FIELDS", b"2", b"a", b"9", b"new", b"9"
4172            ]),
4173            ":0\r\n",
4174            "FNX wants every field named to be missing"
4175        );
4176        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4177        assert_eq!(
4178            f.run(&[b"HEXISTS", b"h", b"new"]),
4179            ":0\r\n",
4180            "and none of the list was written"
4181        );
4182        assert_eq!(
4183            f.run(&[
4184                b"HSETEX", b"h", b"FXX", b"FIELDS", b"2", b"a", b"9", b"nope", b"9"
4185            ]),
4186            ":0\r\n",
4187            "and FXX wants every one of them to be there"
4188        );
4189        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n1\r\n");
4190        assert_eq!(
4191            f.run(&[b"HSETEX", b"h", b"FXX", b"FIELDS", b"1", b"a", b"9"]),
4192            ":1\r\n"
4193        );
4194        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n9\r\n");
4195
4196        assert_eq!(
4197            f.run(&[b"HSETEX", b"gone", b"FXX", b"FIELDS", b"1", b"a", b"1"]),
4198            ":0\r\n"
4199        );
4200        assert_eq!(
4201            f.run(&[b"EXISTS", b"gone"]),
4202            ":0\r\n",
4203            "a key with no fields cannot meet FXX and is not created trying"
4204        );
4205    }
4206
4207    #[test]
4208    fn hsetex_clears_the_deadline_unless_it_is_told_to_keep_it() {
4209        let mut f = Fixture::new();
4210        f.run(&[b"HSETEX", b"h", b"EX", b"100", b"FIELDS", b"1", b"a", b"1"]);
4211        assert_eq!(
4212            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4213            "*1\r\n:100\r\n"
4214        );
4215
4216        f.run(&[b"HSETEX", b"h", b"KEEPTTL", b"FIELDS", b"1", b"a", b"2"]);
4217        assert_eq!(f.run(&[b"HGET", b"h", b"a"]), "$1\r\n2\r\n");
4218        assert_eq!(
4219            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4220            "*1\r\n:100\r\n",
4221            "KEEPTTL put back what the write cleared"
4222        );
4223
4224        f.run(&[b"HSETEX", b"h", b"FIELDS", b"1", b"a", b"3"]);
4225        assert_eq!(
4226            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4227            "*1\r\n:-1\r\n",
4228            "and without it a write clears the deadline the way HSET does"
4229        );
4230
4231        // Any order, because Redis reads these in a loop and not in a fixed
4232        // sequence.
4233        assert_eq!(
4234            f.run(&[
4235                b"HSETEX", b"h", b"PX", b"100000", b"FXX", b"FIELDS", b"1", b"a", b"4"
4236            ]),
4237            ":1\r\n"
4238        );
4239        assert_eq!(
4240            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4241            "*1\r\n:100\r\n"
4242        );
4243
4244        assert_eq!(
4245            f.run(&[b"HSETEX", b"h", b"EXAT", b"1", b"FIELDS", b"1", b"a", b"5"]),
4246            ":1\r\n",
4247            "written, and not the separate code the HEXPIRE family has for this"
4248        );
4249        assert_eq!(
4250            f.run(&[b"EXISTS", b"h"]),
4251            ":0\r\n",
4252            "and storing it and then removing it emptied the hash"
4253        );
4254    }
4255
4256    #[test]
4257    fn the_last_three_hash_commands_word_their_mistakes_their_own_way() {
4258        let mut f = Fixture::new();
4259        f.run(&[b"HSET", b"h", b"a", b"1"]);
4260        for (bad, want) in [
4261            // HGETDEL has three sentences of its own for these three mistakes.
4262            (
4263                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4264                "-ERR Number of fields must be a positive integer",
4265            ),
4266            (
4267                &[b"HGETDEL".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4268                "-ERR The `numfields` parameter must match the number of arguments",
4269            ),
4270            (
4271                &[b"HGETDEL".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4272                "-ERR Mandatory argument FIELDS is missing or not at the right position",
4273            ),
4274            // And HGETEX and HSETEX have three different ones between them.
4275            (
4276                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"0", b"a"][..],
4277                "-ERR invalid number of fields",
4278            ),
4279            (
4280                &[b"HGETEX".as_slice(), b"h", b"FIELDS", b"2", b"a"][..],
4281                "-ERR wrong number of arguments",
4282            ),
4283            (
4284                &[b"HGETEX".as_slice(), b"h", b"FIELD", b"1", b"a"][..],
4285                "-ERR unknown argument: FIELD",
4286            ),
4287            (
4288                &[
4289                    b"HGETEX".as_slice(),
4290                    b"h",
4291                    b"KEEPTTL",
4292                    b"FIELDS",
4293                    b"1",
4294                    b"a",
4295                ][..],
4296                "-ERR unknown argument: KEEPTTL",
4297            ),
4298            (
4299                &[
4300                    b"HGETEX".as_slice(),
4301                    b"h",
4302                    b"EX",
4303                    b"100",
4304                    b"PERSIST",
4305                    b"FIELDS",
4306                    b"1",
4307                    b"a",
4308                ][..],
4309                "-ERR Only one of EX, PX, EXAT, PXAT or PERSIST arguments can be specified",
4310            ),
4311            (
4312                &[
4313                    b"HSETEX".as_slice(),
4314                    b"h",
4315                    b"EX",
4316                    b"1",
4317                    b"KEEPTTL",
4318                    b"FIELDS",
4319                    b"1",
4320                    b"a",
4321                    b"1",
4322                ][..],
4323                "-ERR Only one of EX, PX, EXAT, PXAT or KEEPTTL arguments can be specified",
4324            ),
4325            (
4326                &[
4327                    b"HSETEX".as_slice(),
4328                    b"h",
4329                    b"FNX",
4330                    b"FXX",
4331                    b"FIELDS",
4332                    b"1",
4333                    b"a",
4334                    b"1",
4335                ][..],
4336                "-ERR Only one of FXX or FNX arguments can be specified",
4337            ),
4338            (
4339                &[
4340                    b"HSETEX".as_slice(),
4341                    b"h",
4342                    b"FIELDS",
4343                    b"2",
4344                    b"a",
4345                    b"1",
4346                    b"b",
4347                ][..],
4348                "-ERR wrong number of arguments",
4349            ),
4350            (
4351                &[
4352                    b"HGETEX".as_slice(),
4353                    b"h",
4354                    b"EX",
4355                    b"-1",
4356                    b"FIELDS",
4357                    b"1",
4358                    b"a",
4359                ][..],
4360                "-ERR invalid expire time, must be >= 0",
4361            ),
4362            (
4363                &[
4364                    b"HGETEX".as_slice(),
4365                    b"h",
4366                    b"PXAT",
4367                    b"99999999999999",
4368                    b"FIELDS",
4369                    b"1",
4370                    b"a",
4371                ][..],
4372                "-ERR invalid expire time in 'hgetex' command",
4373            ),
4374            (
4375                &[
4376                    b"HSETEX".as_slice(),
4377                    b"h",
4378                    b"EX",
4379                    b"abc",
4380                    b"FIELDS",
4381                    b"1",
4382                    b"a",
4383                    b"1",
4384                ][..],
4385                "-ERR value is not an integer or out of range",
4386            ),
4387        ] {
4388            let reply = f.run(bad);
4389            assert!(reply.starts_with(want), "wanted {want}, got {reply}");
4390            assert!(!reply.contains('*'), "an array header went out in front");
4391        }
4392        assert_eq!(
4393            f.run(&[b"HGET", b"h", b"a"]),
4394            "$1\r\n1\r\n",
4395            "and not one of them wrote anything"
4396        );
4397        assert_eq!(
4398            f.run(&[b"HTTL", b"h", b"FIELDS", b"1", b"a"]),
4399            "*1\r\n:-1\r\n"
4400        );
4401    }
4402
4403    #[test]
4404    fn the_last_three_hash_commands_say_wrongtype_and_write_nothing() {
4405        let mut f = Fixture::new();
4406        f.run(&[b"SET", b"str", b"v"]);
4407        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4408        for cmd in [
4409            &[b"HGETDEL".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4410            &[b"HGETEX".as_slice(), b"str", b"FIELDS", b"1", b"f"][..],
4411            &[
4412                b"HGETEX".as_slice(),
4413                b"str",
4414                b"EX",
4415                b"100",
4416                b"FIELDS",
4417                b"1",
4418                b"f",
4419            ][..],
4420            &[b"HSETEX".as_slice(), b"str", b"FIELDS", b"1", b"f", b"v"][..],
4421        ] {
4422            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
4423        }
4424        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
4425    }
4426
4427    /// The one integer of a single element array reply.
4428    /// The number out of a plain integer reply.
4429    ///
4430    /// [`int_reply`] is the same thing wrapped in a one element array, which is
4431    /// the shape every hash field command answers in.
4432    fn int(reply: &str) -> i64 {
4433        let body = reply
4434            .strip_prefix(':')
4435            .and_then(|s| s.strip_suffix("\r\n"))
4436            .unwrap_or_else(|| panic!("wanted an integer, got {reply}"));
4437        body.parse().expect("an integer")
4438    }
4439
4440    fn int_reply(reply: &str) -> i64 {
4441        let body = reply
4442            .strip_prefix("*1\r\n:")
4443            .and_then(|s| s.strip_suffix("\r\n"))
4444            .unwrap_or_else(|| panic!("wanted one integer, got {reply}"));
4445        body.parse().expect("an integer")
4446    }
4447
4448    /// The cursor and the flat items of a scan reply.
4449    fn scan_reply(reply: &str) -> (String, Vec<String>) {
4450        let mut lines = reply.split("\r\n");
4451        assert_eq!(lines.next(), Some("*2"), "got {reply}");
4452        lines.next().expect("the cursor header");
4453        let cursor = lines.next().expect("a cursor").to_owned();
4454        let header = lines.next().expect("an item count");
4455        let n: usize = header[1..].parse().expect("a count");
4456        let mut items = Vec::with_capacity(n);
4457        for _ in 0..n {
4458            lines.next().expect("an item header");
4459            items.push(lines.next().expect("an item").to_owned());
4460        }
4461        (cursor, items)
4462    }
4463
4464    /// The members of a set reply, sorted, since none of these promise an
4465    /// order and a test that asserted one would be asserting an accident.
4466    fn sorted(reply: &str) -> Vec<String> {
4467        let mut lines = reply.split("\r\n");
4468        let header = lines.next().expect("a header");
4469        assert!(
4470            header.starts_with('*') || header.starts_with('~'),
4471            "got {reply}"
4472        );
4473        let n: usize = header[1..].parse().expect("a member count");
4474        let mut got = Vec::with_capacity(n);
4475        for _ in 0..n {
4476            lines.next().expect("a member header");
4477            got.push(lines.next().expect("a member").to_owned());
4478        }
4479        got.sort();
4480        got
4481    }
4482
4483    #[test]
4484    fn the_algebra_answers_what_the_sets_share_and_do_not() {
4485        let mut f = Fixture::new();
4486        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4487        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4488        f.run(&[b"SADD", b"c", b"3", b"4", b"5"]);
4489
4490        assert_eq!(sorted(&f.run(&[b"SINTER", b"a", b"b", b"c"])), ["3"]);
4491        assert_eq!(
4492            sorted(&f.run(&[b"SUNION", b"a", b"b", b"c"])),
4493            ["1", "2", "3", "4", "5"]
4494        );
4495        assert_eq!(sorted(&f.run(&[b"SDIFF", b"a", b"b"])), ["1"]);
4496        assert_eq!(sorted(&f.run(&[b"SINTER", b"a"])), ["1", "2", "3"]);
4497
4498        // A key that is not there is an empty set, which empties an
4499        // intersection and does nothing at all to a union.
4500        assert_eq!(f.run(&[b"SINTER", b"a", b"nope"]), "*0\r\n");
4501        assert_eq!(sorted(&f.run(&[b"SUNION", b"a", b"nope"])), ["1", "2", "3"]);
4502        assert_eq!(f.run(&[b"SDIFF", b"nope", b"a"]), "*0\r\n");
4503        assert_eq!(f.run(&[b"DBSIZE"]), ":3\r\n", "and none of it made a key");
4504    }
4505
4506    #[test]
4507    fn the_algebra_answers_a_set_on_resp3_and_an_array_on_resp2() {
4508        let mut f = Fixture::new();
4509        f.run(&[b"SADD", b"a", b"x"]);
4510        assert_eq!(f.run(&[b"SINTER", b"a"]), "*1\r\n$1\r\nx\r\n");
4511        assert_eq!(f.run(&[b"SUNION", b"a"]), "*1\r\n$1\r\nx\r\n");
4512        assert_eq!(f.run(&[b"SDIFF", b"a"]), "*1\r\n$1\r\nx\r\n");
4513
4514        f.run(&[b"HELLO", b"3"]);
4515        assert_eq!(f.run(&[b"SINTER", b"a"]), "~1\r\n$1\r\nx\r\n");
4516        assert_eq!(f.run(&[b"SUNION", b"a"]), "~1\r\n$1\r\nx\r\n");
4517        assert_eq!(f.run(&[b"SDIFF", b"a"]), "~1\r\n$1\r\nx\r\n");
4518        assert_eq!(f.run(&[b"SINTER", b"nope"]), "~0\r\n");
4519    }
4520
4521    #[test]
4522    fn a_store_form_writes_a_key_and_answers_how_big_it_is() {
4523        let mut f = Fixture::new();
4524        f.run(&[b"SADD", b"a", b"1", b"2", b"3"]);
4525        f.run(&[b"SADD", b"b", b"2", b"3", b"4"]);
4526
4527        assert_eq!(f.run(&[b"SINTERSTORE", b"d", b"a", b"b"]), ":2\r\n");
4528        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["2", "3"]);
4529        assert_eq!(f.run(&[b"SUNIONSTORE", b"d", b"a", b"b"]), ":4\r\n");
4530        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"d"])), ["1", "2", "3", "4"]);
4531        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"b"]), ":1\r\n");
4532        assert_eq!(f.run(&[b"SMEMBERS", b"d"]), "*1\r\n$1\r\n1\r\n");
4533
4534        // An empty answer deletes the destination rather than leaving an empty
4535        // set behind, and the destination may be one of the sources.
4536        assert_eq!(f.run(&[b"SDIFFSTORE", b"d", b"a", b"a"]), ":0\r\n");
4537        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
4538        assert_eq!(f.run(&[b"SINTERSTORE", b"a", b"a", b"b"]), ":2\r\n");
4539        assert_eq!(sorted(&f.run(&[b"SMEMBERS", b"a"])), ["2", "3"]);
4540
4541        // And a destination holding something else is overwritten, the same way
4542        // SET overwrites, rather than refused.
4543        f.run(&[b"SET", b"str", b"v"]);
4544        assert_eq!(f.run(&[b"SUNIONSTORE", b"str", b"b"]), ":3\r\n");
4545        assert_eq!(f.run(&[b"TYPE", b"str"]), "+set\r\n");
4546    }
4547
4548    #[test]
4549    fn sintercard_counts_without_building_and_stops_at_a_limit() {
4550        let mut f = Fixture::new();
4551        f.run(&[b"SADD", b"a", b"1", b"2", b"3", b"4"]);
4552        f.run(&[b"SADD", b"b", b"2", b"3", b"4", b"5"]);
4553
4554        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"b"]), ":3\r\n");
4555        assert_eq!(
4556            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"2"]),
4557            ":2\r\n"
4558        );
4559        assert_eq!(
4560            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"0"]),
4561            ":3\r\n",
4562            "a limit of zero is no limit"
4563        );
4564        assert_eq!(f.run(&[b"SINTERCARD", b"1", b"a"]), ":4\r\n");
4565        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"nope"]), ":0\r\n");
4566
4567        // The counted keys are what make its three error messages its own.
4568        assert_eq!(
4569            f.run(&[b"SINTERCARD", b"0", b"a"]),
4570            "-ERR numkeys should be greater than 0\r\n"
4571        );
4572        assert_eq!(
4573            f.run(&[b"SINTERCARD", b"abc", b"a"]),
4574            "-ERR numkeys should be greater than 0\r\n"
4575        );
4576        assert_eq!(
4577            f.run(&[b"SINTERCARD", b"3", b"a", b"b"]),
4578            "-ERR Number of keys can't be greater than number of args\r\n"
4579        );
4580        assert_eq!(
4581            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"LIMIT", b"-1"]),
4582            "-ERR LIMIT can't be negative\r\n"
4583        );
4584        assert_eq!(
4585            f.run(&[b"SINTERCARD", b"2", b"a", b"b", b"NOPE", b"1"]),
4586            "-ERR syntax error\r\n"
4587        );
4588        // A key really can be called LIMIT, which is why the count exists.
4589        f.run(&[b"SADD", b"LIMIT", b"2"]);
4590        assert_eq!(f.run(&[b"SINTERCARD", b"2", b"a", b"LIMIT"]), ":1\r\n");
4591    }
4592
4593    #[test]
4594    fn the_algebra_answers_wrongtype_before_it_writes_anything() {
4595        let mut f = Fixture::new();
4596        f.run(&[b"SADD", b"a", b"1"]);
4597        f.run(&[b"SADD", b"d", b"old"]);
4598        f.run(&[b"SET", b"str", b"v"]);
4599
4600        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4601        for bad in [
4602            &[b"SINTER".as_slice(), b"a", b"str"][..],
4603            &[b"SUNION".as_slice(), b"str"][..],
4604            &[b"SDIFF".as_slice(), b"a", b"str"][..],
4605            &[b"SINTERCARD".as_slice(), b"2", b"a", b"str"][..],
4606            &[b"SINTERSTORE".as_slice(), b"d", b"a", b"str"][..],
4607            &[b"SUNIONSTORE".as_slice(), b"d", b"str"][..],
4608            &[b"SDIFFSTORE".as_slice(), b"d", b"a", b"str"][..],
4609        ] {
4610            let reply = f.run(bad);
4611            assert_eq!(reply, wrong, "for {:?}", bad[0]);
4612        }
4613        assert_eq!(
4614            f.run(&[b"SMEMBERS", b"d"]),
4615            "*1\r\n$3\r\nold\r\n",
4616            "and the destination was left alone every time"
4617        );
4618    }
4619
4620    /// The leak a set can spring that nothing on the wire would ever show: the
4621    /// key goes, the body does not, and `DBSIZE` looks right the whole time.
4622    #[test]
4623    fn churning_sets_does_not_grow_the_server() {
4624        let mut f = Fixture::new();
4625        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
4626        let args: Vec<&[u8]> = std::iter::once(&b"SADD"[..])
4627            .chain(std::iter::once(&b"s"[..]))
4628            .chain(members.iter().map(Vec::as_slice))
4629            .collect();
4630
4631        f.run(&args);
4632        f.run(&[b"DEL", b"s"]);
4633        f.server.compact_step();
4634        let after_first = f.server.memory_bytes();
4635
4636        for _ in 0..200 {
4637            f.run(&args);
4638            f.run(&[b"DEL", b"s"]);
4639            f.server.compact_step();
4640        }
4641        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
4642        assert!(
4643            f.server.memory_bytes() <= after_first * 2,
4644            "held {} after two hundred passes against {after_first} after one",
4645            f.server.memory_bytes()
4646        );
4647    }
4648
4649    /// A RESP2 array of bulk strings, which is what most of the list replies
4650    /// are and what writing them out by hand in every assertion looks like.
4651    fn bulks(parts: &[&str]) -> String {
4652        let mut s = format!("*{}\r\n", parts.len());
4653        for p in parts {
4654            s.push_str(&format!("${}\r\n{p}\r\n", p.len()));
4655        }
4656        s
4657    }
4658
4659    #[test]
4660    fn a_list_is_pushed_from_both_ends_and_the_left_one_reverses() {
4661        let mut f = Fixture::new();
4662        // Each element in turn goes at the head, so the last one sent is at the
4663        // front when it is over. That reads like a bug in the client and it is
4664        // what every Redis has always done.
4665        assert_eq!(f.run(&[b"LPUSH", b"k", b"a", b"b", b"c"]), ":3\r\n");
4666        assert_eq!(
4667            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4668            bulks(&["c", "b", "a"])
4669        );
4670        assert_eq!(f.run(&[b"RPUSH", b"k", b"d"]), ":4\r\n");
4671        assert_eq!(f.run(&[b"LLEN", b"k"]), ":4\r\n");
4672        assert_eq!(f.run(&[b"LPOP", b"k"]), "$1\r\nc\r\n");
4673        assert_eq!(f.run(&[b"RPOP", b"k"]), "$1\r\nd\r\n");
4674        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "a"]));
4675        assert_eq!(f.run(&[b"TYPE", b"k"]), "+list\r\n");
4676    }
4677
4678    #[test]
4679    fn the_x_pushes_refuse_to_bring_a_list_back_to_life() {
4680        let mut f = Fixture::new();
4681        assert_eq!(f.run(&[b"LPUSHX", b"k", b"a"]), ":0\r\n");
4682        assert_eq!(f.run(&[b"RPUSHX", b"k", b"a"]), ":0\r\n");
4683        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4684        f.run(&[b"RPUSH", b"k", b"a"]);
4685        assert_eq!(f.run(&[b"LPUSHX", b"k", b"z"]), ":2\r\n");
4686        assert_eq!(f.run(&[b"RPUSHX", b"k", b"y"]), ":3\r\n");
4687        assert_eq!(
4688            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4689            bulks(&["z", "a", "y"])
4690        );
4691    }
4692
4693    /// The four ways a pop can come back with nothing, which are three
4694    /// different replies and a RESP2 client can tell all of them apart.
4695    #[test]
4696    fn an_empty_pop_is_a_different_nothing_with_a_count_and_without() {
4697        let mut f = Fixture::new();
4698        assert_eq!(f.run(&[b"LPOP", b"nope"]), "$-1\r\n");
4699        assert_eq!(f.run(&[b"LPOP", b"nope", b"2"]), "*-1\r\n");
4700        assert_eq!(f.run(&[b"RPOP", b"nope"]), "$-1\r\n");
4701        assert_eq!(f.run(&[b"RPOP", b"nope", b"2"]), "*-1\r\n");
4702        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4703        // A count of zero against a list that is there is an empty array and
4704        // not a null array, which is the fourth answer.
4705        assert_eq!(f.run(&[b"LPOP", b"k", b"0"]), "*0\r\n");
4706        assert_eq!(f.run(&[b"LPOP", b"k", b"1"]), bulks(&["a"]));
4707        // More than there is takes what there is and the key goes with it.
4708        assert_eq!(f.run(&[b"RPOP", b"k", b"9"]), bulks(&["c", "b"]));
4709        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4710    }
4711
4712    #[test]
4713    fn a_pop_count_has_its_own_sentence_and_a_third_argument_is_an_arity_error() {
4714        let mut f = Fixture::new();
4715        f.run(&[b"RPUSH", b"k", b"a"]);
4716        let range = "-ERR value is out of range, must be positive\r\n";
4717        assert_eq!(f.run(&[b"LPOP", b"k", b"-1"]), range);
4718        assert_eq!(f.run(&[b"LPOP", b"k", b"abc"]), range);
4719        assert_eq!(f.run(&[b"RPOP", b"k", b"-1"]), range);
4720        // Redis calls this an arity error and not a syntax error, which is a
4721        // distinction it does not always make.
4722        assert_eq!(
4723            f.run(&[b"LPOP", b"k", b"1", b"2"]),
4724            "-ERR wrong number of arguments for 'lpop' command\r\n"
4725        );
4726        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4727    }
4728
4729    #[test]
4730    fn a_range_takes_negative_ends_and_clamps_the_ones_that_run_off() {
4731        let mut f = Fixture::new();
4732        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4733        assert_eq!(
4734            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4735            bulks(&["a", "b", "c"])
4736        );
4737        assert_eq!(f.run(&[b"LRANGE", b"k", b"-2", b"-1"]), bulks(&["b", "c"]));
4738        assert_eq!(f.run(&[b"LRANGE", b"k", b"1", b"1"]), bulks(&["b"]));
4739        assert_eq!(f.run(&[b"LRANGE", b"k", b"5", b"10"]), "*0\r\n");
4740        assert_eq!(f.run(&[b"LRANGE", b"k", b"2", b"1"]), "*0\r\n");
4741        assert_eq!(
4742            f.run(&[b"LRANGE", b"k", b"-100", b"100"]),
4743            bulks(&["a", "b", "c"])
4744        );
4745        // A key that is not there is an empty range and not a nil, which is the
4746        // one place a list disagrees with a set.
4747        assert_eq!(f.run(&[b"LRANGE", b"nope", b"0", b"-1"]), "*0\r\n");
4748        assert_eq!(
4749            f.run(&[b"LRANGE", b"k", b"a", b"b"]),
4750            "-ERR value is not an integer or out of range\r\n"
4751        );
4752    }
4753
4754    #[test]
4755    fn an_index_reads_and_writes_from_whichever_end_is_nearer() {
4756        let mut f = Fixture::new();
4757        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4758        assert_eq!(f.run(&[b"LINDEX", b"k", b"0"]), "$1\r\na\r\n");
4759        assert_eq!(f.run(&[b"LINDEX", b"k", b"-1"]), "$1\r\nc\r\n");
4760        assert_eq!(f.run(&[b"LINDEX", b"k", b"99"]), "$-1\r\n");
4761        assert_eq!(f.run(&[b"LINDEX", b"nope", b"0"]), "$-1\r\n");
4762        assert_eq!(f.run(&[b"LSET", b"k", b"-1", b"z"]), "+OK\r\n");
4763        assert_eq!(
4764            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4765            bulks(&["a", "b", "z"])
4766        );
4767        // Both ways of missing are errors here rather than a nil, because a
4768        // list is never empty and there is nothing else the reply could be.
4769        assert_eq!(
4770            f.run(&[b"LSET", b"k", b"99", b"z"]),
4771            "-ERR index out of range\r\n"
4772        );
4773        assert_eq!(
4774            f.run(&[b"LSET", b"nope", b"0", b"z"]),
4775            "-ERR no such key\r\n"
4776        );
4777    }
4778
4779    #[test]
4780    fn linsert_says_three_things_with_one_signed_number() {
4781        let mut f = Fixture::new();
4782        // Zero for a key that is not there, which is not the same as minus one
4783        // for a pivot that is not in a list that is.
4784        assert_eq!(
4785            f.run(&[b"LINSERT", b"nope", b"BEFORE", b"a", b"x"]),
4786            ":0\r\n"
4787        );
4788        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4789        assert_eq!(f.run(&[b"LINSERT", b"k", b"before", b"a", b"X"]), ":3\r\n");
4790        assert_eq!(f.run(&[b"LINSERT", b"k", b"AFTER", b"b", b"Y"]), ":4\r\n");
4791        assert_eq!(
4792            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4793            bulks(&["X", "a", "b", "Y"])
4794        );
4795        assert_eq!(
4796            f.run(&[b"LINSERT", b"k", b"BEFORE", b"zz", b"x"]),
4797            ":-1\r\n"
4798        );
4799        assert_eq!(
4800            f.run(&[b"LINSERT", b"k", b"SIDEWAYS", b"a", b"x"]),
4801            "-ERR syntax error\r\n"
4802        );
4803    }
4804
4805    #[test]
4806    fn lrem_counts_in_three_directions_and_takes_the_key_when_it_empties() {
4807        let mut f = Fixture::new();
4808        f.run(&[b"RPUSH", b"k", b"a", b"b", b"a", b"c", b"a"]);
4809        assert_eq!(f.run(&[b"LREM", b"k", b"2", b"a"]), ":2\r\n");
4810        assert_eq!(
4811            f.run(&[b"LRANGE", b"k", b"0", b"-1"]),
4812            bulks(&["b", "c", "a"])
4813        );
4814        assert_eq!(f.run(&[b"LREM", b"k", b"-1", b"a"]), ":1\r\n");
4815        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4816        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"b"]), ":1\r\n");
4817        assert_eq!(f.run(&[b"LREM", b"k", b"0", b"c"]), ":1\r\n");
4818        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4819        assert_eq!(f.run(&[b"LREM", b"nope", b"0", b"a"]), ":0\r\n");
4820    }
4821
4822    #[test]
4823    fn ltrim_keeps_a_window_and_an_empty_one_deletes_the_key() {
4824        let mut f = Fixture::new();
4825        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c", b"d"]);
4826        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"-2"]), "+OK\r\n");
4827        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["b", "c"]));
4828        // `LTRIM k 1 0` is the documented way to empty a list, so it has to
4829        // leave `EXISTS` answering zero rather than leaving an empty one.
4830        assert_eq!(f.run(&[b"LTRIM", b"k", b"1", b"0"]), "+OK\r\n");
4831        assert_eq!(f.run(&[b"EXISTS", b"k"]), ":0\r\n");
4832        assert_eq!(f.run(&[b"LTRIM", b"nope", b"0", b"-1"]), "+OK\r\n");
4833    }
4834
4835    #[test]
4836    fn lpos_walks_from_either_end_and_stops_where_it_is_told() {
4837        let mut f = Fixture::new();
4838        f.run(&[b"RPUSH", b"p", b"a", b"b", b"c", b"a", b"b", b"c", b"a"]);
4839        assert_eq!(f.run(&[b"LPOS", b"p", b"a"]), ":0\r\n");
4840        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1"]), ":6\r\n");
4841        assert_eq!(f.run(&[b"LPOS", b"p", b"a", b"RANK", b"2"]), ":3\r\n");
4842        assert_eq!(
4843            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"2"]),
4844            "*2\r\n:0\r\n:3\r\n"
4845        );
4846        assert_eq!(
4847            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"-1", b"COUNT", b"0"]),
4848            "*3\r\n:6\r\n:3\r\n:0\r\n"
4849        );
4850        // MAXLEN counts elements looked at and not matches found, so three
4851        // stops after `a b c` and finds the one match in it.
4852        assert_eq!(
4853            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"0", b"MAXLEN", b"3"]),
4854            "*1\r\n:0\r\n"
4855        );
4856        // Nothing found is three different replies depending on how it was
4857        // asked and whether the key is there at all.
4858        assert_eq!(f.run(&[b"LPOS", b"p", b"zz"]), "$-1\r\n");
4859        assert_eq!(f.run(&[b"LPOS", b"p", b"zz", b"COUNT", b"0"]), "*0\r\n");
4860        assert_eq!(f.run(&[b"LPOS", b"nope", b"a"]), "$-1\r\n");
4861        assert_eq!(f.run(&[b"LPOS", b"nope", b"a", b"COUNT", b"2"]), "*0\r\n");
4862    }
4863
4864    #[test]
4865    fn lpos_words_its_three_mistakes_the_way_redis_does() {
4866        let mut f = Fixture::new();
4867        f.run(&[b"RPUSH", b"p", b"a"]);
4868        // The whole sentence and not a prefix, because the older wording of it
4869        // is still all over the internet and clients match on the text.
4870        assert_eq!(
4871            f.run(&[b"LPOS", b"p", b"a", b"RANK", b"0"]),
4872            "-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"
4873        );
4874        assert_eq!(
4875            f.run(&[b"LPOS", b"p", b"a", b"COUNT", b"-1"]),
4876            "-ERR COUNT can't be negative\r\n"
4877        );
4878        assert_eq!(
4879            f.run(&[b"LPOS", b"p", b"a", b"MAXLEN", b"-1"]),
4880            "-ERR MAXLEN can't be negative\r\n"
4881        );
4882        assert_eq!(
4883            f.run(&[b"LPOS", b"p", b"a", b"RANK"]),
4884            "-ERR syntax error\r\n"
4885        );
4886        assert_eq!(
4887            f.run(&[b"LPOS", b"p", b"a", b"FOO", b"1"]),
4888            "-ERR syntax error\r\n"
4889        );
4890    }
4891
4892    #[test]
4893    fn a_move_takes_from_one_end_and_gives_to_another_even_on_one_key() {
4894        let mut f = Fixture::new();
4895        f.run(&[b"RPUSH", b"k", b"a", b"b", b"c"]);
4896        assert_eq!(f.run(&[b"RPOPLPUSH", b"k", b"d"]), "$1\r\nc\r\n");
4897        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4898        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c"]));
4899        assert_eq!(
4900            f.run(&[b"LMOVE", b"k", b"d", b"LEFT", b"RIGHT"]),
4901            "$1\r\na\r\n"
4902        );
4903        assert_eq!(f.run(&[b"LRANGE", b"d", b"0", b"-1"]), bulks(&["c", "a"]));
4904        // The same key twice is the documented way to rotate a list and falls
4905        // out of taking the element before deciding where to put it.
4906        f.run(&[b"DEL", b"r"]);
4907        f.run(&[b"RPUSH", b"r", b"1", b"2", b"3"]);
4908        assert_eq!(f.run(&[b"RPOPLPUSH", b"r", b"r"]), "$1\r\n3\r\n");
4909        assert_eq!(
4910            f.run(&[b"LRANGE", b"r", b"0", b"-1"]),
4911            bulks(&["3", "1", "2"])
4912        );
4913        assert_eq!(
4914            f.run(&[b"LMOVE", b"nope", b"d", b"LEFT", b"LEFT"]),
4915            "$-1\r\n"
4916        );
4917        assert_eq!(
4918            f.run(&[b"LMOVE", b"r", b"d", b"LEFT", b"SIDEWAYS"]),
4919            "-ERR syntax error\r\n"
4920        );
4921    }
4922
4923    #[test]
4924    fn a_move_checks_the_destination_before_it_takes_anything() {
4925        let mut f = Fixture::new();
4926        f.run(&[b"RPUSH", b"k", b"a", b"b"]);
4927        f.run(&[b"SET", b"str", b"v"]);
4928        assert_eq!(
4929            f.run(&[b"LMOVE", b"k", b"str", b"LEFT", b"LEFT"]),
4930            "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n"
4931        );
4932        // The element is still where it was, rather than having gone nowhere.
4933        assert_eq!(f.run(&[b"LRANGE", b"k", b"0", b"-1"]), bulks(&["a", "b"]));
4934    }
4935
4936    #[test]
4937    fn lmpop_answers_from_the_first_key_that_has_anything() {
4938        let mut f = Fixture::new();
4939        f.run(&[b"RPUSH", b"b", b"1", b"2", b"3"]);
4940        // The name of the key that answered comes back with the elements,
4941        // because the client cannot work out which one it was.
4942        assert_eq!(
4943            f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT", b"COUNT", b"2"]),
4944            "*2\r\n$1\r\nb\r\n*2\r\n$1\r\n1\r\n$1\r\n2\r\n"
4945        );
4946        assert_eq!(
4947            f.run(&[b"LMPOP", b"2", b"a", b"b", b"RIGHT"]),
4948            "*2\r\n$1\r\nb\r\n*1\r\n$1\r\n3\r\n"
4949        );
4950        assert_eq!(f.run(&[b"EXISTS", b"b"]), ":0\r\n");
4951        // A null array and not a null, even though what it stands in for is an
4952        // array holding a key name and then another array.
4953        assert_eq!(f.run(&[b"LMPOP", b"2", b"a", b"b", b"LEFT"]), "*-1\r\n");
4954    }
4955
4956    #[test]
4957    fn lmpop_has_its_own_words_for_a_count_and_for_a_key_count() {
4958        let mut f = Fixture::new();
4959        f.run(&[b"RPUSH", b"k", b"a"]);
4960        assert_eq!(
4961            f.run(&[b"LMPOP", b"0", b"k", b"LEFT"]),
4962            "-ERR numkeys should be greater than 0\r\n"
4963        );
4964        assert_eq!(
4965            f.run(&[b"LMPOP", b"-1", b"k", b"LEFT"]),
4966            "-ERR numkeys should be greater than 0\r\n"
4967        );
4968        assert_eq!(
4969            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"0"]),
4970            "-ERR count should be greater than 0\r\n"
4971        );
4972        // A key count that eats the direction is a syntax error and not a
4973        // sentence about key counts, because the direction is simply not there.
4974        assert_eq!(
4975            f.run(&[b"LMPOP", b"3", b"k", b"LEFT"]),
4976            "-ERR syntax error\r\n"
4977        );
4978        assert_eq!(
4979            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"COUNT", b"1", b"x"]),
4980            "-ERR syntax error\r\n"
4981        );
4982        assert_eq!(
4983            f.run(&[b"LMPOP", b"1", b"k", b"LEFT", b"FOO", b"1"]),
4984            "-ERR syntax error\r\n"
4985        );
4986        assert_eq!(
4987            f.run(&[b"LMPOP", b"1", b"k", b"SIDEWAYS"]),
4988            "-ERR syntax error\r\n"
4989        );
4990        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n");
4991    }
4992
4993    #[test]
4994    fn every_list_command_says_wrongtype_and_writes_nothing() {
4995        let mut f = Fixture::new();
4996        f.run(&[b"SET", b"str", b"v"]);
4997        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
4998        for cmd in [
4999            &[b"LPUSH".as_slice(), b"str", b"a"][..],
5000            &[b"RPUSH", b"str", b"a"],
5001            &[b"LPUSHX", b"str", b"a"],
5002            &[b"RPUSHX", b"str", b"a"],
5003            &[b"LPOP", b"str"],
5004            &[b"LPOP", b"str", b"2"],
5005            &[b"RPOP", b"str"],
5006            &[b"LLEN", b"str"],
5007            &[b"LRANGE", b"str", b"0", b"-1"],
5008            &[b"LINDEX", b"str", b"0"],
5009            &[b"LSET", b"str", b"0", b"a"],
5010            &[b"LINSERT", b"str", b"BEFORE", b"a", b"b"],
5011            &[b"LREM", b"str", b"0", b"a"],
5012            &[b"LTRIM", b"str", b"0", b"-1"],
5013            &[b"LPOS", b"str", b"a"],
5014            &[b"LPOS", b"str", b"a", b"COUNT", b"0"],
5015            &[b"RPOPLPUSH", b"str", b"d"],
5016            &[b"LMOVE", b"str", b"d", b"LEFT", b"LEFT"],
5017            &[b"LMPOP", b"1", b"str", b"LEFT"],
5018        ] {
5019            assert_eq!(f.run(cmd), wrong, "{:?}", String::from_utf8_lossy(cmd[0]));
5020        }
5021        assert_eq!(f.run(&[b"GET", b"str"]), "$1\r\nv\r\n");
5022        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5023    }
5024
5025    /// A timeout is not an integer and it is not an ordinary float either: the
5026    /// three sentences it can answer with are its own, and which one a given
5027    /// argument gets is not what reading the code would suggest.
5028    #[test]
5029    fn a_timeout_has_three_ways_of_being_wrong() {
5030        let mut f = Fixture::new();
5031        let not_float = "-ERR timeout is not a float or out of range\r\n";
5032        let range = "-ERR timeout is out of range\r\n";
5033        for (bad, want) in [
5034            (&[b"BLPOP".as_slice(), b"k", b"abc"][..], not_float),
5035            (&[b"BLPOP", b"k", b"nan"], not_float),
5036            (&[b"BLPOP", b"k", b""], not_float),
5037            // Whitespace on either side, which `strtold` would take and Redis
5038            // does not.
5039            (&[b"BLPOP", b"k", b" 1"], not_float),
5040            (&[b"BLPOP", b"k", b"1 "], not_float),
5041            (&[b"BLPOP", b"k", b"-1"], "-ERR timeout is negative\r\n"),
5042            (&[b"BLPOP", b"k", b"-0.1"], "-ERR timeout is negative\r\n"),
5043            // These three parse, so they are not the not-a-float error, and all
5044            // three are further off than an i64 of milliseconds reaches.
5045            (&[b"BLPOP", b"k", b"1e400"], range),
5046            (&[b"BLPOP", b"k", b"inf"], range),
5047            (&[b"BLPOP", b"k", b"9999999999999999"], range),
5048            (&[b"BRPOP", b"k", b"abc"], not_float),
5049            (
5050                &[b"BLMOVE", b"a", b"b", b"LEFT", b"RIGHT", b"abc"],
5051                not_float,
5052            ),
5053            (
5054                &[b"BRPOPLPUSH", b"a", b"b", b"-1"],
5055                "-ERR timeout is negative\r\n",
5056            ),
5057            (&[b"BLMPOP", b"abc", b"1", b"k", b"LEFT"], not_float),
5058        ] {
5059            assert_eq!(f.run(bad), want, "for {bad:?}");
5060        }
5061    }
5062
5063    /// A timeout of exactly zero means no timeout, and there are two ways of
5064    /// writing exactly zero.
5065    #[test]
5066    fn a_zero_timeout_waits_and_the_smallest_positive_one_does_not() {
5067        let mut f = Fixture::new();
5068        for timeout in [b"0".as_slice(), b"0.0", b"-0.0"] {
5069            let (flow, out) = f.flow(&[b"BLPOP", b"k", timeout]);
5070            assert_eq!(flow, Flow::Block, "for {timeout:?}");
5071            assert!(out.is_empty(), "for {timeout:?}");
5072        }
5073        // Positive, so it is a real deadline, and the deadline is this
5074        // millisecond. Nothing is written here either: the reply comes from the
5075        // sweep, which is the engine's and not this layer's.
5076        let (flow, out) = f.flow(&[b"BLPOP", b"k", b"0.0000001"]);
5077        assert_eq!(flow, Flow::Block);
5078        assert!(out.is_empty());
5079    }
5080
5081    #[test]
5082    fn a_blocking_command_that_can_be_answered_answers_like_the_one_it_wraps() {
5083        let mut f = Fixture::new();
5084        f.run(&[b"RPUSH", b"L", b"a", b"b", b"c", b"d", b"e"]);
5085
5086        // The one difference from LPOP: the reply names the key that answered,
5087        // which is what makes BLPOP over several keys usable.
5088        assert_eq!(
5089            f.flow(&[b"BLPOP", b"nope", b"L", b"0"]),
5090            (Flow::Continue, "*2\r\n$1\r\nL\r\n$1\r\na\r\n".to_owned())
5091        );
5092        assert_eq!(
5093            f.run(&[b"BRPOP", b"L", b"0"]),
5094            "*2\r\n$1\r\nL\r\n$1\r\ne\r\n"
5095        );
5096        assert_eq!(
5097            f.run(&[
5098                b"BLMPOP", b"0", b"2", b"nope", b"L", b"LEFT", b"COUNT", b"2"
5099            ]),
5100            "*2\r\n$1\r\nL\r\n*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5101        );
5102        assert_eq!(
5103            f.run(&[b"BLMOVE", b"L", b"D", b"LEFT", b"RIGHT", b"0"]),
5104            "$1\r\nd\r\n"
5105        );
5106        assert_eq!(
5107            f.run(&[b"EXISTS", b"L"]),
5108            ":0\r\n",
5109            "and the key went with it"
5110        );
5111        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nd\r\n");
5112        // Onto itself, which is how a list is rotated and is a real thing to ask
5113        // a blocking move for.
5114        f.run(&[b"RPUSH", b"D", b"x"]);
5115        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"D", b"0"]), "$1\r\nx\r\n");
5116        assert_eq!(
5117            f.run(&[b"LRANGE", b"D", b"0", b"-1"]),
5118            "*2\r\n$1\r\nx\r\n$1\r\nd\r\n"
5119        );
5120    }
5121
5122    #[test]
5123    fn blmpop_reads_its_count_and_its_key_count_the_way_lmpop_does() {
5124        let mut f = Fixture::new();
5125        f.run(&[b"RPUSH", b"k", b"a"]);
5126        for (bad, want) in [
5127            (
5128                &[b"BLMPOP".as_slice(), b"0", b"0", b"k", b"LEFT"][..],
5129                "-ERR numkeys should be greater than 0\r\n",
5130            ),
5131            (
5132                &[b"BLMPOP", b"0", b"-1", b"k", b"LEFT"],
5133                "-ERR numkeys should be greater than 0\r\n",
5134            ),
5135            // Two keys named and one given, so the word that should have been
5136            // the direction is a key and there is no direction left.
5137            (
5138                &[b"BLMPOP", b"0", b"2", b"k", b"LEFT"],
5139                "-ERR syntax error\r\n",
5140            ),
5141            (
5142                &[b"BLMPOP", b"0", b"1", b"k", b"SIDEWAYS"],
5143                "-ERR syntax error\r\n",
5144            ),
5145            (
5146                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT"],
5147                "-ERR syntax error\r\n",
5148            ),
5149            (
5150                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"2", b"x"],
5151                "-ERR syntax error\r\n",
5152            ),
5153            // A count that is not a number at all gets the same sentence a zero
5154            // or a negative one gets, rather than the usual one about integers.
5155            (
5156                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"0"],
5157                "-ERR count should be greater than 0\r\n",
5158            ),
5159            (
5160                &[b"BLMPOP", b"0", b"1", b"k", b"LEFT", b"COUNT", b"abc"],
5161                "-ERR count should be greater than 0\r\n",
5162            ),
5163        ] {
5164            assert_eq!(f.run(bad), want, "for {bad:?}");
5165        }
5166        assert_eq!(f.run(&[b"LLEN", b"k"]), ":1\r\n", "and none of them popped");
5167    }
5168
5169    #[test]
5170    fn a_blocking_move_reads_its_directions_before_its_timeout() {
5171        let mut f = Fixture::new();
5172        // Both are wrong. Redis checks the directions first, so this is the
5173        // syntax error and not a complaint about the timeout.
5174        assert_eq!(
5175            f.run(&[b"BLMOVE", b"a", b"b", b"UP", b"DOWN", b"abc"]),
5176            "-ERR syntax error\r\n"
5177        );
5178        assert_eq!(
5179            f.run(&[b"BLMOVE", b"a", b"b", b"LEFT", b"DOWN", b"0.05"]),
5180            "-ERR syntax error\r\n"
5181        );
5182    }
5183
5184    /// The four ways a blocking command sees a key of another type, and the one
5185    /// way it does not.
5186    #[test]
5187    fn a_blocking_command_errors_on_a_wrong_type_rather_than_waiting_on_it() {
5188        let mut f = Fixture::new();
5189        f.run(&[b"SET", b"S", b"v"]);
5190        f.run(&[b"RPUSH", b"D", b"x"]);
5191        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
5192
5193        assert_eq!(f.run(&[b"BLPOP", b"S", b"0"]), wrong);
5194        // Every key is checked even when an earlier one would have blocked, so
5195        // an empty key in front of a string does not hide it.
5196        assert_eq!(f.run(&[b"BLPOP", b"E", b"S", b"0"]), wrong);
5197        assert_eq!(f.run(&[b"BRPOP", b"S", b"0"]), wrong);
5198        assert_eq!(f.run(&[b"BLMPOP", b"0", b"1", b"S", b"LEFT"]), wrong);
5199        assert_eq!(f.run(&[b"BRPOPLPUSH", b"S", b"D", b"0"]), wrong);
5200        // The destination, which is only reached because the source has
5201        // something in it.
5202        assert_eq!(f.run(&[b"BRPOPLPUSH", b"D", b"S", b"0"]), wrong);
5203        assert_eq!(f.run(&[b"LRANGE", b"D", b"0", b"-1"]), "*1\r\n$1\r\nx\r\n");
5204
5205        // And the one that does not: an empty source means the destination is
5206        // never looked at, so this waits rather than erroring, and on a real
5207        // server it times out.
5208        assert_eq!(
5209            f.flow(&[b"BLMOVE", b"E", b"S", b"LEFT", b"RIGHT", b"0.1"])
5210                .0,
5211            Flow::Block
5212        );
5213    }
5214
5215    /// The same churn the set and the string get, because a list that leaks a
5216    /// chunk per push looks exactly like one that does not until it has run for
5217    /// an afternoon.
5218    #[test]
5219    fn churning_lists_does_not_grow_the_server() {
5220        let mut f = Fixture::new();
5221        let vals: Vec<Vec<u8>> = (0..200).map(|i| format!("v{i}").into_bytes()).collect();
5222        let args: Vec<&[u8]> = [&b"RPUSH"[..], &b"k"[..]]
5223            .into_iter()
5224            .chain(vals.iter().map(Vec::as_slice))
5225            .collect();
5226
5227        f.run(&args);
5228        f.run(&[b"DEL", b"k"]);
5229        f.server.compact_step();
5230        let after_first = f.server.memory_bytes();
5231
5232        for _ in 0..200 {
5233            f.run(&args);
5234            f.run(&[b"LTRIM", b"k", b"1", b"0"]);
5235            f.server.compact_step();
5236        }
5237        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
5238        assert!(
5239            f.server.memory_bytes() <= after_first * 2,
5240            "held {} after two hundred passes against {after_first} after one",
5241            f.server.memory_bytes()
5242        );
5243    }
5244
5245    // ------------------------------------------------------------ sorted set
5246
5247    #[test]
5248    fn a_sorted_set_takes_scores_and_gives_them_back() {
5249        let mut f = Fixture::new();
5250        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]), ":2\r\n");
5251        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]), ":1\r\n");
5252        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":3\r\n");
5253        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$1\r\n2\r\n");
5254        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "$-1\r\n");
5255        assert_eq!(f.run(&[b"ZSCORE", b"nokey", b"b"]), "$-1\r\n");
5256        assert_eq!(
5257            f.run(&[b"ZMSCORE", b"z", b"a", b"nope", b"c"]),
5258            "*3\r\n$1\r\n1\r\n$-1\r\n$1\r\n3\r\n"
5259        );
5260        assert_eq!(f.run(&[b"ZREM", b"z", b"a", b"nope"]), ":1\r\n");
5261        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":2\r\n");
5262        // The key goes when the last member does.
5263        assert_eq!(f.run(&[b"ZREM", b"z", b"b", b"c"]), ":2\r\n");
5264        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5265    }
5266
5267    #[test]
5268    fn a_score_is_a_double_on_resp3_and_digits_on_resp2() {
5269        let mut f = Fixture::new();
5270        f.run(&[b"ZADD", b"z", b"1.5", b"a", b"inf", b"b", b"-inf", b"c"]);
5271        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$3\r\n1.5\r\n");
5272        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), "$3\r\ninf\r\n");
5273        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), "$4\r\n-inf\r\n");
5274
5275        f.out = Out::new(Proto::Resp3);
5276        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), ",1.5\r\n");
5277        assert_eq!(f.run(&[b"ZSCORE", b"z", b"b"]), ",inf\r\n");
5278        assert_eq!(f.run(&[b"ZSCORE", b"z", b"c"]), ",-inf\r\n");
5279        assert_eq!(f.run(&[b"ZSCORE", b"z", b"nope"]), "_\r\n");
5280    }
5281
5282    #[test]
5283    fn the_zadd_options_gate_what_gets_written() {
5284        let mut f = Fixture::new();
5285        f.run(&[b"ZADD", b"z", b"5", b"a"]);
5286        // NX leaves a member that is there alone, XX will not create one.
5287        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"9", b"a"]), ":0\r\n");
5288        assert_eq!(f.run(&[b"ZSCORE", b"z", b"a"]), "$1\r\n5\r\n");
5289        assert_eq!(f.run(&[b"ZADD", b"z", b"XX", b"9", b"new"]), ":0\r\n");
5290        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":1\r\n");
5291        // GT and LT only move a score one way.
5292        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"3", b"a"]), ":0\r\n");
5293        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"CH", b"7", b"a"]), ":1\r\n");
5294        assert_eq!(f.run(&[b"ZADD", b"z", b"LT", b"CH", b"9", b"a"]), ":0\r\n");
5295        // CH counts a moved score and plain ZADD does not.
5296        assert_eq!(f.run(&[b"ZADD", b"z", b"1", b"a", b"1", b"b"]), ":1\r\n");
5297        assert_eq!(
5298            f.run(&[b"ZADD", b"z", b"CH", b"2", b"a", b"2", b"c"]),
5299            ":2\r\n"
5300        );
5301    }
5302
5303    #[test]
5304    fn zadd_incr_answers_a_score_or_nothing_at_all() {
5305        let mut f = Fixture::new();
5306        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"5", b"m"]), "$1\r\n5\r\n");
5307        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"2", b"m"]), "$1\r\n7\r\n");
5308        // A gate that refuses is the string nil, because the reply it stands in
5309        // for is a score.
5310        assert_eq!(
5311            f.run(&[b"ZADD", b"z", b"NX", b"INCR", b"2", b"m"]),
5312            "$-1\r\n"
5313        );
5314        assert_eq!(
5315            f.run(&[b"ZADD", b"z", b"XX", b"INCR", b"2", b"gone"]),
5316            "$-1\r\n"
5317        );
5318        assert_eq!(
5319            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"-1", b"m"]),
5320            "$-1\r\n"
5321        );
5322        assert_eq!(
5323            f.run(&[b"ZADD", b"z", b"GT", b"INCR", b"1", b"m"]),
5324            "$1\r\n8\r\n"
5325        );
5326        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"2", b"m"]), "$2\r\n10\r\n");
5327        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"1", b"fresh"]), "$1\r\n1\r\n");
5328    }
5329
5330    #[test]
5331    fn the_two_infinities_will_not_be_added_together() {
5332        let mut f = Fixture::new();
5333        f.run(&[b"ZADD", b"z", b"inf", b"m"]);
5334        let nan = "-ERR resulting score is not a number (NaN)\r\n";
5335        assert_eq!(f.run(&[b"ZINCRBY", b"z", b"-inf", b"m"]), nan);
5336        assert_eq!(f.run(&[b"ZADD", b"z", b"INCR", b"-inf", b"m"]), nan);
5337        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\ninf\r\n");
5338        // And a key made for an increment that then fails does not stay behind.
5339        assert_eq!(f.run(&[b"ZINCRBY", b"gone", b"1", b"m"]), "$1\r\n1\r\n");
5340    }
5341
5342    #[test]
5343    fn zadd_says_its_mistakes_the_way_redis_says_them() {
5344        let mut f = Fixture::new();
5345        // The pairs are counted before the options are looked at, so this is a
5346        // syntax error about having none and not a complaint about NX and XX.
5347        assert_eq!(
5348            f.run(&[b"ZADD", b"z", b"NX", b"XX"]),
5349            "-ERR syntax error\r\n"
5350        );
5351        assert_eq!(
5352            f.run(&[b"ZADD", b"z", b"NX", b"XX", b"1", b"a"]),
5353            "-ERR XX and NX options at the same time are not compatible\r\n"
5354        );
5355        let gtlt = "-ERR GT, LT, and/or NX options at the same time are not compatible\r\n";
5356        assert_eq!(f.run(&[b"ZADD", b"z", b"NX", b"GT", b"1", b"a"]), gtlt);
5357        assert_eq!(f.run(&[b"ZADD", b"z", b"GT", b"LT", b"1", b"a"]), gtlt);
5358        assert_eq!(
5359            f.run(&[b"ZADD", b"z", b"INCR", b"1", b"a", b"2", b"b"]),
5360            "-ERR INCR option supports a single increment-element pair\r\n"
5361        );
5362        // An odd number of arguments after the options.
5363        assert_eq!(
5364            f.run(&[b"ZADD", b"z", b"1", b"a", b"2"]),
5365            "-ERR syntax error\r\n"
5366        );
5367        // Every score is read before the first is stored.
5368        assert_eq!(
5369            f.run(&[b"ZADD", b"z", b"1", b"a", b"nonsense", b"b"]),
5370            "-ERR value is not a valid float\r\n"
5371        );
5372        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5373    }
5374
5375    #[test]
5376    fn a_rank_says_where_a_member_sits_from_either_end() {
5377        let mut f = Fixture::new();
5378        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5379        assert_eq!(f.run(&[b"ZRANK", b"z", b"a"]), ":0\r\n");
5380        assert_eq!(f.run(&[b"ZRANK", b"z", b"c"]), ":2\r\n");
5381        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"c"]), ":0\r\n");
5382        assert_eq!(f.run(&[b"ZREVRANK", b"z", b"a"]), ":2\r\n");
5383        // WITHSCORE changes both shapes: the answer and the nothing.
5384        assert_eq!(
5385            f.run(&[b"ZRANK", b"z", b"b", b"WITHSCORE"]),
5386            "*2\r\n:1\r\n$1\r\n2\r\n"
5387        );
5388        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope"]), "$-1\r\n");
5389        assert_eq!(f.run(&[b"ZRANK", b"z", b"nope", b"WITHSCORE"]), "*-1\r\n");
5390        assert_eq!(f.run(&[b"ZRANK", b"nokey", b"a", b"WITHSCORE"]), "*-1\r\n");
5391        // A bad option is a syntax error and one argument too many is an arity
5392        // error, which is Redis's split.
5393        assert_eq!(
5394            f.run(&[b"ZRANK", b"z", b"b", b"bogus"]),
5395            "-ERR syntax error\r\n"
5396        );
5397        assert_eq!(
5398            f.run(&[b"ZREVRANK", b"z", b"b", b"WITHSCORE", b"more"]),
5399            "-ERR wrong number of arguments for 'zrevrank' command\r\n"
5400        );
5401    }
5402
5403    #[test]
5404    fn the_two_counts_read_their_two_kinds_of_bound() {
5405        let mut f = Fixture::new();
5406        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5407        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"-inf", b"+inf"]), ":3\r\n");
5408        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"2", b"3"]), ":2\r\n");
5409        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"3"]), ":2\r\n");
5410        assert_eq!(f.run(&[b"ZCOUNT", b"z", b"(1", b"(3"]), ":1\r\n");
5411        assert_eq!(f.run(&[b"ZCOUNT", b"nokey", b"-inf", b"+inf"]), ":0\r\n");
5412        assert_eq!(
5413            f.run(&[b"ZCOUNT", b"z", b"bogus", b"3"]),
5414            "-ERR min or max is not a float\r\n"
5415        );
5416
5417        f.run(&[b"ZADD", b"l", b"0", b"a", b"0", b"b", b"0", b"c"]);
5418        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"-", b"+"]), ":3\r\n");
5419        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"[a", b"(c"]), ":2\r\n");
5420        assert_eq!(f.run(&[b"ZLEXCOUNT", b"l", b"(a", b"+"]), ":2\r\n");
5421        // A bare member is not a bound, because a member can start with any
5422        // byte and there would be no way to say the bracket if it were optional.
5423        assert_eq!(
5424            f.run(&[b"ZLEXCOUNT", b"l", b"a", b"c"]),
5425            "-ERR min or max not valid string range item\r\n"
5426        );
5427    }
5428
5429    /// The three ways `ZRANGE` can be asked for a window, forwards and back.
5430    ///
5431    /// Every byte in here was read off a real 8.10.1 rather than worked out,
5432    /// because the interesting part of this command is not what it selects, it
5433    /// is which of the two ends the client is expected to name first.
5434    #[test]
5435    fn one_range_command_selects_by_rank_or_score_or_name() {
5436        let mut f = Fixture::new();
5437        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5438        assert_eq!(
5439            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5440            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5441        );
5442        assert_eq!(
5443            f.run(&[b"ZRANGE", b"z", b"-2", b"-1"]),
5444            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5445        );
5446        assert_eq!(f.run(&[b"ZRANGE", b"z", b"5", b"9"]), "*0\r\n");
5447        assert_eq!(f.run(&[b"ZRANGE", b"nokey", b"0", b"-1"]), "*0\r\n");
5448        // REV over ranks reverses the walk and leaves the two arguments alone,
5449        // because a rank counts from the end the walk starts at.
5450        assert_eq!(
5451            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"REV"]),
5452            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
5453        );
5454        assert_eq!(
5455            f.run(&[b"ZRANGE", b"z", b"(1", b"+inf", b"BYSCORE"]),
5456            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5457        );
5458        // And REV over scores does swap them, since a bound does not count from
5459        // anywhere. This is the one line of the parse that tells the two apart.
5460        assert_eq!(
5461            f.run(&[b"ZRANGE", b"z", b"+inf", b"(1", b"BYSCORE", b"REV"]),
5462            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
5463        );
5464        assert_eq!(
5465            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX"]),
5466            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5467        );
5468        assert_eq!(
5469            f.run(&[b"ZRANGE", b"z", b"+", b"-", b"BYLEX", b"REV"]),
5470            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
5471        );
5472    }
5473
5474    /// The older spellings, which are the same six windows with the mode in the
5475    /// name and the high end named first on the three that go backwards.
5476    #[test]
5477    fn the_older_range_spellings_name_their_high_end_first() {
5478        let mut f = Fixture::new();
5479        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5480        assert_eq!(
5481            f.run(&[b"ZREVRANGE", b"z", b"0", b"-1"]),
5482            "*3\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\na\r\n"
5483        );
5484        assert_eq!(
5485            f.run(&[b"ZREVRANGE", b"z", b"0", b"0", b"WITHSCORES"]),
5486            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5487        );
5488        assert_eq!(
5489            f.run(&[b"ZRANGEBYSCORE", b"z", b"(1", b"3"]),
5490            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5491        );
5492        assert_eq!(
5493            f.run(&[b"ZREVRANGEBYSCORE", b"z", b"3", b"(1"]),
5494            "*2\r\n$1\r\nc\r\n$1\r\nb\r\n"
5495        );
5496        // The two arguments the wrong way round is an empty answer and not an
5497        // error, which is what the swap being in the parse rather than in the
5498        // window buys.
5499        assert_eq!(f.run(&[b"ZREVRANGEBYSCORE", b"z", b"(1", b"3"]), "*0\r\n");
5500        assert_eq!(
5501            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"(c"]),
5502            "*2\r\n$1\r\na\r\n$1\r\nb\r\n"
5503        );
5504        assert_eq!(
5505            f.run(&[b"ZREVRANGEBYLEX", b"z", b"(c", b"[a"]),
5506            "*2\r\n$1\r\nb\r\n$1\r\na\r\n"
5507        );
5508        // BYSCORE, BYLEX and REV mean nothing to these, so they are not another
5509        // way of spelling the mode, they are a syntax error.
5510        for cmd in [
5511            &[b"ZREVRANGE".as_slice(), b"z", b"0", b"-1", b"BYSCORE"][..],
5512            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"REV"],
5513            &[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"BYLEX"],
5514        ] {
5515            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{:?}", cmd[0]);
5516        }
5517    }
5518
5519    /// `LIMIT` and `WITHSCORES`, which every one of these commands reads and
5520    /// only some of them accept.
5521    #[test]
5522    fn limit_and_withscores_are_read_by_all_of_them_and_refused_afterwards() {
5523        let mut f = Fixture::new();
5524        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5525        assert_eq!(
5526            f.run(&[
5527                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"1", b"1"
5528            ]),
5529            "*1\r\n$1\r\nb\r\n"
5530        );
5531        // A negative offset skips past everything, a negative count is no bound.
5532        assert_eq!(
5533            f.run(&[
5534                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"-1", b"2"
5535            ]),
5536            "*0\r\n"
5537        );
5538        assert_eq!(
5539            f.run(&[
5540                b"ZRANGE", b"z", b"-inf", b"+inf", b"BYSCORE", b"LIMIT", b"0", b"-1"
5541            ]),
5542            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5543        );
5544        // The two options in either order, which falls out of the parse loop.
5545        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";
5546        assert_eq!(
5547            f.run(&[
5548                b"ZRANGEBYSCORE",
5549                b"z",
5550                b"1",
5551                b"3",
5552                b"WITHSCORES",
5553                b"LIMIT",
5554                b"0",
5555                b"2"
5556            ]),
5557            both
5558        );
5559        assert_eq!(
5560            f.run(&[
5561                b"ZRANGEBYSCORE",
5562                b"z",
5563                b"1",
5564                b"3",
5565                b"LIMIT",
5566                b"0",
5567                b"2",
5568                b"WITHSCORES"
5569            ]),
5570            both
5571        );
5572        // LIMIT on a range by rank is refused after the whole option list has
5573        // been read, so this complains about LIMIT and not about WITHSCORES.
5574        let needs_by = "-ERR syntax error, LIMIT is only supported in combination with either BYSCORE or BYLEX\r\n";
5575        assert_eq!(
5576            f.run(&[
5577                b"ZREVRANGE",
5578                b"z",
5579                b"0",
5580                b"-1",
5581                b"WITHSCORES",
5582                b"LIMIT",
5583                b"0",
5584                b"1"
5585            ]),
5586            needs_by
5587        );
5588        assert_eq!(
5589            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"LIMIT", b"0", b"1"]),
5590            needs_by
5591        );
5592        let not_bylex = "-ERR syntax error, WITHSCORES not supported in combination with BYLEX\r\n";
5593        assert_eq!(
5594            f.run(&[b"ZRANGE", b"z", b"-", b"+", b"BYLEX", b"WITHSCORES"]),
5595            not_bylex
5596        );
5597        assert_eq!(
5598            f.run(&[b"ZRANGEBYLEX", b"z", b"[a", b"[c", b"WITHSCORES"]),
5599            not_bylex
5600        );
5601        // Two modes at once, an option nobody knows, a LIMIT missing its count,
5602        // and the three number errors, which are three different sentences.
5603        for cmd in [
5604            &[
5605                b"ZRANGE".as_slice(),
5606                b"z",
5607                b"0",
5608                b"-1",
5609                b"BYSCORE",
5610                b"BYLEX",
5611            ][..],
5612            &[b"ZRANGE", b"z", b"0", b"-1", b"junk"],
5613            &[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"0"],
5614        ] {
5615            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5616        }
5617        assert_eq!(
5618            f.run(&[b"ZRANGEBYSCORE", b"z", b"bad", b"3"]),
5619            "-ERR min or max is not a float\r\n"
5620        );
5621        assert_eq!(
5622            f.run(&[b"ZRANGEBYLEX", b"z", b"a", b"[c"]),
5623            "-ERR min or max not valid string range item\r\n"
5624        );
5625        assert_eq!(
5626            f.run(&[b"ZRANGEBYSCORE", b"z", b"1", b"3", b"LIMIT", b"a", b"2"]),
5627            "-ERR value is not an integer or out of range\r\n"
5628        );
5629    }
5630
5631    /// `WITHSCORES` is the one place in this group where the two protocols
5632    /// disagree about the shape of the reply and not just the type of a value.
5633    #[test]
5634    fn withscores_nests_on_resp3_and_flattens_on_resp2() {
5635        let mut f = Fixture::new();
5636        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5637        assert_eq!(
5638            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5639            "*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"
5640        );
5641        f.out = Out::new(Proto::Resp3);
5642        assert_eq!(
5643            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5644            "*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"
5645        );
5646        assert_eq!(
5647            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5648            "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nc\r\n"
5649        );
5650    }
5651
5652    /// The store form, which is the same parse with the destination in front.
5653    #[test]
5654    fn a_range_store_writes_the_window_into_another_key() {
5655        let mut f = Fixture::new();
5656        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5657        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1"]), ":3\r\n");
5658        // A window that selects nothing deletes the destination rather than
5659        // leaving an empty sorted set, because an empty one does not exist.
5660        assert_eq!(f.run(&[b"ZRANGESTORE", b"d", b"z", b"5", b"9"]), ":0\r\n");
5661        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5662        assert_eq!(
5663            f.run(&[b"ZRANGESTORE", b"d", b"z", b"(1", b"+inf", b"BYSCORE"]),
5664            ":2\r\n"
5665        );
5666        assert_eq!(
5667            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
5668            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5669        );
5670        // The destination is allowed to be the source, because the result is
5671        // built whole before anything is written over.
5672        assert_eq!(f.run(&[b"ZRANGESTORE", b"z", b"z", b"1", b"2"]), ":2\r\n");
5673        assert_eq!(
5674            f.run(&[b"ZRANGE", b"z", b"0", b"-1", b"WITHSCORES"]),
5675            "*4\r\n$1\r\nb\r\n$1\r\n2\r\n$1\r\nc\r\n$1\r\n3\r\n"
5676        );
5677        // It takes every option ZRANGE takes except WITHSCORES, which is a
5678        // plain syntax error here and not the sentence about BYLEX.
5679        assert_eq!(
5680            f.run(&[b"ZRANGESTORE", b"d", b"z", b"0", b"-1", b"WITHSCORES"]),
5681            "-ERR syntax error\r\n"
5682        );
5683    }
5684
5685    /// The three removals, which are the read side's window with the walk
5686    /// turned into a removal and no options at all.
5687    #[test]
5688    fn the_three_removals_share_their_window_with_the_reads() {
5689        let mut f = Fixture::new();
5690        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5691        assert_eq!(f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"0"]), ":1\r\n");
5692        assert_eq!(
5693            f.run(&[b"ZRANGE", b"z", b"0", b"-1"]),
5694            "*2\r\n$1\r\nb\r\n$1\r\nc\r\n"
5695        );
5696        assert_eq!(
5697            f.run(&[b"ZREMRANGEBYSCORE", b"z", b"(2", b"+inf"]),
5698            ":1\r\n"
5699        );
5700        assert_eq!(f.run(&[b"ZRANGE", b"z", b"0", b"-1"]), "*1\r\n$1\r\nb\r\n");
5701        // The last member going takes the key with it.
5702        assert_eq!(f.run(&[b"ZREMRANGEBYLEX", b"z", b"-", b"+"]), ":1\r\n");
5703        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
5704        assert_eq!(
5705            f.run(&[b"ZREMRANGEBYRANK", b"nokey", b"0", b"-1"]),
5706            ":0\r\n"
5707        );
5708        assert_eq!(
5709            f.run(&[b"ZREMRANGEBYRANK", b"z", b"0", b"x"]),
5710            "-ERR value is not an integer or out of range\r\n"
5711        );
5712    }
5713
5714    /// The algebra, which is one gather and three names for it.
5715    #[test]
5716    fn the_three_algebra_commands_combine_scores_and_order_the_answer_once() {
5717        let mut f = Fixture::new();
5718        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5719        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5720        assert_eq!(
5721            f.run(&[b"ZUNION", b"2", b"z", b"y"]),
5722            "*4\r\n$1\r\na\r\n$1\r\nc\r\n$1\r\nb\r\n$1\r\nd\r\n"
5723        );
5724        // The scores are added where a member is in both, and the answer comes
5725        // out in the order those combined scores put it in.
5726        assert_eq!(
5727            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WITHSCORES"]),
5728            "*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"
5729        );
5730        assert_eq!(
5731            f.run(&[
5732                b"ZUNION",
5733                b"2",
5734                b"z",
5735                b"y",
5736                b"WEIGHTS",
5737                b"2",
5738                b"3",
5739                b"WITHSCORES"
5740            ]),
5741            "*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"
5742        );
5743        assert_eq!(
5744            f.run(&[
5745                b"ZUNION",
5746                b"2",
5747                b"z",
5748                b"y",
5749                b"AGGREGATE",
5750                b"MIN",
5751                b"WITHSCORES"
5752            ]),
5753            "*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"
5754        );
5755        assert_eq!(
5756            f.run(&[
5757                b"ZUNION",
5758                b"2",
5759                b"z",
5760                b"y",
5761                b"AGGREGATE",
5762                b"MAX",
5763                b"WITHSCORES"
5764            ]),
5765            "*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"
5766        );
5767        assert_eq!(
5768            f.run(&[b"ZINTER", b"2", b"z", b"y", b"WITHSCORES"]),
5769            "*2\r\n$1\r\nb\r\n$2\r\n12\r\n"
5770        );
5771        assert_eq!(
5772            f.run(&[b"ZDIFF", b"2", b"z", b"y", b"WITHSCORES"]),
5773            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nc\r\n$1\r\n3\r\n"
5774        );
5775        assert_eq!(f.run(&[b"ZUNION", b"1", b"nokey"]), "*0\r\n");
5776        // A plain set is an input, and it behaves as a sorted set in which
5777        // every member scores one.
5778        f.run(&[b"SADD", b"p", b"a", b"d"]);
5779        assert_eq!(
5780            f.run(&[b"ZUNION", b"2", b"z", b"p", b"WITHSCORES"]),
5781            "*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"
5782        );
5783        // A difference never combines two scores, so it has nothing for either
5784        // of the two options to do and refuses both.
5785        for cmd in [
5786            &[
5787                b"ZDIFF".as_slice(),
5788                b"2",
5789                b"z",
5790                b"y",
5791                b"WEIGHTS",
5792                b"1",
5793                b"1",
5794            ][..],
5795            &[b"ZDIFF", b"2", b"z", b"y", b"AGGREGATE", b"MIN"],
5796        ] {
5797            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5798        }
5799    }
5800
5801    /// The count of keys, which is what lets a key be named `WEIGHTS`.
5802    #[test]
5803    fn the_algebra_counts_its_keys_and_says_so_when_the_count_is_wrong() {
5804        let mut f = Fixture::new();
5805        f.run(&[b"ZADD", b"z", b"1", b"a"]);
5806        f.run(&[b"ZADD", b"y", b"2", b"b"]);
5807        // Redis names the command in this one, so each spelling says its own.
5808        assert_eq!(
5809            f.run(&[b"ZUNION", b"0", b"z"]),
5810            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5811        );
5812        assert_eq!(
5813            f.run(&[b"ZUNION", b"-1", b"z"]),
5814            "-ERR at least 1 input key is needed for 'zunion' command\r\n"
5815        );
5816        assert_eq!(
5817            f.run(&[b"ZINTERCARD", b"0", b"z"]),
5818            "-ERR at least 1 input key is needed for 'zintercard' command\r\n"
5819        );
5820        // A count bigger than the line is a plain syntax error, which reads
5821        // oddly and is what Redis says.
5822        assert_eq!(
5823            f.run(&[b"ZUNION", b"3", b"z", b"y"]),
5824            "-ERR syntax error\r\n"
5825        );
5826        assert_eq!(
5827            f.run(&[b"ZUNION", b"x", b"z"]),
5828            "-ERR value is not an integer or out of range\r\n"
5829        );
5830        // A WEIGHTS list that is not one per key is a syntax error, and a
5831        // weight that is not a number gets a sentence of its own.
5832        assert_eq!(
5833            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"1"]),
5834            "-ERR syntax error\r\n"
5835        );
5836        assert_eq!(
5837            f.run(&[b"ZUNION", b"2", b"z", b"y", b"WEIGHTS", b"a", b"b"]),
5838            "-ERR weight value is not a float\r\n"
5839        );
5840        assert_eq!(
5841            f.run(&[b"ZUNION", b"2", b"z", b"y", b"AGGREGATE", b"NOPE"]),
5842            "-ERR syntax error\r\n"
5843        );
5844    }
5845
5846    /// The three store forms, which answer a count and take no WITHSCORES.
5847    #[test]
5848    fn the_algebra_stores_answer_a_count_and_delete_an_empty_destination() {
5849        let mut f = Fixture::new();
5850        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5851        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"d"]);
5852        assert_eq!(f.run(&[b"ZUNIONSTORE", b"d", b"2", b"z", b"y"]), ":4\r\n");
5853        assert_eq!(
5854            f.run(&[b"ZRANGE", b"d", b"0", b"-1", b"WITHSCORES"]),
5855            "*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"
5856        );
5857        assert_eq!(f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"y"]), ":1\r\n");
5858        assert_eq!(f.run(&[b"ZDIFFSTORE", b"d", b"2", b"z", b"y"]), ":2\r\n");
5859        // An empty result deletes the destination rather than leaving an empty
5860        // sorted set, because an empty one does not exist.
5861        assert_eq!(
5862            f.run(&[b"ZINTERSTORE", b"d", b"2", b"z", b"nokey"]),
5863            ":0\r\n"
5864        );
5865        assert_eq!(f.run(&[b"EXISTS", b"d"]), ":0\r\n");
5866        // The destination is allowed to name its own source.
5867        assert_eq!(f.run(&[b"ZUNIONSTORE", b"z", b"2", b"z", b"y"]), ":4\r\n");
5868        assert_eq!(f.run(&[b"ZCARD", b"z"]), ":4\r\n");
5869        for cmd in [
5870            &[
5871                b"ZUNIONSTORE".as_slice(),
5872                b"d",
5873                b"2",
5874                b"z",
5875                b"y",
5876                b"WITHSCORES",
5877            ][..],
5878            &[
5879                b"ZDIFFSTORE",
5880                b"d",
5881                b"2",
5882                b"z",
5883                b"y",
5884                b"WEIGHTS",
5885                b"1",
5886                b"1",
5887            ],
5888        ] {
5889            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5890        }
5891    }
5892
5893    /// `ZINTERCARD`, which counts without building anything.
5894    #[test]
5895    fn intercard_counts_and_stops_at_its_limit() {
5896        let mut f = Fixture::new();
5897        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5898        f.run(&[b"ZADD", b"y", b"10", b"b", b"20", b"c", b"30", b"d"]);
5899        assert_eq!(f.run(&[b"ZINTERCARD", b"2", b"z", b"y"]), ":2\r\n");
5900        // A limit of zero is no limit, which is Redis's reading of it.
5901        assert_eq!(
5902            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"0"]),
5903            ":2\r\n"
5904        );
5905        assert_eq!(
5906            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"1"]),
5907            ":1\r\n"
5908        );
5909        // A negative limit and a limit that is not a number at all get the same
5910        // sentence, which looks like a mistake in Redis and is copied as one.
5911        let bad = "-ERR LIMIT can't be negative\r\n";
5912        assert_eq!(
5913            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"-1"]),
5914            bad
5915        );
5916        assert_eq!(
5917            f.run(&[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT", b"x"]),
5918            bad
5919        );
5920        for cmd in [
5921            &[b"ZINTERCARD".as_slice(), b"3", b"z", b"y"][..],
5922            &[b"ZINTERCARD", b"2", b"z", b"y", b"LIMIT"],
5923            &[b"ZINTERCARD", b"2", b"z", b"y", b"junk", b"1"],
5924        ] {
5925            assert_eq!(f.run(cmd), "-ERR syntax error\r\n", "{cmd:?}");
5926        }
5927    }
5928
5929    /// `ZRANDMEMBER`, which answers two different shapes out of one name.
5930    #[test]
5931    fn a_draw_answers_one_member_or_an_array_of_them() {
5932        let mut f = Fixture::new();
5933        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5934        // No count is one member or a nil, a count is an array that may be
5935        // empty, and those are two reply types the client has to tell apart.
5936        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "$-1\r\n");
5937        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey", b"3"]), "*0\r\n");
5938        assert_eq!(f.run(&[b"ZRANDMEMBER", b"z", b"0"]), "*0\r\n");
5939        assert!(f.run(&[b"ZRANDMEMBER", b"z"]).starts_with("$1\r\n"));
5940        // A positive count draws without replacement, so a count over the size
5941        // answers the whole set and never a member twice.
5942        let all = f.run(&[b"ZRANDMEMBER", b"z", b"10"]);
5943        assert!(all.starts_with("*3\r\n"), "{all}");
5944        for m in ["a", "b", "c"] {
5945            assert!(all.contains(m), "{all}");
5946        }
5947        // A negative one draws with replacement and answers exactly as many as
5948        // it was asked for, whatever the size of the set.
5949        assert!(
5950            f.run(&[b"ZRANDMEMBER", b"z", b"-5"]).starts_with("*5\r\n"),
5951            "five draws with replacement"
5952        );
5953        assert!(
5954            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"])
5955                .starts_with("*4\r\n"),
5956            "two pairs, flat on RESP2"
5957        );
5958        f.out = Out::new(Proto::Resp3);
5959        let got = f.run(&[b"ZRANDMEMBER", b"z", b"2", b"WITHSCORES"]);
5960        assert!(got.starts_with("*2\r\n*2\r\n"), "{got}");
5961        assert_eq!(f.run(&[b"ZRANDMEMBER", b"nokey"]), "_\r\n");
5962        f.out = Out::new(Proto::Resp2);
5963        assert_eq!(
5964            f.run(&[b"ZRANDMEMBER", b"z", b"2", b"junk"]),
5965            "-ERR syntax error\r\n"
5966        );
5967        assert_eq!(
5968            f.run(&[b"ZRANDMEMBER", b"z", b"x"]),
5969            "-ERR value is not an integer or out of range\r\n"
5970        );
5971    }
5972
5973    /// `ZSCAN`, and the one sorted set reply where a score is not a double.
5974    #[test]
5975    fn a_sorted_set_scan_answers_pairs_of_strings_on_both_protocols() {
5976        let mut f = Fixture::new();
5977        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
5978        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";
5979        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5980        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"10"]), all);
5981        assert_eq!(
5982            f.run(&[b"ZSCAN", b"z", b"0", b"MATCH", b"a*"]),
5983            "*2\r\n$1\r\n0\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
5984        );
5985        assert_eq!(
5986            f.run(&[b"ZSCAN", b"nokey", b"0"]),
5987            "*2\r\n$1\r\n0\r\n*0\r\n"
5988        );
5989        // A score stays a bulk string on RESP3, which is the one place the two
5990        // protocols agree about a score and everywhere else they do not.
5991        f.out = Out::new(Proto::Resp3);
5992        assert_eq!(f.run(&[b"ZSCAN", b"z", b"0"]), all);
5993        f.out = Out::new(Proto::Resp2);
5994        assert_eq!(
5995            f.run(&[b"ZSCAN", b"z", b"0", b"NOVALUES"]),
5996            "-ERR NOVALUES option can only be used in HSCAN\r\n"
5997        );
5998        assert_eq!(f.run(&[b"ZSCAN", b"z", b"-1"]), "-ERR invalid cursor\r\n");
5999        assert_eq!(
6000            f.run(&[b"ZSCAN", b"z", b"0", b"COUNT", b"0"]),
6001            "-ERR syntax error\r\n"
6002        );
6003    }
6004
6005    /// The count is what decides the shape, and its value is not.
6006    #[test]
6007    fn a_sorted_set_pop_changes_shape_when_it_is_given_a_count() {
6008        let mut f = Fixture::new();
6009        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6010        // No count, so one flat pair, and the score is a bulk string on RESP2.
6011        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n$1\r\n1\r\n");
6012        assert_eq!(f.run(&[b"ZPOPMAX", b"z"]), "*2\r\n$1\r\nc\r\n$1\r\n3\r\n");
6013        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
6014        // A count, so pairs, and on RESP2 they are flattened into one run.
6015        assert_eq!(
6016            f.run(&[b"ZPOPMIN", b"z", b"2"]),
6017            "*4\r\n$1\r\na\r\n$1\r\n1\r\n$1\r\nb\r\n$1\r\n2\r\n"
6018        );
6019        // An empty array rather than a null, which is where a sorted set pop and
6020        // a list pop part company, and the same answer a count of zero gives.
6021        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey"]), "*0\r\n");
6022        assert_eq!(f.run(&[b"ZPOPMIN", b"nokey", b"2"]), "*0\r\n");
6023        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"0"]), "*0\r\n");
6024        // The last member takes the key with it.
6025        assert_eq!(
6026            f.run(&[b"ZPOPMIN", b"z", b"9"]),
6027            "*2\r\n$1\r\nc\r\n$1\r\n3\r\n"
6028        );
6029        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6030
6031        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b"]);
6032        f.out = Out::new(Proto::Resp3);
6033        assert_eq!(f.run(&[b"ZPOPMIN", b"z"]), "*2\r\n$1\r\na\r\n,1\r\n");
6034        assert_eq!(
6035            f.run(&[b"ZPOPMIN", b"z", b"1"]),
6036            "*1\r\n*2\r\n$1\r\nb\r\n,2\r\n"
6037        );
6038        f.out = Out::new(Proto::Resp2);
6039        // Both of these are the range error rather than the usual sentence about
6040        // integers, which is the odd answer and so the one worth copying.
6041        let bad = "-ERR value is out of range, must be positive\r\n";
6042        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"x"]), bad);
6043        assert_eq!(f.run(&[b"ZPOPMIN", b"z", b"-1"]), bad);
6044        assert_eq!(
6045            f.run(&[b"ZPOPMIN", b"z", b"1", b"2"]),
6046            "-ERR syntax error\r\n"
6047        );
6048    }
6049
6050    /// `ZMPOP`, which is `LMPOP` with scores and the same parse.
6051    #[test]
6052    fn a_multi_key_pop_names_the_key_that_answered_and_nests_its_pairs() {
6053        let mut f = Fixture::new();
6054        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6055        assert_eq!(
6056            f.run(&[b"ZMPOP", b"2", b"nokey", b"z", b"MIN"]),
6057            "*2\r\n$1\r\nz\r\n*1\r\n*2\r\n$1\r\na\r\n$1\r\n1\r\n"
6058        );
6059        // Nested on RESP2 as well, because the key name is already in front of
6060        // the pairs and there is nothing left to flatten into.
6061        assert_eq!(
6062            f.run(&[b"ZMPOP", b"1", b"z", b"MAX", b"COUNT", b"2"]),
6063            "*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"
6064        );
6065        // A null array and not a null, the same as LMPOP.
6066        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "*-1\r\n");
6067        f.out = Out::new(Proto::Resp3);
6068        assert_eq!(f.run(&[b"ZMPOP", b"1", b"nokey", b"MIN"]), "_\r\n");
6069        f.out = Out::new(Proto::Resp2);
6070        let numkeys = "-ERR numkeys should be greater than 0\r\n";
6071        for bad in [
6072            &[b"ZMPOP".as_slice(), b"0", b"z", b"MIN"][..],
6073            &[b"ZMPOP", b"-1", b"z", b"MIN"],
6074            &[b"ZMPOP", b"x", b"z", b"MIN"],
6075        ] {
6076            assert_eq!(f.run(bad), numkeys, "{:?}", bad[1]);
6077        }
6078        let count = "-ERR count should be greater than 0\r\n";
6079        for bad in [
6080            &[b"ZMPOP".as_slice(), b"1", b"z", b"MIN", b"COUNT", b"0"][..],
6081            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"-1"],
6082            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"x"],
6083        ] {
6084            assert_eq!(f.run(bad), count, "{:?}", bad[5]);
6085        }
6086        let syntax = "-ERR syntax error\r\n";
6087        for bad in [
6088            // Two keys named and one given, so the word that should have been
6089            // the direction is a key and there is no direction left.
6090            &[b"ZMPOP".as_slice(), b"2", b"z", b"MIN"][..],
6091            &[b"ZMPOP", b"1", b"z", b"SIDEWAYS"],
6092            &[b"ZMPOP", b"1", b"z", b"MIN", b"junk"],
6093            &[b"ZMPOP", b"1", b"z", b"MIN", b"COUNT", b"1", b"junk"],
6094        ] {
6095            assert_eq!(f.run(bad), syntax, "{bad:?}");
6096        }
6097    }
6098
6099    /// The three that wait, when there is something there and they do not have
6100    /// to. `BZPOPMIN` is the one reply in the group that is three flat elements.
6101    #[test]
6102    fn the_sorted_set_pops_that_wait_answer_like_the_ones_they_wrap() {
6103        let mut f = Fixture::new();
6104        f.run(&[b"ZADD", b"z", b"1", b"a", b"2", b"b", b"3", b"c"]);
6105        assert_eq!(
6106            f.flow(&[b"BZPOPMIN", b"nokey", b"z", b"0"]),
6107            (
6108                Flow::Continue,
6109                "*3\r\n$1\r\nz\r\n$1\r\na\r\n$1\r\n1\r\n".to_owned()
6110            )
6111        );
6112        assert_eq!(
6113            f.run(&[b"BZPOPMAX", b"z", b"0"]),
6114            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n$1\r\n3\r\n"
6115        );
6116        f.run(&[b"ZADD", b"z", b"1", b"a", b"3", b"c"]);
6117        assert_eq!(
6118            f.run(&[
6119                b"BZMPOP", b"0", b"2", b"nokey", b"z", b"MIN", b"COUNT", b"2"
6120            ]),
6121            "*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"
6122        );
6123        f.out = Out::new(Proto::Resp3);
6124        assert_eq!(
6125            f.run(&[b"BZPOPMIN", b"z", b"0"]),
6126            "*3\r\n$1\r\nz\r\n$1\r\nc\r\n,3\r\n"
6127        );
6128        f.out = Out::new(Proto::Resp2);
6129        // Nothing to take, so the client is parked and nothing was written.
6130        assert_eq!(
6131            f.flow(&[b"BZPOPMIN", b"z", b"0"]),
6132            (Flow::Block, String::new())
6133        );
6134        assert_eq!(
6135            f.flow(&[b"BZMPOP", b"0", b"1", b"z", b"MIN"]),
6136            (Flow::Block, String::new())
6137        );
6138        // The timeout is read before the key count, so this complains about the
6139        // timeout and not about the count.
6140        assert_eq!(
6141            f.run(&[b"BZMPOP", b"abc", b"0", b"z", b"MIN"]),
6142            "-ERR timeout is not a float or out of range\r\n"
6143        );
6144        assert_eq!(
6145            f.run(&[b"BZMPOP", b"0", b"0", b"z", b"MIN"]),
6146            "-ERR numkeys should be greater than 0\r\n"
6147        );
6148        assert_eq!(
6149            f.run(&[b"BZPOPMIN", b"z", b"-1"]),
6150            "-ERR timeout is negative\r\n"
6151        );
6152    }
6153
6154    /// A parked sorted set client is served by whatever puts a member under one
6155    /// of its keys, and is not served by something of another type landing
6156    /// there.
6157    #[test]
6158    fn a_parked_sorted_set_client_waits_for_a_member_and_not_for_a_key() {
6159        let mut f = Fixture::new();
6160        assert_eq!(f.flow(&[b"BZPOPMIN", b"z", b"0"]).0, Flow::Block);
6161        assert_eq!(f.server.waiters().len(), 1);
6162        // A string under the key is not what it asked for, so it stays parked
6163        // rather than being handed a WRONGTYPE on a command that was accepted.
6164        f.run(&[b"SET", b"z", b"v"]);
6165        let mut out = Out::new(Proto::Resp2);
6166        assert!(!f.server.serve_waiter(0, 0, &mut out));
6167        assert!(out.as_slice().is_empty());
6168        f.run(&[b"DEL", b"z"]);
6169        f.run(&[b"ZADD", b"z", b"5", b"m"]);
6170        assert!(f.server.serve_waiter(0, 0, &mut out));
6171        assert_eq!(
6172            core::str::from_utf8(out.as_slice()).expect("ascii"),
6173            "*3\r\n$1\r\nz\r\n$1\r\nm\r\n$1\r\n5\r\n"
6174        );
6175        // And the member is gone, which is what makes a queue of workers on a
6176        // sorted set work at all.
6177        assert_eq!(f.run(&[b"EXISTS", b"z"]), ":0\r\n");
6178    }
6179
6180    #[test]
6181    fn every_sorted_set_command_says_wrongtype_and_writes_nothing() {
6182        let mut f = Fixture::new();
6183        f.run(&[b"SET", b"s", b"v"]);
6184        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6185        for cmd in [
6186            &[b"ZADD".as_slice(), b"s", b"1", b"a"][..],
6187            &[b"ZINCRBY", b"s", b"1", b"a"],
6188            &[b"ZCARD", b"s"],
6189            &[b"ZSCORE", b"s", b"a"],
6190            &[b"ZMSCORE", b"s", b"a"],
6191            &[b"ZREM", b"s", b"a"],
6192            &[b"ZRANK", b"s", b"a"],
6193            &[b"ZREVRANK", b"s", b"a"],
6194            &[b"ZCOUNT", b"s", b"1", b"2"],
6195            &[b"ZLEXCOUNT", b"s", b"-", b"+"],
6196            &[b"ZRANGE", b"s", b"0", b"-1"],
6197            &[b"ZREVRANGE", b"s", b"0", b"-1"],
6198            &[b"ZRANGEBYSCORE", b"s", b"1", b"2"],
6199            &[b"ZREVRANGEBYSCORE", b"s", b"2", b"1"],
6200            &[b"ZRANGEBYLEX", b"s", b"-", b"+"],
6201            &[b"ZREVRANGEBYLEX", b"s", b"+", b"-"],
6202            &[b"ZRANGESTORE", b"d", b"s", b"0", b"-1"],
6203            &[b"ZREMRANGEBYRANK", b"s", b"0", b"-1"],
6204            &[b"ZREMRANGEBYSCORE", b"s", b"1", b"2"],
6205            &[b"ZREMRANGEBYLEX", b"s", b"-", b"+"],
6206            &[b"ZUNION", b"1", b"s"],
6207            &[b"ZINTER", b"1", b"s"],
6208            &[b"ZDIFF", b"1", b"s"],
6209            &[b"ZUNIONSTORE", b"d", b"1", b"s"],
6210            &[b"ZINTERSTORE", b"d", b"1", b"s"],
6211            &[b"ZDIFFSTORE", b"d", b"1", b"s"],
6212            &[b"ZINTERCARD", b"1", b"s"],
6213            &[b"ZRANDMEMBER", b"s"],
6214            &[b"ZSCAN", b"s", b"0"],
6215            &[b"ZPOPMIN", b"s"],
6216            &[b"ZPOPMAX", b"s", b"2"],
6217            &[b"ZMPOP", b"1", b"s", b"MIN"],
6218            &[b"BZPOPMIN", b"s", b"0"],
6219            &[b"BZPOPMAX", b"s", b"0"],
6220            &[b"BZMPOP", b"0", b"1", b"s", b"MIN"],
6221        ] {
6222            assert_eq!(f.run(cmd), wrong, "{:?}", cmd[0]);
6223        }
6224        assert_eq!(f.run(&[b"GET", b"s"]), "$1\r\nv\r\n");
6225    }
6226
6227    /// The same churn the set, the string and the list get, because a sorted
6228    /// set that leaks a tree node per add looks exactly like one that does not
6229    /// until it has run for an afternoon.
6230    #[test]
6231    fn churning_sorted_sets_does_not_grow_the_server() {
6232        let mut f = Fixture::new();
6233        let members: Vec<Vec<u8>> = (0..200).map(|i| format!("m{i}").into_bytes()).collect();
6234        let scores: Vec<Vec<u8>> = (0..200).map(|i| format!("{i}").into_bytes()).collect();
6235        let mut args: Vec<&[u8]> = vec![b"ZADD", b"z"];
6236        for i in 0..200 {
6237            args.push(&scores[i]);
6238            args.push(&members[i]);
6239        }
6240
6241        f.run(&args);
6242        f.run(&[b"DEL", b"z"]);
6243        f.server.compact_step();
6244        let after_first = f.server.memory_bytes();
6245
6246        for _ in 0..200 {
6247            f.run(&args);
6248            f.run(&[b"DEL", b"z"]);
6249            f.server.compact_step();
6250        }
6251        assert_eq!(f.run(&[b"DBSIZE"]), ":0\r\n");
6252        assert!(
6253            f.server.memory_bytes() <= after_first * 2,
6254            "held {} after two hundred passes against {after_first} after one",
6255            f.server.memory_bytes()
6256        );
6257    }
6258
6259    // ----------------------------------------------------------------- array
6260
6261    #[test]
6262    fn an_array_writes_at_any_index_and_reads_back_what_it_sent() {
6263        let mut f = Fixture::new();
6264        // Three consecutive positions from a high index, and the reply is how
6265        // many of them were empty before rather than how many were written.
6266        assert_eq!(
6267            f.run(&[b"ARSET", b"a", b"1000", b"x", b"y", b"z"]),
6268            ":3\r\n"
6269        );
6270        assert_eq!(f.run(&[b"ARSET", b"a", b"1000", b"X", b"Y"]), ":0\r\n");
6271        assert_eq!(f.run(&[b"ARGET", b"a", b"1000"]), "$1\r\nX\r\n");
6272        assert_eq!(f.run(&[b"ARGET", b"a", b"1002"]), "$1\r\nz\r\n");
6273        // A hole and a key that is not there are the same answer.
6274        assert_eq!(f.run(&[b"ARGET", b"a", b"999"]), "$-1\r\n");
6275        assert_eq!(f.run(&[b"ARGET", b"nope", b"0"]), "$-1\r\n");
6276        assert_eq!(
6277            f.run(&[b"ARMGET", b"a", b"1002", b"999", b"1000"]),
6278            "*3\r\n$1\r\nz\r\n$-1\r\n$1\r\nX\r\n"
6279        );
6280        // Scattered pairs in one command, last write wins within it.
6281        assert_eq!(f.run(&[b"ARMSET", b"a", b"5", b"p", b"5", b"q"]), ":1\r\n");
6282        assert_eq!(f.run(&[b"ARGET", b"a", b"5"]), "$1\r\nq\r\n");
6283    }
6284
6285    /// The two numbers an array reports are not the same number, and one of
6286    /// them does not fit a signed integer.
6287    #[test]
6288    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
6289        let mut f = Fixture::new();
6290        assert_eq!(f.run(&[b"ARLEN", b"nope"]), ":0\r\n");
6291        assert_eq!(f.run(&[b"ARCOUNT", b"nope"]), ":0\r\n");
6292        f.run(&[b"ARMSET", b"a", b"0", b"x", b"9", b"y"]);
6293        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
6294        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
6295        // Deleting in the middle leaves the high water mark where it was.
6296        assert_eq!(f.run(&[b"ARDEL", b"a", b"0"]), ":1\r\n");
6297        assert_eq!(f.run(&[b"ARLEN", b"a"]), ":10\r\n");
6298        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":1\r\n");
6299
6300        // The top of the space is addressable, and its length is a number with
6301        // bit sixty three set, so the reply has to be unsigned or it comes back
6302        // negative.
6303        f.run(&[b"ARSET", b"top", b"18446744073709551614", b"z"]);
6304        assert_eq!(f.run(&[b"ARLEN", b"top"]), ":18446744073709551615\r\n");
6305        assert_eq!(f.run(&[b"ARCOUNT", b"top"]), ":1\r\n");
6306        // And one past it does not exist, so a write that would reach it fails
6307        // before any of it lands.
6308        assert_eq!(
6309            f.run(&[b"ARSET", b"over", b"18446744073709551614", b"a", b"b"]),
6310            "-ERR array index overflow\r\n"
6311        );
6312        assert_eq!(f.run(&[b"EXISTS", b"over"]), ":0\r\n");
6313    }
6314
6315    /// One reply per position and not one per element, which is the whole
6316    /// reason the range is capped.
6317    #[test]
6318    fn a_range_read_answers_for_the_holes_too_and_is_capped_at_a_million() {
6319        let mut f = Fixture::new();
6320        f.run(&[b"ARSET", b"a", b"1", b"x"]);
6321        assert_eq!(
6322            f.run(&[b"ARGETRANGE", b"a", b"0", b"3"]),
6323            "*4\r\n$-1\r\n$1\r\nx\r\n$-1\r\n$-1\r\n"
6324        );
6325        // The two ends may come in either order, and the answer is reversed
6326        // rather than empty.
6327        assert_eq!(
6328            f.run(&[b"ARGETRANGE", b"a", b"3", b"0"]),
6329            "*4\r\n$-1\r\n$-1\r\n$1\r\nx\r\n$-1\r\n"
6330        );
6331        // A key that is not there reads like an array of nothing but holes.
6332        assert_eq!(
6333            f.run(&[b"ARGETRANGE", b"nope", b"0", b"1"]),
6334            "*2\r\n$-1\r\n$-1\r\n"
6335        );
6336        // A range wider than a million positions is refused and not trimmed,
6337        // because against a missing key it is a request for as many nulls as
6338        // the range is wide.
6339        assert_eq!(
6340            f.run(&[b"ARGETRANGE", b"nope", b"0", b"18446744073709551614"]),
6341            "-ERR range exceeds maximum of 1000000 items\r\n"
6342        );
6343    }
6344
6345    /// Every index in the argument list is read before the key is touched, so
6346    /// a bad one at the end leaves nothing half written.
6347    #[test]
6348    fn a_bad_index_late_in_the_line_writes_none_of_the_earlier_ones() {
6349        let mut f = Fixture::new();
6350        assert_eq!(
6351            f.run(&[b"ARMSET", b"a", b"0", b"x", b"-1", b"y"]),
6352            "-ERR invalid array index\r\n"
6353        );
6354        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
6355        f.run(&[b"ARSET", b"a", b"0", b"x", b"y", b"z"]);
6356        assert_eq!(
6357            f.run(&[b"ARDEL", b"a", b"0", b"01"]),
6358            "-ERR invalid array index\r\n"
6359        );
6360        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":3\r\n");
6361        // An index is unsigned here, so the numbers a list would take are not
6362        // the last element, they are errors.
6363        assert_eq!(
6364            f.run(&[b"ARGET", b"a", b"-1"]),
6365            "-ERR invalid array index\r\n"
6366        );
6367        // And a pair list with an odd tail is an arity error rather than a
6368        // syntax one.
6369        assert_eq!(
6370            f.run(&[b"ARMSET", b"a", b"0", b"x", b"1"]),
6371            "-ERR wrong number of arguments for 'armset' command\r\n"
6372        );
6373        assert_eq!(
6374            f.run(&[b"ARDELRANGE", b"a", b"0", b"1", b"2"]),
6375            "-ERR wrong number of arguments for 'ardelrange' command\r\n"
6376        );
6377    }
6378
6379    #[test]
6380    fn a_range_delete_costs_the_elements_and_takes_the_key_when_it_empties() {
6381        let mut f = Fixture::new();
6382        f.run(&[b"ARSET", b"a", b"0", b"0", b"1", b"2", b"3", b"4"]);
6383        assert_eq!(f.run(&[b"ARDELRANGE", b"a", b"3", b"1"]), ":3\r\n");
6384        assert_eq!(f.run(&[b"ARCOUNT", b"a"]), ":2\r\n");
6385        // Two ranges in one command, and the second one covers the whole space
6386        // without walking it.
6387        assert_eq!(
6388            f.run(&[
6389                b"ARDELRANGE",
6390                b"a",
6391                b"100",
6392                b"200",
6393                b"0",
6394                b"18446744073709551614"
6395            ]),
6396            ":2\r\n"
6397        );
6398        assert_eq!(f.run(&[b"EXISTS", b"a"]), ":0\r\n");
6399        assert_eq!(f.run(&[b"ARDELRANGE", b"nope", b"0", b"1"]), ":0\r\n");
6400        assert_eq!(f.run(&[b"ARDEL", b"nope", b"0"]), ":0\r\n");
6401    }
6402
6403    /// A value goes out as the bytes it came in as, whichever of the three ways
6404    /// the array found to store it.
6405    #[test]
6406    fn a_value_comes_back_byte_for_byte_however_it_was_packed() {
6407        let mut f = Fixture::new();
6408        let long = vec![b'v'; 200];
6409        f.run(&[
6410            b"ARMSET", b"a", b"0", b"42", b"1", b"007", b"2", b"3.5", b"3", b"3.14", b"4",
6411            b"short", b"5", &long, b"6", b"-0",
6412        ]);
6413        // 42 is an integer, 007 is not one because it does not print back the
6414        // same, 3.5 survives a double and 3.14 does not, and the last two are a
6415        // word packed string and a blob.
6416        assert_eq!(
6417            f.run(&[b"ARGETRANGE", b"a", b"0", b"6"]),
6418            format!(
6419                "*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",
6420                String::from_utf8_lossy(&long)
6421            )
6422        );
6423    }
6424
6425    #[test]
6426    fn an_array_is_a_type_and_an_encoding_a_client_can_see() {
6427        let mut f = Fixture::new();
6428        f.run(&[b"ARSET", b"a", b"0", b"x"]);
6429        assert_eq!(f.run(&[b"TYPE", b"a"]), "+array\r\n");
6430        assert_eq!(
6431            f.run(&[b"OBJECT", b"ENCODING", b"a"]),
6432            "$12\r\nsliced-array\r\n"
6433        );
6434        // And it is a body like any other, so the key commands work on it.
6435        assert_eq!(f.run(&[b"EXPIRE", b"a", b"100"]), ":1\r\n");
6436        assert_eq!(f.run(&[b"PERSIST", b"a"]), ":1\r\n");
6437        assert_eq!(f.run(&[b"COPY", b"a", b"b"]), ":1\r\n");
6438        assert_eq!(f.run(&[b"ARGET", b"b", b"0"]), "$1\r\nx\r\n");
6439        assert_eq!(f.run(&[b"RENAME", b"a", b"c"]), "+OK\r\n");
6440        assert_eq!(f.run(&[b"ARCOUNT", b"c"]), ":1\r\n");
6441    }
6442
6443    #[test]
6444    fn every_array_command_refuses_a_key_holding_something_else() {
6445        let mut f = Fixture::new();
6446        f.run(&[b"SET", b"s", b"v"]);
6447        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6448        for cmd in [
6449            &[b"ARSET".as_ref(), b"s", b"0", b"x"][..],
6450            &[b"ARMSET".as_ref(), b"s", b"0", b"x"][..],
6451            &[b"ARGET".as_ref(), b"s", b"0"][..],
6452            &[b"ARMGET".as_ref(), b"s", b"0"][..],
6453            &[b"ARGETRANGE".as_ref(), b"s", b"0", b"1"][..],
6454            &[b"ARLEN".as_ref(), b"s"][..],
6455            &[b"ARCOUNT".as_ref(), b"s"][..],
6456            &[b"ARDEL".as_ref(), b"s", b"0"][..],
6457            &[b"ARDELRANGE".as_ref(), b"s", b"0", b"1"][..],
6458            &[b"ARINSERT".as_ref(), b"s", b"x"][..],
6459            &[b"ARRING".as_ref(), b"s", b"4", b"x"][..],
6460            &[b"ARNEXT".as_ref(), b"s"][..],
6461            &[b"ARSEEK".as_ref(), b"s", b"1"][..],
6462            &[b"ARLASTITEMS".as_ref(), b"s", b"1"][..],
6463            &[b"ARSCAN".as_ref(), b"s", b"0", b"1"][..],
6464            &[b"ARGREP".as_ref(), b"s", b"0", b"1", b"EXACT", b"v"][..],
6465            &[b"AROP".as_ref(), b"s", b"0", b"1", b"SUM"][..],
6466            &[b"ARINFO".as_ref(), b"s"][..],
6467        ] {
6468            assert_eq!(f.run(cmd), wrong, "{}", String::from_utf8_lossy(cmd[0]));
6469        }
6470    }
6471
6472    /// Two of the array commands look the key up before they read the index and
6473    /// the rest read the index first, so the same broken argument gets two
6474    /// different errors depending on which command it went to.
6475    #[test]
6476    fn a_bad_index_reports_the_type_only_where_redis_reports_it() {
6477        let mut f = Fixture::new();
6478        f.run(&[b"SET", b"s", b"v"]);
6479        let wrong = "-WRONGTYPE Operation against a key holding the wrong kind of value\r\n";
6480        let bad = "-ERR invalid array index\r\n";
6481        assert_eq!(f.run(&[b"ARGET", b"s", b"-1"]), wrong);
6482        assert_eq!(f.run(&[b"ARMGET", b"s", b"0", b"-1"]), wrong);
6483        assert_eq!(f.run(&[b"ARSET", b"s", b"-1", b"x"]), bad);
6484        assert_eq!(f.run(&[b"ARDEL", b"s", b"-1"]), bad);
6485        assert_eq!(f.run(&[b"ARSCAN", b"s", b"-1", b"0"]), bad);
6486        assert_eq!(f.run(&[b"ARGREP", b"s", b"-1", b"0", b"EXACT", b"v"]), bad);
6487        // And on a key that is an array the index is just an index.
6488        f.run(&[b"ARSET", b"a", b"0", b"x"]);
6489        assert_eq!(f.run(&[b"ARGET", b"a", b"-1"]), bad);
6490        assert_eq!(f.run(&[b"ARGET", b"nope", b"-1"]), bad);
6491    }
6492
6493    #[test]
6494    fn an_append_follows_a_cursor_the_client_can_move() {
6495        let mut f = Fixture::new();
6496        assert_eq!(f.run(&[b"ARNEXT", b"nope"]), ":0\r\n");
6497        assert_eq!(f.run(&[b"ARINSERT", b"a", b"x", b"y"]), ":1\r\n");
6498        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":2\r\n");
6499        assert_eq!(f.run(&[b"ARINSERT", b"a", b"z"]), ":2\r\n");
6500        assert_eq!(f.run(&[b"ARGET", b"a", b"2"]), "$1\r\nz\r\n");
6501
6502        // A seek says where the next one goes, and a missing key has no cursor
6503        // to move and is not created by the asking.
6504        assert_eq!(f.run(&[b"ARSEEK", b"nope", b"5"]), ":0\r\n");
6505        assert_eq!(f.run(&[b"EXISTS", b"nope"]), ":0\r\n");
6506        assert_eq!(f.run(&[b"ARSEEK", b"a", b"100"]), ":1\r\n");
6507        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":100\r\n");
6508        assert_eq!(f.run(&[b"ARINSERT", b"a", b"far"]), ":100\r\n");
6509        assert_eq!(f.run(&[b"ARSEEK", b"a", b"0"]), ":1\r\n");
6510        assert_eq!(f.run(&[b"ARNEXT", b"a"]), ":0\r\n");
6511
6512        // The top of the space is the one index only ARSEEK will take, and it
6513        // leaves the cursor with nowhere to go.
6514        assert_eq!(f.run(&[b"ARSEEK", b"a", b"18446744073709551615"]), ":1\r\n");
6515        assert_eq!(f.run(&[b"ARNEXT", b"a"]), "$-1\r\n");
6516        assert_eq!(
6517            f.run(&[b"ARINSERT", b"a", b"x"]),
6518            "-ERR insert index overflow\r\n"
6519        );
6520        assert_eq!(
6521            f.run(&[b"ARSET", b"a", b"18446744073709551615", b"x"]),
6522            "-ERR invalid array index\r\n"
6523        );
6524    }
6525
6526    #[test]
6527    fn a_ring_keeps_the_newest_and_renumbers_them_when_it_is_resized() {
6528        let mut f = Fixture::new();
6529        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"a", b"b", b"c"]), ":2\r\n");
6530        assert_eq!(f.run(&[b"ARRING", b"r", b"3", b"d", b"e"]), ":1\r\n");
6531        assert_eq!(f.run(&[b"ARLEN", b"r"]), ":3\r\n");
6532        assert_eq!(
6533            f.run(&[b"ARGETRANGE", b"r", b"0", b"2"]),
6534            "*3\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nc\r\n"
6535        );
6536        // Growing it after it has wrapped puts the survivors back in the order
6537        // they arrived, which is the whole point of paying for the rebuild.
6538        assert_eq!(f.run(&[b"ARRING", b"r", b"5", b"f"]), ":3\r\n");
6539        assert_eq!(
6540            f.run(&[b"ARGETRANGE", b"r", b"0", b"3"]),
6541            "*4\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n$1\r\nf\r\n"
6542        );
6543        // The size is read before the key, so a bad one is a bad size wherever
6544        // it is sent.
6545        assert_eq!(
6546            f.run(&[b"ARRING", b"r", b"0", b"x"]),
6547            "-ERR size must be positive\r\n"
6548        );
6549        assert_eq!(
6550            f.run(&[b"ARRING", b"r", b"big", b"x"]),
6551            "-ERR invalid size\r\n"
6552        );
6553    }
6554
6555    #[test]
6556    fn the_last_items_walk_back_from_the_cursor_and_report_the_holes() {
6557        let mut f = Fixture::new();
6558        assert_eq!(f.run(&[b"ARLASTITEMS", b"nope", b"5"]), "*0\r\n");
6559        f.run(&[b"ARRING", b"r", b"4", b"a", b"b", b"c", b"d", b"e"]);
6560        assert_eq!(
6561            f.run(&[b"ARLASTITEMS", b"r", b"3"]),
6562            "*3\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n"
6563        );
6564        assert_eq!(
6565            f.run(&[b"ARLASTITEMS", b"r", b"3", b"rev"]),
6566            "*3\r\n$1\r\ne\r\n$1\r\nd\r\n$1\r\nc\r\n"
6567        );
6568        assert_eq!(
6569            f.run(&[b"ARLASTITEMS", b"r", b"99"]),
6570            "*4\r\n$1\r\nb\r\n$1\r\nc\r\n$1\r\nd\r\n$1\r\ne\r\n",
6571            "more than there is gets what there is"
6572        );
6573        // Nothing asked for is an empty reply, and Redis answers that before it
6574        // has read the option or looked at the key.
6575        assert_eq!(f.run(&[b"ARLASTITEMS", b"r", b"0", b"junk"]), "*0\r\n");
6576        assert_eq!(
6577            f.run(&[b"ARLASTITEMS", b"r", b"1", b"junk"]),
6578            "-ERR syntax error\r\n"
6579        );
6580        assert_eq!(
6581            f.run(&[b"ARLASTITEMS", b"r", b"nine"]),
6582            "-ERR invalid COUNT\r\n"
6583        );
6584
6585        // With no cursor the tail of the array is the anchor, and a hole inside
6586        // the window is reported as one.
6587        f.run(&[b"ARMSET", b"h", b"0", b"x", b"2", b"z"]);
6588        assert_eq!(
6589            f.run(&[b"ARLASTITEMS", b"h", b"5"]),
6590            "*2\r\n$-1\r\n$1\r\nz\r\n"
6591        );
6592    }
6593
6594    #[test]
6595    fn a_scan_answers_pairs_for_what_is_there_and_skips_what_is_not() {
6596        let mut f = Fixture::new();
6597        assert_eq!(f.run(&[b"ARSCAN", b"nope", b"0", b"10"]), "*0\r\n");
6598        f.run(&[b"ARMSET", b"a", b"0", b"x", b"7", b"y", b"1000000", b"z"]);
6599        // The whole index space, which ARGETRANGE refuses and this one answers
6600        // in three visits because holes cost nothing.
6601        assert_eq!(
6602            f.run(&[b"ARSCAN", b"a", b"0", b"18446744073709551614"]),
6603            "*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"
6604        );
6605        assert_eq!(
6606            f.run(&[
6607                b"ARSCAN",
6608                b"a",
6609                b"18446744073709551614",
6610                b"0",
6611                b"LIMIT",
6612                b"1"
6613            ]),
6614            "*1\r\n*2\r\n:1000000\r\n$1\r\nz\r\n"
6615        );
6616        assert_eq!(f.run(&[b"ARSCAN", b"a", b"1", b"6"]), "*0\r\n");
6617        assert_eq!(
6618            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT", b"0"]),
6619            "-ERR LIMIT must be positive\r\n"
6620        );
6621        assert_eq!(
6622            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"NOPE", b"1"]),
6623            "-ERR syntax error\r\n"
6624        );
6625        assert_eq!(
6626            f.run(&[b"ARSCAN", b"a", b"0", b"10", b"LIMIT"]),
6627            "-ERR wrong number of arguments for 'arscan' command\r\n"
6628        );
6629    }
6630
6631    #[test]
6632    fn a_grep_answers_the_indexes_whose_elements_match() {
6633        let mut f = Fixture::new();
6634        assert_eq!(
6635            f.run(&[b"ARGREP", b"nope", b"0", b"10", b"EXACT", b"x"]),
6636            "*0\r\n"
6637        );
6638        f.run(&[b"ARSET", b"a", b"0", b"alpha", b"beta", b"gamma", b"ALPHA"]);
6639
6640        // The two bounds take the ends of the array as well as an index, and a
6641        // reversed range is walked backwards the way ARSCAN walks one.
6642        assert_eq!(
6643            f.run(&[b"ARGREP", b"a", b"-", b"+", b"GLOB", b"*a"]),
6644            "*3\r\n:0\r\n:1\r\n:2\r\n"
6645        );
6646        assert_eq!(
6647            f.run(&[b"ARGREP", b"a", b"+", b"-", b"GLOB", b"*a"]),
6648            "*3\r\n:2\r\n:1\r\n:0\r\n"
6649        );
6650        assert_eq!(
6651            f.run(&[b"ARGREP", b"a", b"1", b"2", b"GLOB", b"*a"]),
6652            "*2\r\n:1\r\n:2\r\n"
6653        );
6654
6655        // One test each. NOCASE reaches all four of them and it may be written
6656        // after the pattern it applies to.
6657        assert_eq!(
6658            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha"]),
6659            "*1\r\n:0\r\n"
6660        );
6661        assert_eq!(
6662            f.run(&[b"ARGREP", b"a", b"-", b"+", b"EXACT", b"alpha", b"NOCASE"]),
6663            "*2\r\n:0\r\n:3\r\n"
6664        );
6665        assert_eq!(
6666            f.run(&[b"ARGREP", b"a", b"-", b"+", b"MATCH", b"mm"]),
6667            "*1\r\n:2\r\n"
6668        );
6669        assert_eq!(
6670            f.run(&[b"ARGREP", b"a", b"-", b"+", b"RE", b"^[bg]"]),
6671            "*2\r\n:1\r\n:2\r\n"
6672        );
6673
6674        // OR is the default and AND has to be asked for, and either way the
6675        // last of a repeated option wins.
6676        let both: &[&[u8]] = &[
6677            b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al",
6678        ];
6679        assert_eq!(f.run(both), "*2\r\n:0\r\n:1\r\n");
6680        assert_eq!(
6681            f.run(&[
6682                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND"
6683            ]),
6684            "*0\r\n"
6685        );
6686        assert_eq!(
6687            f.run(&[
6688                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"beta", b"MATCH", b"al", b"AND", b"OR"
6689            ]),
6690            "*2\r\n:0\r\n:1\r\n"
6691        );
6692
6693        // WITHVALUES turns each hit into a pair, and LIMIT counts the hits and
6694        // not the positions it had to look at.
6695        assert_eq!(
6696            f.run(&[
6697                b"ARGREP",
6698                b"a",
6699                b"-",
6700                b"+",
6701                b"MATCH",
6702                b"a",
6703                b"WITHVALUES",
6704                b"LIMIT",
6705                b"2"
6706            ]),
6707            "*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"
6708        );
6709        assert_eq!(
6710            f.run(&[
6711                b"ARGREP", b"a", b"-", b"+", b"EXACT", b"ALPHA", b"LIMIT", b"1"
6712            ]),
6713            "*1\r\n:3\r\n"
6714        );
6715    }
6716
6717    /// Everything ARGREP refuses, in the order it refuses it.
6718    #[test]
6719    fn a_grep_reports_a_broken_command_the_way_redis_does() {
6720        let mut f = Fixture::new();
6721        f.run(&[b"ARSET", b"a", b"0", b"alpha"]);
6722        let syntax = "-ERR syntax error\r\n";
6723
6724        // The bounds are read before the plan, so a bad index beats a bad
6725        // predicate whichever way round the two are written.
6726        assert_eq!(
6727            f.run(&[b"ARGREP", b"a", b"-1", b"0", b"NOPE", b"x"]),
6728            "-ERR invalid array index\r\n"
6729        );
6730        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOPE", b"x"]), syntax);
6731        // A keyword with nothing after it, and a command that asks for nothing.
6732        assert_eq!(
6733            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"EXACT"]),
6734            syntax
6735        );
6736        assert_eq!(
6737            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT"]),
6738            syntax
6739        );
6740        assert_eq!(
6741            f.run(&[b"ARGREP", b"a", b"0", b"1", b"NOCASE", b"WITHVALUES"]),
6742            syntax,
6743            "a command with no predicate in it at all"
6744        );
6745        assert_eq!(
6746            f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"0"]),
6747            "-ERR LIMIT must be positive\r\n"
6748        );
6749        assert_eq!(
6750            f.run(&[
6751                b"ARGREP", b"a", b"0", b"1", b"EXACT", b"x", b"LIMIT", b"nine"
6752            ]),
6753            "-ERR value is not an integer or out of range\r\n"
6754        );
6755        assert_eq!(
6756            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b""]),
6757            "-ERR regular expression is empty\r\n"
6758        );
6759        assert_eq!(
6760            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", b"(a"]),
6761            "-ERR invalid regular expression: Missing ')'\r\n"
6762        );
6763        assert_eq!(
6764            f.run(&[b"ARGREP", b"a", b"0", b"1", b"RE", br"(a)\1"]),
6765            "-ERR regular expression backreferences are not supported\r\n"
6766        );
6767        // The arity is minus six, so a predicate keyword with no pattern after
6768        // it is short by one and never reaches the parser.
6769        let arity = "-ERR wrong number of arguments for 'argrep' command\r\n";
6770        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1", b"EXACT"]), arity);
6771        assert_eq!(f.run(&[b"ARGREP", b"a", b"0", b"1"]), arity);
6772    }
6773
6774    #[test]
6775    fn an_op_reduces_a_range_to_one_number() {
6776        let mut f = Fixture::new();
6777        f.run(&[b"ARSET", b"a", b"0", b"1", b"2.5", b"word", b"-4"]);
6778        assert_eq!(
6779            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM"]),
6780            "$4\r\n-0.5\r\n"
6781        );
6782        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"min"]), "$2\r\n-4\r\n");
6783        assert_eq!(
6784            f.run(&[b"AROP", b"a", b"0", b"10", b"MAX"]),
6785            "$3\r\n2.5\r\n"
6786        );
6787        assert_eq!(f.run(&[b"AROP", b"a", b"0", b"10", b"USED"]), ":4\r\n");
6788        assert_eq!(
6789            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH", b"word"]),
6790            ":1\r\n"
6791        );
6792        // An aggregate is written with seventeen significant digits, which is
6793        // Redis's own choice and not what a score comes back as.
6794        f.run(&[b"ARSET", b"t", b"0", b"0.1", b"0.2"]);
6795        assert_eq!(
6796            f.run(&[b"AROP", b"t", b"0", b"10", b"SUM"]),
6797            "$19\r\n0.30000000000000004\r\n"
6798        );
6799        assert_eq!(f.run(&[b"ZADD", b"z", b"0.3", b"m"]), ":1\r\n");
6800        assert_eq!(f.run(&[b"ZSCORE", b"z", b"m"]), "$3\r\n0.3\r\n");
6801
6802        // Nothing to work with is a null, and a missing key is a null for the
6803        // aggregates and a zero for the two that count.
6804        f.run(&[b"ARSET", b"w", b"0", b"word"]);
6805        assert_eq!(f.run(&[b"AROP", b"w", b"0", b"10", b"SUM"]), "$-1\r\n");
6806        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"SUM"]), "$-1\r\n");
6807        assert_eq!(f.run(&[b"AROP", b"nope", b"0", b"10", b"USED"]), ":0\r\n");
6808
6809        assert_eq!(
6810            f.run(&[b"AROP", b"a", b"0", b"10", b"NOPE"]),
6811            "-ERR unknown operation\r\n"
6812        );
6813        assert_eq!(
6814            f.run(&[b"AROP", b"a", b"0", b"10", b"MATCH"]),
6815            "-ERR MATCH requires a value argument\r\n"
6816        );
6817        assert_eq!(
6818            f.run(&[b"AROP", b"a", b"0", b"10", b"SUM", b"extra"]),
6819            "-ERR wrong number of arguments for 'arop' command\r\n"
6820        );
6821    }
6822
6823    #[test]
6824    fn the_info_is_a_map_and_a_missing_key_is_an_error() {
6825        let mut f = Fixture::new();
6826        assert_eq!(f.run(&[b"ARINFO", b"nope"]), "-ERR no such key\r\n");
6827        f.run(&[b"ARINSERT", b"a", b"x", b"y"]);
6828        let short = f.run(&[b"ARINFO", b"a"]);
6829        assert!(
6830            short.starts_with("*14\r\n"),
6831            "seven pairs on RESP2: {short}"
6832        );
6833        assert!(short.contains("$5\r\ncount\r\n:2\r\n"), "{short}");
6834        assert!(
6835            short.contains("$17\r\nnext-insert-index\r\n:2\r\n"),
6836            "{short}"
6837        );
6838        assert!(short.contains("$10\r\nslice-size\r\n:4096\r\n"), "{short}");
6839        let full = f.run(&[b"ARINFO", b"a", b"full"]);
6840        assert!(full.starts_with("*24\r\n"), "twelve pairs: {full}");
6841        // Two values one apart are held sparsely, so the dense count is zero and
6842        // the two dense averages have nothing to average.
6843        assert!(full.contains("$12\r\ndense-slices\r\n:0\r\n"), "{full}");
6844        assert!(full.contains("$13\r\nsparse-slices\r\n:1\r\n"), "{full}");
6845        assert!(
6846            full.contains("$14\r\navg-dense-size\r\n$1\r\n0\r\n"),
6847            "{full}"
6848        );
6849        assert_eq!(f.run(&[b"ARINFO", b"a", b"nope"]), "-ERR syntax error\r\n");
6850
6851        // On RESP3 the same reply is a map and the averages are doubles.
6852        let mut g = Fixture::new();
6853        g.run(&[b"HELLO", b"3"]);
6854        g.run(&[b"ARINSERT", b"a", b"x"]);
6855        let map = g.run(&[b"ARINFO", b"a", b"FULL"]);
6856        assert!(map.starts_with("%12\r\n"), "{map}");
6857        assert!(map.contains("$5\r\ncount\r\n:1\r\n"), "{map}");
6858        assert!(map.contains("$14\r\navg-dense-size\r\n,0\r\n"), "{map}");
6859    }
6860
6861    #[test]
6862    fn a_double_on_the_wire_is_written_the_way_redis_writes_one() {
6863        let mut f = Fixture::new();
6864        // Whole numbers up to two to the sixty second come back as integers,
6865        // and past that the digit generator takes over and uses an exponent.
6866        for (score, want) in [
6867            ("3", "3"),
6868            ("3.5", "3.5"),
6869            ("0.3", "0.3"),
6870            ("1e30", "1e+30"),
6871            ("1e19", "1e+19"),
6872            ("1e-7", "1e-7"),
6873            ("0.000001", "0.000001"),
6874            ("4611686018427387904", "4611686018427387904"),
6875            ("-0", "-0"),
6876        ] {
6877            f.run(&[b"ZADD", b"z", score.as_bytes(), b"m"]);
6878            assert_eq!(
6879                f.run(&[b"ZSCORE", b"z", b"m"]),
6880                format!("${}\r\n{want}\r\n", want.len()),
6881                "score {score}"
6882            );
6883        }
6884
6885        // The same bytes on RESP3, where the reply is a double rather than a
6886        // bulk string.
6887        let mut g = Fixture::new();
6888        g.run(&[b"HELLO", b"3"]);
6889        g.run(&[b"ZADD", b"z", b"1e30", b"m"]);
6890        assert_eq!(g.run(&[b"ZSCORE", b"z", b"m"]), ",1e+30\r\n");
6891        // The two float increments are not this printer. They go through
6892        // ld2string in its human mode, which is a fixed point conversion with
6893        // the trailing zeros taken off, so they never write an exponent, and
6894        // they reply with a bulk string on both protocols.
6895        assert_eq!(
6896            g.run(&[b"INCRBYFLOAT", b"s", b"1e30"]),
6897            "$31\r\n1000000000000000000000000000000\r\n"
6898        );
6899        assert_eq!(g.run(&[b"INCRBYFLOAT", b"t", b"0.1"]), "$3\r\n0.1\r\n");
6900        assert_eq!(
6901            g.run(&[b"HINCRBYFLOAT", b"h", b"f", b"1e19"]),
6902            "$20\r\n10000000000000000000\r\n"
6903        );
6904    }
6905}