Skip to main content

yo_resp/dispatch/
mod.rs

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