Skip to main content

memcache_proto/
command.rs

1//! Server-side command parsing for Memcache ASCII protocol.
2//!
3//! This module parses incoming Memcache requests into structured commands.
4
5use crate::error::ParseError;
6
7/// Default maximum key size in bytes (memcached default is 250)
8pub const DEFAULT_MAX_KEY_LEN: usize = 250;
9
10/// Default maximum value size in bytes (memcached default is 1MB)
11pub const DEFAULT_MAX_VALUE_LEN: usize = 1024 * 1024;
12
13/// Default maximum number of keys in a multi-GET command (batch size).
14pub const DEFAULT_MAX_KEYS: usize = 1024;
15
16/// Configuration options for command parsing.
17///
18/// These options allow customizing the DoS protection limits for different
19/// deployment scenarios. More restrictive limits provide better protection
20/// against resource exhaustion attacks.
21#[derive(Debug, Clone, Copy)]
22pub struct ParseOptions {
23    /// Maximum key size in bytes.
24    pub max_key_len: usize,
25    /// Maximum value size in bytes.
26    pub max_value_len: usize,
27    /// Maximum number of keys in a multi-GET command.
28    pub max_keys: usize,
29}
30
31impl Default for ParseOptions {
32    fn default() -> Self {
33        Self {
34            max_key_len: DEFAULT_MAX_KEY_LEN,
35            max_value_len: DEFAULT_MAX_VALUE_LEN,
36            max_keys: DEFAULT_MAX_KEYS,
37        }
38    }
39}
40
41impl ParseOptions {
42    /// Create new parse options with default values.
43    pub const fn new() -> Self {
44        Self {
45            max_key_len: DEFAULT_MAX_KEY_LEN,
46            max_value_len: DEFAULT_MAX_VALUE_LEN,
47            max_keys: DEFAULT_MAX_KEYS,
48        }
49    }
50
51    /// Set the maximum key length.
52    pub const fn max_key_len(mut self, len: usize) -> Self {
53        self.max_key_len = len;
54        self
55    }
56
57    /// Set the maximum value length.
58    pub const fn max_value_len(mut self, len: usize) -> Self {
59        self.max_value_len = len;
60        self
61    }
62
63    /// Set the maximum number of keys in a multi-GET command.
64    pub const fn max_keys(mut self, count: usize) -> Self {
65        self.max_keys = count;
66        self
67    }
68
69    /// Calculate the maximum command line length based on the configured limits.
70    ///
71    /// The longest line is a multi-GET with max_keys keys of max_key_len each:
72    /// `get <key1> <key2> ... <keyN>\r\n`
73    pub const fn max_line_len(&self) -> usize {
74        // "get " + (key + space) * max_keys
75        4 + (self.max_key_len + 1) * self.max_keys
76    }
77}
78
79/// A parsed Memcache command with references to the original buffer.
80///
81/// Commands are parsed with zero-copy semantics where possible.
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub enum Command<'a> {
84    /// GET command
85    Get { key: &'a [u8] },
86    /// Multi-GET command
87    Gets { keys: Vec<&'a [u8]> },
88    /// SET command
89    Set {
90        key: &'a [u8],
91        flags: u32,
92        exptime: u32,
93        data: &'a [u8],
94    },
95    /// ADD command - store only if key doesn't exist
96    Add {
97        key: &'a [u8],
98        flags: u32,
99        exptime: u32,
100        data: &'a [u8],
101    },
102    /// REPLACE command - store only if key exists
103    Replace {
104        key: &'a [u8],
105        flags: u32,
106        exptime: u32,
107        data: &'a [u8],
108    },
109    /// CAS (compare-and-swap) command
110    Cas {
111        key: &'a [u8],
112        flags: u32,
113        exptime: u32,
114        data: &'a [u8],
115        cas_unique: u64,
116    },
117    /// DELETE command
118    Delete { key: &'a [u8] },
119    /// FLUSH_ALL command
120    FlushAll,
121    /// VERSION command
122    Version,
123    /// QUIT command
124    Quit,
125    /// INCR command - increment numeric value
126    Incr {
127        key: &'a [u8],
128        delta: u64,
129        noreply: bool,
130    },
131    /// DECR command - decrement numeric value
132    Decr {
133        key: &'a [u8],
134        delta: u64,
135        noreply: bool,
136    },
137    /// APPEND command - append data to existing value
138    Append {
139        key: &'a [u8],
140        data: &'a [u8],
141        noreply: bool,
142    },
143    /// PREPEND command - prepend data to existing value
144    Prepend {
145        key: &'a [u8],
146        data: &'a [u8],
147        noreply: bool,
148    },
149}
150
151impl<'a> Command<'a> {
152    /// Parse a command from a byte buffer using default options.
153    ///
154    /// Returns the parsed command and the number of bytes consumed.
155    ///
156    /// # Zero-copy
157    ///
158    /// The returned command contains references to the input buffer for keys
159    /// and data, avoiding allocation in the hot path.
160    #[inline]
161    pub fn parse(buffer: &'a [u8]) -> Result<(Self, usize), ParseError> {
162        Self::parse_with_options(buffer, &ParseOptions::default())
163    }
164
165    /// Parse a command from a byte buffer with custom options.
166    ///
167    /// This allows configuring DoS protection limits like maximum key size,
168    /// value size, and line length.
169    pub fn parse_with_options(
170        buffer: &'a [u8],
171        options: &ParseOptions,
172    ) -> Result<(Self, usize), ParseError> {
173        // Find the end of the command line
174        let max_line_len = options.max_line_len();
175        let line_end = find_crlf(buffer, max_line_len)?.ok_or(ParseError::Incomplete)?;
176        let line = &buffer[..line_end];
177        let mut parts = line.split(|&b| b == b' ');
178
179        let cmd = parts.next().ok_or(ParseError::Protocol("empty command"))?;
180
181        match cmd {
182            b"get" | b"GET" => {
183                // Collect all keys
184                let keys: Vec<&[u8]> = parts.filter(|k| !k.is_empty()).collect();
185                if keys.is_empty() {
186                    return Err(ParseError::Protocol("get requires key"));
187                }
188                if keys.len() > options.max_keys {
189                    return Err(ParseError::Protocol("too many keys"));
190                }
191                if keys.len() == 1 {
192                    Ok((Command::Get { key: keys[0] }, line_end + 2))
193                } else {
194                    Ok((Command::Gets { keys }, line_end + 2))
195                }
196            }
197
198            b"gets" | b"GETS" => {
199                // gets always returns CAS tokens — maps to Command::Gets
200                let keys: Vec<&[u8]> = parts.filter(|k| !k.is_empty()).collect();
201                if keys.is_empty() {
202                    return Err(ParseError::Protocol("gets requires key"));
203                }
204                if keys.len() > options.max_keys {
205                    return Err(ParseError::Protocol("too many keys"));
206                }
207                Ok((Command::Gets { keys }, line_end + 2))
208            }
209
210            b"set" | b"SET" => {
211                let key = parts
212                    .next()
213                    .ok_or(ParseError::Protocol("set requires key"))?;
214                if key.is_empty() {
215                    return Err(ParseError::Protocol("empty key"));
216                }
217                if key.len() > options.max_key_len {
218                    return Err(ParseError::Protocol("key too large"));
219                }
220                let flags_str = parts
221                    .next()
222                    .ok_or(ParseError::Protocol("set requires flags"))?;
223                let exptime_str = parts
224                    .next()
225                    .ok_or(ParseError::Protocol("set requires exptime"))?;
226                let bytes_str = parts
227                    .next()
228                    .ok_or(ParseError::Protocol("set requires bytes"))?;
229
230                let flags = parse_u32(flags_str)?;
231                let exptime = parse_u32(exptime_str)?;
232                let data_len = parse_usize(bytes_str)?;
233                if data_len > options.max_value_len {
234                    return Err(ParseError::Protocol("value too large"));
235                }
236
237                // Data block follows the command line: <data>\r\n
238                let data_start = line_end + 2;
239                let data_end = data_start
240                    .checked_add(data_len)
241                    .ok_or(ParseError::InvalidNumber)?;
242                let total_len = data_end.checked_add(2).ok_or(ParseError::InvalidNumber)?;
243
244                if buffer.len() < total_len {
245                    return Err(ParseError::Incomplete);
246                }
247
248                // Verify trailing \r\n
249                if buffer[data_end] != b'\r' || buffer[data_end + 1] != b'\n' {
250                    return Err(ParseError::Protocol("missing data terminator"));
251                }
252
253                let data = &buffer[data_start..data_end];
254                Ok((
255                    Command::Set {
256                        key,
257                        flags,
258                        exptime,
259                        data,
260                    },
261                    total_len,
262                ))
263            }
264
265            b"add" | b"ADD" => {
266                let key = parts
267                    .next()
268                    .ok_or(ParseError::Protocol("add requires key"))?;
269                if key.is_empty() {
270                    return Err(ParseError::Protocol("empty key"));
271                }
272                if key.len() > options.max_key_len {
273                    return Err(ParseError::Protocol("key too large"));
274                }
275                let flags_str = parts
276                    .next()
277                    .ok_or(ParseError::Protocol("add requires flags"))?;
278                let exptime_str = parts
279                    .next()
280                    .ok_or(ParseError::Protocol("add requires exptime"))?;
281                let bytes_str = parts
282                    .next()
283                    .ok_or(ParseError::Protocol("add requires bytes"))?;
284
285                let flags = parse_u32(flags_str)?;
286                let exptime = parse_u32(exptime_str)?;
287                let data_len = parse_usize(bytes_str)?;
288                if data_len > options.max_value_len {
289                    return Err(ParseError::Protocol("value too large"));
290                }
291
292                // Data block follows the command line: <data>\r\n
293                let data_start = line_end + 2;
294                let data_end = data_start
295                    .checked_add(data_len)
296                    .ok_or(ParseError::InvalidNumber)?;
297                let total_len = data_end.checked_add(2).ok_or(ParseError::InvalidNumber)?;
298
299                if buffer.len() < total_len {
300                    return Err(ParseError::Incomplete);
301                }
302
303                // Verify trailing \r\n
304                if buffer[data_end] != b'\r' || buffer[data_end + 1] != b'\n' {
305                    return Err(ParseError::Protocol("missing data terminator"));
306                }
307
308                let data = &buffer[data_start..data_end];
309                Ok((
310                    Command::Add {
311                        key,
312                        flags,
313                        exptime,
314                        data,
315                    },
316                    total_len,
317                ))
318            }
319
320            b"replace" | b"REPLACE" => {
321                let key = parts
322                    .next()
323                    .ok_or(ParseError::Protocol("replace requires key"))?;
324                if key.is_empty() {
325                    return Err(ParseError::Protocol("empty key"));
326                }
327                if key.len() > options.max_key_len {
328                    return Err(ParseError::Protocol("key too large"));
329                }
330                let flags_str = parts
331                    .next()
332                    .ok_or(ParseError::Protocol("replace requires flags"))?;
333                let exptime_str = parts
334                    .next()
335                    .ok_or(ParseError::Protocol("replace requires exptime"))?;
336                let bytes_str = parts
337                    .next()
338                    .ok_or(ParseError::Protocol("replace requires bytes"))?;
339
340                let flags = parse_u32(flags_str)?;
341                let exptime = parse_u32(exptime_str)?;
342                let data_len = parse_usize(bytes_str)?;
343                if data_len > options.max_value_len {
344                    return Err(ParseError::Protocol("value too large"));
345                }
346
347                // Data block follows the command line: <data>\r\n
348                let data_start = line_end + 2;
349                let data_end = data_start
350                    .checked_add(data_len)
351                    .ok_or(ParseError::InvalidNumber)?;
352                let total_len = data_end.checked_add(2).ok_or(ParseError::InvalidNumber)?;
353
354                if buffer.len() < total_len {
355                    return Err(ParseError::Incomplete);
356                }
357
358                // Verify trailing \r\n
359                if buffer[data_end] != b'\r' || buffer[data_end + 1] != b'\n' {
360                    return Err(ParseError::Protocol("missing data terminator"));
361                }
362
363                let data = &buffer[data_start..data_end];
364                Ok((
365                    Command::Replace {
366                        key,
367                        flags,
368                        exptime,
369                        data,
370                    },
371                    total_len,
372                ))
373            }
374
375            b"cas" | b"CAS" => {
376                let key = parts
377                    .next()
378                    .ok_or(ParseError::Protocol("cas requires key"))?;
379                if key.is_empty() {
380                    return Err(ParseError::Protocol("empty key"));
381                }
382                if key.len() > options.max_key_len {
383                    return Err(ParseError::Protocol("key too large"));
384                }
385                let flags_str = parts
386                    .next()
387                    .ok_or(ParseError::Protocol("cas requires flags"))?;
388                let exptime_str = parts
389                    .next()
390                    .ok_or(ParseError::Protocol("cas requires exptime"))?;
391                let bytes_str = parts
392                    .next()
393                    .ok_or(ParseError::Protocol("cas requires bytes"))?;
394                let cas_str = parts
395                    .next()
396                    .ok_or(ParseError::Protocol("cas requires cas_unique"))?;
397
398                let flags = parse_u32(flags_str)?;
399                let exptime = parse_u32(exptime_str)?;
400                let data_len = parse_usize(bytes_str)?;
401                let cas_unique = parse_u64(cas_str)?;
402                if data_len > options.max_value_len {
403                    return Err(ParseError::Protocol("value too large"));
404                }
405
406                // Data block follows the command line: <data>\r\n
407                let data_start = line_end + 2;
408                let data_end = data_start
409                    .checked_add(data_len)
410                    .ok_or(ParseError::InvalidNumber)?;
411                let total_len = data_end.checked_add(2).ok_or(ParseError::InvalidNumber)?;
412
413                if buffer.len() < total_len {
414                    return Err(ParseError::Incomplete);
415                }
416
417                // Verify trailing \r\n
418                if buffer[data_end] != b'\r' || buffer[data_end + 1] != b'\n' {
419                    return Err(ParseError::Protocol("missing data terminator"));
420                }
421
422                let data = &buffer[data_start..data_end];
423                Ok((
424                    Command::Cas {
425                        key,
426                        flags,
427                        exptime,
428                        data,
429                        cas_unique,
430                    },
431                    total_len,
432                ))
433            }
434
435            b"delete" | b"DELETE" => {
436                let key = parts
437                    .next()
438                    .ok_or(ParseError::Protocol("delete requires key"))?;
439                if key.is_empty() {
440                    return Err(ParseError::Protocol("empty key"));
441                }
442                Ok((Command::Delete { key }, line_end + 2))
443            }
444
445            b"flush_all" | b"FLUSH_ALL" => Ok((Command::FlushAll, line_end + 2)),
446
447            b"version" | b"VERSION" => Ok((Command::Version, line_end + 2)),
448
449            b"quit" | b"QUIT" => Ok((Command::Quit, line_end + 2)),
450
451            b"incr" | b"INCR" => {
452                let key = parts
453                    .next()
454                    .ok_or(ParseError::Protocol("incr requires key"))?;
455                if key.is_empty() {
456                    return Err(ParseError::Protocol("empty key"));
457                }
458                if key.len() > options.max_key_len {
459                    return Err(ParseError::Protocol("key too large"));
460                }
461                let delta_str = parts
462                    .next()
463                    .ok_or(ParseError::Protocol("incr requires delta"))?;
464                let delta = parse_u64(delta_str)?;
465                let noreply = parts
466                    .next()
467                    .map(|s| s == b"noreply" || s == b"NOREPLY")
468                    .unwrap_or(false);
469                Ok((
470                    Command::Incr {
471                        key,
472                        delta,
473                        noreply,
474                    },
475                    line_end + 2,
476                ))
477            }
478
479            b"decr" | b"DECR" => {
480                let key = parts
481                    .next()
482                    .ok_or(ParseError::Protocol("decr requires key"))?;
483                if key.is_empty() {
484                    return Err(ParseError::Protocol("empty key"));
485                }
486                if key.len() > options.max_key_len {
487                    return Err(ParseError::Protocol("key too large"));
488                }
489                let delta_str = parts
490                    .next()
491                    .ok_or(ParseError::Protocol("decr requires delta"))?;
492                let delta = parse_u64(delta_str)?;
493                let noreply = parts
494                    .next()
495                    .map(|s| s == b"noreply" || s == b"NOREPLY")
496                    .unwrap_or(false);
497                Ok((
498                    Command::Decr {
499                        key,
500                        delta,
501                        noreply,
502                    },
503                    line_end + 2,
504                ))
505            }
506
507            b"append" | b"APPEND" => {
508                let key = parts
509                    .next()
510                    .ok_or(ParseError::Protocol("append requires key"))?;
511                if key.is_empty() {
512                    return Err(ParseError::Protocol("empty key"));
513                }
514                if key.len() > options.max_key_len {
515                    return Err(ParseError::Protocol("key too large"));
516                }
517                // flags and exptime are required by protocol but ignored for append
518                let _flags_str = parts
519                    .next()
520                    .ok_or(ParseError::Protocol("append requires flags"))?;
521                let _exptime_str = parts
522                    .next()
523                    .ok_or(ParseError::Protocol("append requires exptime"))?;
524                let bytes_str = parts
525                    .next()
526                    .ok_or(ParseError::Protocol("append requires bytes"))?;
527
528                let data_len = parse_usize(bytes_str)?;
529                if data_len > options.max_value_len {
530                    return Err(ParseError::Protocol("value too large"));
531                }
532
533                // Check for noreply
534                let noreply = parts
535                    .next()
536                    .map(|s| s == b"noreply" || s == b"NOREPLY")
537                    .unwrap_or(false);
538
539                // Data block follows the command line: <data>\r\n
540                let data_start = line_end + 2;
541                let data_end = data_start
542                    .checked_add(data_len)
543                    .ok_or(ParseError::InvalidNumber)?;
544                let total_len = data_end.checked_add(2).ok_or(ParseError::InvalidNumber)?;
545
546                if buffer.len() < total_len {
547                    return Err(ParseError::Incomplete);
548                }
549
550                // Verify trailing \r\n
551                if buffer[data_end] != b'\r' || buffer[data_end + 1] != b'\n' {
552                    return Err(ParseError::Protocol("missing data terminator"));
553                }
554
555                let data = &buffer[data_start..data_end];
556                Ok((Command::Append { key, data, noreply }, total_len))
557            }
558
559            b"prepend" | b"PREPEND" => {
560                let key = parts
561                    .next()
562                    .ok_or(ParseError::Protocol("prepend requires key"))?;
563                if key.is_empty() {
564                    return Err(ParseError::Protocol("empty key"));
565                }
566                if key.len() > options.max_key_len {
567                    return Err(ParseError::Protocol("key too large"));
568                }
569                // flags and exptime are required by protocol but ignored for prepend
570                let _flags_str = parts
571                    .next()
572                    .ok_or(ParseError::Protocol("prepend requires flags"))?;
573                let _exptime_str = parts
574                    .next()
575                    .ok_or(ParseError::Protocol("prepend requires exptime"))?;
576                let bytes_str = parts
577                    .next()
578                    .ok_or(ParseError::Protocol("prepend requires bytes"))?;
579
580                let data_len = parse_usize(bytes_str)?;
581                if data_len > options.max_value_len {
582                    return Err(ParseError::Protocol("value too large"));
583                }
584
585                // Check for noreply
586                let noreply = parts
587                    .next()
588                    .map(|s| s == b"noreply" || s == b"NOREPLY")
589                    .unwrap_or(false);
590
591                // Data block follows the command line: <data>\r\n
592                let data_start = line_end + 2;
593                let data_end = data_start
594                    .checked_add(data_len)
595                    .ok_or(ParseError::InvalidNumber)?;
596                let total_len = data_end.checked_add(2).ok_or(ParseError::InvalidNumber)?;
597
598                if buffer.len() < total_len {
599                    return Err(ParseError::Incomplete);
600                }
601
602                // Verify trailing \r\n
603                if buffer[data_end] != b'\r' || buffer[data_end + 1] != b'\n' {
604                    return Err(ParseError::Protocol("missing data terminator"));
605                }
606
607                let data = &buffer[data_start..data_end];
608                Ok((Command::Prepend { key, data, noreply }, total_len))
609            }
610
611            _ => Err(ParseError::UnknownCommand),
612        }
613    }
614
615    /// Returns the command name as a string.
616    pub fn name(&self) -> &'static str {
617        match self {
618            Command::Get { .. } => "GET",
619            Command::Gets { .. } => "GETS",
620            Command::Set { .. } => "SET",
621            Command::Add { .. } => "ADD",
622            Command::Replace { .. } => "REPLACE",
623            Command::Cas { .. } => "CAS",
624            Command::Delete { .. } => "DELETE",
625            Command::FlushAll => "FLUSH_ALL",
626            Command::Version => "VERSION",
627            Command::Quit => "QUIT",
628            Command::Incr { .. } => "INCR",
629            Command::Decr { .. } => "DECR",
630            Command::Append { .. } => "APPEND",
631            Command::Prepend { .. } => "PREPEND",
632        }
633    }
634
635    /// Returns true if this command should close the connection.
636    pub fn is_quit(&self) -> bool {
637        matches!(self, Command::Quit)
638    }
639}
640
641/// Find \r\n in buffer, return position of \r.
642///
643/// Returns:
644/// - `Ok(Some(pos))` if CRLF found at position `pos`
645/// - `Ok(None)` if no CRLF found yet (need more data)
646/// - `Err(ParseError::Protocol)` if buffer exceeds max_line_len without CRLF
647fn find_crlf(buffer: &[u8], max_line_len: usize) -> Result<Option<usize>, ParseError> {
648    if let Some(pos) = memchr::memchr(b'\r', buffer)
649        .filter(|&pos| pos + 1 < buffer.len() && buffer[pos + 1] == b'\n')
650    {
651        return Ok(Some(pos));
652    }
653
654    // No CRLF found - check if we've exceeded the line length limit
655    if buffer.len() > max_line_len {
656        return Err(ParseError::Protocol("line too long"));
657    }
658
659    Ok(None)
660}
661
662/// Parse a u32 from ASCII decimal.
663fn parse_u32(data: &[u8]) -> Result<u32, ParseError> {
664    std::str::from_utf8(data)
665        .map_err(|_| ParseError::InvalidNumber)?
666        .parse()
667        .map_err(|_| ParseError::InvalidNumber)
668}
669
670/// Parse a u64 from ASCII decimal.
671fn parse_u64(data: &[u8]) -> Result<u64, ParseError> {
672    std::str::from_utf8(data)
673        .map_err(|_| ParseError::InvalidNumber)?
674        .parse()
675        .map_err(|_| ParseError::InvalidNumber)
676}
677
678/// Parse a usize from ASCII decimal.
679fn parse_usize(data: &[u8]) -> Result<usize, ParseError> {
680    std::str::from_utf8(data)
681        .map_err(|_| ParseError::InvalidNumber)?
682        .parse()
683        .map_err(|_| ParseError::InvalidNumber)
684}
685
686#[cfg(test)]
687mod tests {
688    use super::*;
689
690    #[test]
691    fn test_parse_get() {
692        let data = b"get mykey\r\n";
693        let (cmd, consumed) = Command::parse(data).unwrap();
694        assert_eq!(cmd, Command::Get { key: b"mykey" });
695        assert_eq!(consumed, data.len());
696    }
697
698    #[test]
699    fn test_parse_gets() {
700        let data = b"get key1 key2 key3\r\n";
701        let (cmd, consumed) = Command::parse(data).unwrap();
702        match cmd {
703            Command::Gets { keys } => {
704                assert_eq!(keys.len(), 3);
705                assert_eq!(keys[0], b"key1");
706                assert_eq!(keys[1], b"key2");
707                assert_eq!(keys[2], b"key3");
708            }
709            _ => panic!("expected Gets"),
710        }
711        assert_eq!(consumed, data.len());
712    }
713
714    #[test]
715    fn test_parse_set() {
716        let data = b"set mykey 0 3600 7\r\nmyvalue\r\n";
717        let (cmd, consumed) = Command::parse(data).unwrap();
718        match cmd {
719            Command::Set {
720                key,
721                flags,
722                exptime,
723                data: value,
724            } => {
725                assert_eq!(key, b"mykey");
726                assert_eq!(flags, 0);
727                assert_eq!(exptime, 3600);
728                assert_eq!(value, b"myvalue");
729            }
730            _ => panic!("expected Set"),
731        }
732        assert_eq!(consumed, data.len());
733    }
734
735    #[test]
736    fn test_parse_delete() {
737        let data = b"delete mykey\r\n";
738        let (cmd, consumed) = Command::parse(data).unwrap();
739        assert_eq!(cmd, Command::Delete { key: b"mykey" });
740        assert_eq!(consumed, data.len());
741    }
742
743    #[test]
744    fn test_parse_flush_all() {
745        let data = b"flush_all\r\n";
746        let (cmd, consumed) = Command::parse(data).unwrap();
747        assert_eq!(cmd, Command::FlushAll);
748        assert_eq!(consumed, data.len());
749    }
750
751    #[test]
752    fn test_parse_version() {
753        let data = b"version\r\n";
754        let (cmd, consumed) = Command::parse(data).unwrap();
755        assert_eq!(cmd, Command::Version);
756        assert_eq!(consumed, data.len());
757    }
758
759    #[test]
760    fn test_parse_quit() {
761        let data = b"quit\r\n";
762        let (cmd, consumed) = Command::parse(data).unwrap();
763        assert_eq!(cmd, Command::Quit);
764        assert!(cmd.is_quit());
765        assert_eq!(consumed, data.len());
766    }
767
768    #[test]
769    fn test_parse_case_insensitive() {
770        let (cmd, _) = Command::parse(b"GET mykey\r\n").unwrap();
771        assert_eq!(cmd, Command::Get { key: b"mykey" });
772
773        let (cmd, _) = Command::parse(b"SET k 0 0 1\r\nv\r\n").unwrap();
774        assert!(matches!(cmd, Command::Set { .. }));
775    }
776
777    #[test]
778    fn test_parse_incomplete() {
779        assert!(matches!(
780            Command::parse(b"set mykey 0 0 7\r\nmyval"),
781            Err(ParseError::Incomplete)
782        ));
783        assert!(matches!(
784            Command::parse(b"get mykey"),
785            Err(ParseError::Incomplete)
786        ));
787    }
788
789    #[test]
790    fn test_parse_unknown() {
791        assert!(matches!(
792            Command::parse(b"unknown\r\n"),
793            Err(ParseError::UnknownCommand)
794        ));
795    }
796
797    #[test]
798    fn test_command_name() {
799        assert_eq!(Command::Get { key: b"k" }.name(), "GET");
800        assert_eq!(
801            Command::Set {
802                key: b"k",
803                flags: 0,
804                exptime: 0,
805                data: b"v"
806            }
807            .name(),
808            "SET"
809        );
810        assert_eq!(Command::FlushAll.name(), "FLUSH_ALL");
811    }
812
813    // Additional tests for improved coverage
814
815    #[test]
816    fn test_command_name_all() {
817        assert_eq!(Command::Gets { keys: vec![b"k"] }.name(), "GETS");
818        assert_eq!(Command::Delete { key: b"k" }.name(), "DELETE");
819        assert_eq!(Command::Version.name(), "VERSION");
820        assert_eq!(Command::Quit.name(), "QUIT");
821    }
822
823    #[test]
824    fn test_parse_get_no_key() {
825        assert!(matches!(
826            Command::parse(b"get\r\n"),
827            Err(ParseError::Protocol("get requires key"))
828        ));
829    }
830
831    #[test]
832    fn test_parse_get_empty_key() {
833        // "get  \r\n" has empty parts which are filtered out
834        assert!(matches!(
835            Command::parse(b"get  \r\n"),
836            Err(ParseError::Protocol("get requires key"))
837        ));
838    }
839
840    #[test]
841    fn test_parse_set_missing_key() {
842        assert!(matches!(
843            Command::parse(b"set\r\n"),
844            Err(ParseError::Protocol("set requires key"))
845        ));
846    }
847
848    #[test]
849    fn test_parse_set_missing_flags() {
850        assert!(matches!(
851            Command::parse(b"set mykey\r\n"),
852            Err(ParseError::Protocol("set requires flags"))
853        ));
854    }
855
856    #[test]
857    fn test_parse_set_missing_exptime() {
858        assert!(matches!(
859            Command::parse(b"set mykey 0\r\n"),
860            Err(ParseError::Protocol("set requires exptime"))
861        ));
862    }
863
864    #[test]
865    fn test_parse_set_missing_bytes() {
866        assert!(matches!(
867            Command::parse(b"set mykey 0 0\r\n"),
868            Err(ParseError::Protocol("set requires bytes"))
869        ));
870    }
871
872    #[test]
873    fn test_parse_set_invalid_flags() {
874        assert!(matches!(
875            Command::parse(b"set mykey abc 0 5\r\nhello\r\n"),
876            Err(ParseError::InvalidNumber)
877        ));
878    }
879
880    #[test]
881    fn test_parse_set_invalid_exptime() {
882        assert!(matches!(
883            Command::parse(b"set mykey 0 xyz 5\r\nhello\r\n"),
884            Err(ParseError::InvalidNumber)
885        ));
886    }
887
888    #[test]
889    fn test_parse_set_invalid_bytes() {
890        assert!(matches!(
891            Command::parse(b"set mykey 0 0 abc\r\nhello\r\n"),
892            Err(ParseError::InvalidNumber)
893        ));
894    }
895
896    #[test]
897    fn test_parse_set_missing_terminator() {
898        // Data is correct length but doesn't have \r\n after
899        assert!(matches!(
900            Command::parse(b"set mykey 0 0 5\r\nhelloXX"),
901            Err(ParseError::Protocol("missing data terminator"))
902        ));
903    }
904
905    #[test]
906    fn test_parse_delete_missing_key() {
907        assert!(matches!(
908            Command::parse(b"delete\r\n"),
909            Err(ParseError::Protocol("delete requires key"))
910        ));
911    }
912
913    #[test]
914    fn test_parse_delete_empty_key() {
915        // The code checks if key.is_empty() - we need to trigger this
916        // Actually looking at the code, after parts.next() we check if empty
917        // Need to have a space followed by empty
918        assert!(matches!(
919            Command::parse(b"delete \r\n"),
920            Err(ParseError::Protocol("empty key"))
921        ));
922    }
923
924    #[test]
925    fn test_parse_case_insensitive_delete() {
926        let (cmd, _) = Command::parse(b"DELETE mykey\r\n").unwrap();
927        assert_eq!(cmd, Command::Delete { key: b"mykey" });
928    }
929
930    #[test]
931    fn test_parse_case_insensitive_flush_all() {
932        let (cmd, _) = Command::parse(b"FLUSH_ALL\r\n").unwrap();
933        assert_eq!(cmd, Command::FlushAll);
934    }
935
936    #[test]
937    fn test_parse_case_insensitive_version() {
938        let (cmd, _) = Command::parse(b"VERSION\r\n").unwrap();
939        assert_eq!(cmd, Command::Version);
940    }
941
942    #[test]
943    fn test_parse_case_insensitive_quit() {
944        let (cmd, _) = Command::parse(b"QUIT\r\n").unwrap();
945        assert_eq!(cmd, Command::Quit);
946    }
947
948    #[test]
949    fn test_is_quit_false() {
950        assert!(!Command::Get { key: b"k" }.is_quit());
951        assert!(
952            !Command::Set {
953                key: b"k",
954                flags: 0,
955                exptime: 0,
956                data: b"v"
957            }
958            .is_quit()
959        );
960        assert!(!Command::Delete { key: b"k" }.is_quit());
961        assert!(!Command::FlushAll.is_quit());
962        assert!(!Command::Version.is_quit());
963    }
964
965    #[test]
966    fn test_command_debug() {
967        let cmd = Command::Get { key: b"test" };
968        let debug_str = format!("{:?}", cmd);
969        assert!(debug_str.contains("Get"));
970    }
971
972    #[test]
973    fn test_command_clone() {
974        let cmd1 = Command::Get { key: b"test" };
975        let cmd2 = cmd1.clone();
976        assert_eq!(cmd1, cmd2);
977    }
978
979    #[test]
980    fn test_command_eq() {
981        assert_eq!(Command::FlushAll, Command::FlushAll);
982        assert_ne!(Command::FlushAll, Command::Version);
983        assert_eq!(Command::Get { key: b"k" }, Command::Get { key: b"k" });
984        assert_ne!(Command::Get { key: b"k1" }, Command::Get { key: b"k2" });
985    }
986
987    #[test]
988    fn test_parse_set_data_with_zeros() {
989        // Test that binary data with null bytes works
990        let data = b"set mykey 0 0 5\r\n\x00\x01\x02\x03\x04\r\n";
991        let (cmd, consumed) = Command::parse(data).unwrap();
992        match cmd {
993            Command::Set { data: value, .. } => {
994                assert_eq!(value, b"\x00\x01\x02\x03\x04");
995            }
996            _ => panic!("expected Set"),
997        }
998        assert_eq!(consumed, data.len());
999    }
1000
1001    #[test]
1002    fn test_find_crlf_edge_case() {
1003        // \r at end without \n
1004        assert!(matches!(
1005            Command::parse(b"get mykey\r"),
1006            Err(ParseError::Incomplete)
1007        ));
1008    }
1009
1010    #[test]
1011    fn test_parse_set_overflow_bytes() {
1012        // A value larger than MAX_VALUE_LEN is rejected with "value too large"
1013        // This catches the DoS before we even try arithmetic that could overflow
1014        assert!(matches!(
1015            Command::parse(b"SET k 0 0 18446744073709551615\r\n"),
1016            Err(ParseError::Protocol("value too large"))
1017        ));
1018
1019        // Test that truly invalid numbers (not parseable as usize) still error
1020        assert!(matches!(
1021            Command::parse(b"set k 0 0 abc\r\nhello\r\n"),
1022            Err(ParseError::InvalidNumber)
1023        ));
1024    }
1025
1026    #[test]
1027    fn test_parse_set_empty_key() {
1028        // SET with empty key (double space after SET)
1029        assert!(matches!(
1030            Command::parse(b"SET  0 0 5\r\nhello\r\n"),
1031            Err(ParseError::Protocol("empty key"))
1032        ));
1033    }
1034
1035    #[test]
1036    fn test_parse_line_too_long() {
1037        // Calculate the default max line length
1038        let max_line_len = ParseOptions::default().max_line_len();
1039
1040        // Create a buffer that exceeds max_line_len without CRLF
1041        let mut data = vec![b'g', b'e', b't', b' '];
1042        data.extend(std::iter::repeat_n(b'a', max_line_len + 1));
1043        // No CRLF - should error, not return Incomplete
1044        assert!(matches!(
1045            Command::parse(&data),
1046            Err(ParseError::Protocol("line too long"))
1047        ));
1048    }
1049
1050    #[test]
1051    fn test_parse_key_too_large() {
1052        // Key exceeds DEFAULT_MAX_KEY_LEN
1053        let mut data = b"set ".to_vec();
1054        data.extend(std::iter::repeat_n(b'a', DEFAULT_MAX_KEY_LEN + 1));
1055        data.extend(b" 0 0 5\r\nhello\r\n");
1056        assert!(matches!(
1057            Command::parse(&data),
1058            Err(ParseError::Protocol("key too large"))
1059        ));
1060    }
1061
1062    #[test]
1063    fn test_parse_value_too_large() {
1064        // Value size exceeds DEFAULT_MAX_VALUE_LEN
1065        let cmd = format!("set k 0 0 {}\r\n", DEFAULT_MAX_VALUE_LEN + 1);
1066        let mut data = cmd.as_bytes().to_vec();
1067        // Don't actually append the value data, just the header
1068        data.extend(std::iter::repeat_n(b'x', DEFAULT_MAX_VALUE_LEN + 1));
1069        data.extend(b"\r\n");
1070        assert!(matches!(
1071            Command::parse(&data),
1072            Err(ParseError::Protocol("value too large"))
1073        ));
1074    }
1075
1076    // ========================================================================
1077    // DoS Protection Edge Case Tests
1078    // ========================================================================
1079
1080    #[test]
1081    fn test_line_length_at_exact_limit() {
1082        let max_line_len = ParseOptions::default().max_line_len();
1083        // Line exactly at max_line_len should return Incomplete (need more data)
1084        let mut data = b"get ".to_vec();
1085        // Fill to exactly max_line_len bytes (no CRLF)
1086        let remaining = max_line_len - data.len();
1087        data.extend(std::iter::repeat_n(b'a', remaining));
1088        assert_eq!(data.len(), max_line_len);
1089        assert!(matches!(Command::parse(&data), Err(ParseError::Incomplete)));
1090    }
1091
1092    #[test]
1093    fn test_line_length_one_over_limit() {
1094        let max_line_len = ParseOptions::default().max_line_len();
1095        // Line at max_line_len + 1 should error (not Incomplete)
1096        let mut data = b"get ".to_vec();
1097        let remaining = max_line_len + 1 - data.len();
1098        data.extend(std::iter::repeat_n(b'a', remaining));
1099        assert_eq!(data.len(), max_line_len + 1);
1100        assert!(matches!(
1101            Command::parse(&data),
1102            Err(ParseError::Protocol("line too long"))
1103        ));
1104    }
1105
1106    #[test]
1107    fn test_long_line_with_crlf_within_limit() {
1108        // A line close to the limit but with CRLF should parse
1109        // With default max_keys=256 and max_key_len=250, we have plenty of room
1110        let mut data = b"get ".to_vec();
1111        // Add enough keys to approach but not exceed limit
1112        for i in 0..10 {
1113            data.extend(format!("key{} ", i).as_bytes());
1114        }
1115        data.extend(b"\r\n");
1116        let result = Command::parse(&data);
1117        assert!(result.is_ok());
1118    }
1119
1120    #[test]
1121    fn test_key_length_at_exact_limit() {
1122        // Key exactly at DEFAULT_MAX_KEY_LEN should succeed
1123        let mut data = b"set ".to_vec();
1124        data.extend(std::iter::repeat_n(b'k', DEFAULT_MAX_KEY_LEN));
1125        data.extend(b" 0 0 1\r\nv\r\n");
1126        let result = Command::parse(&data);
1127        assert!(result.is_ok());
1128        if let Ok((Command::Set { key, .. }, _)) = result {
1129            assert_eq!(key.len(), DEFAULT_MAX_KEY_LEN);
1130        }
1131    }
1132
1133    #[test]
1134    fn test_key_length_one_over_limit() {
1135        // Key at DEFAULT_MAX_KEY_LEN + 1 should fail
1136        let mut data = b"set ".to_vec();
1137        data.extend(std::iter::repeat_n(b'k', DEFAULT_MAX_KEY_LEN + 1));
1138        data.extend(b" 0 0 1\r\nv\r\n");
1139        assert!(matches!(
1140            Command::parse(&data),
1141            Err(ParseError::Protocol("key too large"))
1142        ));
1143    }
1144
1145    #[test]
1146    fn test_value_length_at_exact_limit() {
1147        // Value exactly at DEFAULT_MAX_VALUE_LEN should succeed
1148        let cmd = format!("set k 0 0 {}\r\n", DEFAULT_MAX_VALUE_LEN);
1149        let mut data = cmd.as_bytes().to_vec();
1150        data.extend(std::iter::repeat_n(b'v', DEFAULT_MAX_VALUE_LEN));
1151        data.extend(b"\r\n");
1152        let result = Command::parse(&data);
1153        assert!(result.is_ok());
1154        if let Ok((Command::Set { data: value, .. }, _)) = result {
1155            assert_eq!(value.len(), DEFAULT_MAX_VALUE_LEN);
1156        }
1157    }
1158
1159    #[test]
1160    fn test_value_length_one_over_limit() {
1161        // Value at DEFAULT_MAX_VALUE_LEN + 1 should fail
1162        let cmd = format!("set k 0 0 {}\r\n", DEFAULT_MAX_VALUE_LEN + 1);
1163        let mut data = cmd.as_bytes().to_vec();
1164        data.extend(std::iter::repeat_n(b'v', DEFAULT_MAX_VALUE_LEN + 1));
1165        data.extend(b"\r\n");
1166        assert!(matches!(
1167            Command::parse(&data),
1168            Err(ParseError::Protocol("value too large"))
1169        ));
1170    }
1171
1172    #[test]
1173    fn test_multiget_many_keys() {
1174        // Multi-GET with many keys should work up to line limit
1175        let mut data = b"get".to_vec();
1176        for i in 0..100 {
1177            data.extend(format!(" key{}", i).as_bytes());
1178        }
1179        data.extend(b"\r\n");
1180
1181        let result = Command::parse(&data);
1182        assert!(result.is_ok());
1183        if let Ok((Command::Gets { keys }, _)) = result {
1184            assert_eq!(keys.len(), 100);
1185        }
1186    }
1187
1188    #[test]
1189    fn test_arithmetic_overflow_protection() {
1190        // Ensure checked arithmetic prevents overflow
1191        // data_start + data_len + 2 could overflow without checked_add
1192        // We already reject huge data_len via DEFAULT_MAX_VALUE_LEN, but test the check
1193
1194        // A value size that would overflow usize when added to data_start
1195        // This is caught by the "value too large" check first
1196        let cmd = format!("set k 0 0 {}\r\n", usize::MAX);
1197        let data = cmd.as_bytes();
1198        let result = Command::parse(data);
1199        // Should fail with "value too large" (early check) or InvalidNumber
1200        assert!(result.is_err());
1201    }
1202
1203    #[test]
1204    fn test_empty_buffer() {
1205        assert!(matches!(Command::parse(b""), Err(ParseError::Incomplete)));
1206    }
1207
1208    #[test]
1209    fn test_only_whitespace_no_crlf() {
1210        let data = b"   ";
1211        assert!(matches!(Command::parse(data), Err(ParseError::Incomplete)));
1212    }
1213
1214    #[test]
1215    fn test_cr_without_lf() {
1216        let data = b"get key\r";
1217        assert!(matches!(Command::parse(data), Err(ParseError::Incomplete)));
1218    }
1219
1220    #[test]
1221    fn test_cr_without_lf_exceeds_limit() {
1222        let max_line_len = ParseOptions::default().max_line_len();
1223        // \r found but no \n, and buffer exceeds limit
1224        let mut data = b"get ".to_vec();
1225        data.extend(std::iter::repeat_n(b'a', max_line_len));
1226        data.push(b'\r');
1227        assert!(matches!(
1228            Command::parse(&data),
1229            Err(ParseError::Protocol("line too long"))
1230        ));
1231    }
1232
1233    #[test]
1234    fn test_get_with_key_at_limit() {
1235        // GET doesn't check key length, but make sure it parses
1236        let mut data = b"get ".to_vec();
1237        data.extend(std::iter::repeat_n(b'k', DEFAULT_MAX_KEY_LEN));
1238        data.extend(b"\r\n");
1239        let result = Command::parse(&data);
1240        assert!(result.is_ok());
1241    }
1242
1243    #[test]
1244    fn test_delete_with_key_not_checked() {
1245        // DELETE doesn't currently check key length in the same way SET does
1246        // It only checks for empty key
1247        let mut data = b"delete ".to_vec();
1248        data.extend(std::iter::repeat_n(b'k', DEFAULT_MAX_KEY_LEN + 100));
1249        data.extend(b"\r\n");
1250        // DELETE doesn't have key length validation like SET does
1251        // This test documents current behavior - may want to add validation
1252        let result = Command::parse(&data);
1253        assert!(result.is_ok()); // Currently passes - could be a gap to fix
1254    }
1255
1256    #[test]
1257    fn test_too_many_keys() {
1258        // Multi-GET with too many keys should fail
1259        let mut data = b"get".to_vec();
1260        for i in 0..DEFAULT_MAX_KEYS + 1 {
1261            data.extend(format!(" k{}", i).as_bytes());
1262        }
1263        data.extend(b"\r\n");
1264        assert!(matches!(
1265            Command::parse(&data),
1266            Err(ParseError::Protocol("too many keys"))
1267        ));
1268    }
1269
1270    #[test]
1271    fn test_custom_options() {
1272        // Test that custom options work correctly
1273        let options = ParseOptions::new()
1274            .max_key_len(10)
1275            .max_value_len(100)
1276            .max_keys(5);
1277
1278        // Key at custom limit should work
1279        let mut data = b"set kkkkkkkkkk 0 0 1\r\nv\r\n".to_vec();
1280        let result = Command::parse_with_options(&data, &options);
1281        assert!(result.is_ok());
1282
1283        // Key over custom limit should fail
1284        data = b"set kkkkkkkkkkk 0 0 1\r\nv\r\n".to_vec(); // 11 chars
1285        let result = Command::parse_with_options(&data, &options);
1286        assert!(matches!(result, Err(ParseError::Protocol("key too large"))));
1287
1288        // Value over custom limit should fail
1289        let cmd = b"set k 0 0 101\r\n";
1290        let mut value_data = cmd.to_vec();
1291        value_data.extend(std::iter::repeat_n(b'v', 101));
1292        value_data.extend(b"\r\n");
1293        let result = Command::parse_with_options(&value_data, &options);
1294        assert!(matches!(
1295            result,
1296            Err(ParseError::Protocol("value too large"))
1297        ));
1298
1299        // Too many keys with custom limit should fail
1300        let data = b"get k1 k2 k3 k4 k5 k6\r\n";
1301        let result = Command::parse_with_options(data, &options);
1302        assert!(matches!(result, Err(ParseError::Protocol("too many keys"))));
1303    }
1304
1305    // ========================================================================
1306    // INCR/DECR Tests
1307    // ========================================================================
1308
1309    #[test]
1310    fn test_parse_incr() {
1311        let data = b"incr counter 5\r\n";
1312        let (cmd, consumed) = Command::parse(data).unwrap();
1313        match cmd {
1314            Command::Incr {
1315                key,
1316                delta,
1317                noreply,
1318            } => {
1319                assert_eq!(key, b"counter");
1320                assert_eq!(delta, 5);
1321                assert!(!noreply);
1322            }
1323            _ => panic!("expected Incr"),
1324        }
1325        assert_eq!(consumed, data.len());
1326    }
1327
1328    #[test]
1329    fn test_parse_incr_noreply() {
1330        let data = b"incr counter 10 noreply\r\n";
1331        let (cmd, consumed) = Command::parse(data).unwrap();
1332        match cmd {
1333            Command::Incr {
1334                key,
1335                delta,
1336                noreply,
1337            } => {
1338                assert_eq!(key, b"counter");
1339                assert_eq!(delta, 10);
1340                assert!(noreply);
1341            }
1342            _ => panic!("expected Incr"),
1343        }
1344        assert_eq!(consumed, data.len());
1345    }
1346
1347    #[test]
1348    fn test_parse_incr_case_insensitive() {
1349        let (cmd, _) = Command::parse(b"INCR key 1\r\n").unwrap();
1350        assert!(matches!(cmd, Command::Incr { .. }));
1351    }
1352
1353    #[test]
1354    fn test_parse_incr_noreply_case_insensitive() {
1355        let (cmd, _) = Command::parse(b"incr key 1 NOREPLY\r\n").unwrap();
1356        match cmd {
1357            Command::Incr { noreply, .. } => assert!(noreply),
1358            _ => panic!("expected Incr"),
1359        }
1360    }
1361
1362    #[test]
1363    fn test_parse_incr_missing_key() {
1364        assert!(matches!(
1365            Command::parse(b"incr\r\n"),
1366            Err(ParseError::Protocol("incr requires key"))
1367        ));
1368    }
1369
1370    #[test]
1371    fn test_parse_incr_empty_key() {
1372        assert!(matches!(
1373            Command::parse(b"incr  5\r\n"),
1374            Err(ParseError::Protocol("empty key"))
1375        ));
1376    }
1377
1378    #[test]
1379    fn test_parse_incr_missing_delta() {
1380        assert!(matches!(
1381            Command::parse(b"incr key\r\n"),
1382            Err(ParseError::Protocol("incr requires delta"))
1383        ));
1384    }
1385
1386    #[test]
1387    fn test_parse_incr_invalid_delta() {
1388        assert!(matches!(
1389            Command::parse(b"incr key abc\r\n"),
1390            Err(ParseError::InvalidNumber)
1391        ));
1392    }
1393
1394    #[test]
1395    fn test_parse_incr_key_too_large() {
1396        let mut data = b"incr ".to_vec();
1397        data.extend(std::iter::repeat_n(b'k', DEFAULT_MAX_KEY_LEN + 1));
1398        data.extend(b" 5\r\n");
1399        assert!(matches!(
1400            Command::parse(&data),
1401            Err(ParseError::Protocol("key too large"))
1402        ));
1403    }
1404
1405    #[test]
1406    fn test_parse_decr() {
1407        let data = b"decr counter 3\r\n";
1408        let (cmd, consumed) = Command::parse(data).unwrap();
1409        match cmd {
1410            Command::Decr {
1411                key,
1412                delta,
1413                noreply,
1414            } => {
1415                assert_eq!(key, b"counter");
1416                assert_eq!(delta, 3);
1417                assert!(!noreply);
1418            }
1419            _ => panic!("expected Decr"),
1420        }
1421        assert_eq!(consumed, data.len());
1422    }
1423
1424    #[test]
1425    fn test_parse_decr_noreply() {
1426        let data = b"decr counter 10 noreply\r\n";
1427        let (cmd, consumed) = Command::parse(data).unwrap();
1428        match cmd {
1429            Command::Decr {
1430                key,
1431                delta,
1432                noreply,
1433            } => {
1434                assert_eq!(key, b"counter");
1435                assert_eq!(delta, 10);
1436                assert!(noreply);
1437            }
1438            _ => panic!("expected Decr"),
1439        }
1440        assert_eq!(consumed, data.len());
1441    }
1442
1443    #[test]
1444    fn test_parse_decr_case_insensitive() {
1445        let (cmd, _) = Command::parse(b"DECR key 1\r\n").unwrap();
1446        assert!(matches!(cmd, Command::Decr { .. }));
1447    }
1448
1449    #[test]
1450    fn test_parse_decr_missing_key() {
1451        assert!(matches!(
1452            Command::parse(b"decr\r\n"),
1453            Err(ParseError::Protocol("decr requires key"))
1454        ));
1455    }
1456
1457    #[test]
1458    fn test_parse_decr_missing_delta() {
1459        assert!(matches!(
1460            Command::parse(b"decr key\r\n"),
1461            Err(ParseError::Protocol("decr requires delta"))
1462        ));
1463    }
1464
1465    #[test]
1466    fn test_parse_decr_invalid_delta() {
1467        assert!(matches!(
1468            Command::parse(b"decr key xyz\r\n"),
1469            Err(ParseError::InvalidNumber)
1470        ));
1471    }
1472
1473    #[test]
1474    fn test_incr_decr_command_names() {
1475        assert_eq!(
1476            Command::Incr {
1477                key: b"k",
1478                delta: 1,
1479                noreply: false
1480            }
1481            .name(),
1482            "INCR"
1483        );
1484        assert_eq!(
1485            Command::Decr {
1486                key: b"k",
1487                delta: 1,
1488                noreply: false
1489            }
1490            .name(),
1491            "DECR"
1492        );
1493    }
1494
1495    #[test]
1496    fn test_incr_decr_is_quit() {
1497        assert!(
1498            !Command::Incr {
1499                key: b"k",
1500                delta: 1,
1501                noreply: false
1502            }
1503            .is_quit()
1504        );
1505        assert!(
1506            !Command::Decr {
1507                key: b"k",
1508                delta: 1,
1509                noreply: false
1510            }
1511            .is_quit()
1512        );
1513    }
1514
1515    #[test]
1516    fn test_incr_large_delta() {
1517        let data = b"incr key 18446744073709551615\r\n"; // u64::MAX
1518        let (cmd, _) = Command::parse(data).unwrap();
1519        match cmd {
1520            Command::Incr { delta, .. } => {
1521                assert_eq!(delta, u64::MAX);
1522            }
1523            _ => panic!("expected Incr"),
1524        }
1525    }
1526}