aboutsummaryrefslogtreecommitdiff
path: root/tests/test_parse_bulk_string.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_parse_bulk_string.rs')
-rw-r--r--tests/test_parse_bulk_string.rs214
1 files changed, 214 insertions, 0 deletions
diff --git a/tests/test_parse_bulk_string.rs b/tests/test_parse_bulk_string.rs
new file mode 100644
index 0000000..1543262
--- /dev/null
+++ b/tests/test_parse_bulk_string.rs
@@ -0,0 +1,214 @@
+use codecrafters_redis::resp_parser::*;
+
+#[test]
+fn test_valid_bulk_strings() {
+ // basic valid cases
+ assert_eq!(parse_bulk_strings(b"$2\r\nok\r\n").unwrap().0, "ok");
+ assert_eq!(parse_bulk_strings(b"$4\r\npong\r\n").unwrap().0, "pong");
+ assert_eq!(
+ parse_bulk_strings(b"$11\r\nhello world\r\n").unwrap().0,
+ "hello world"
+ );
+
+ // empty string
+ assert_eq!(parse_bulk_strings(b"$0\r\n\r\n").unwrap().0, "");
+
+ // string with special characters (including \r and \n - allowed in bulk strings)
+ assert_eq!(
+ parse_bulk_strings(b"$13\r\nhello\r\nworld!\r\n").unwrap().0,
+ "hello\r\nworld!"
+ );
+
+ // string with various ascii characters
+ assert_eq!(
+ parse_bulk_strings(b"$30\r\n!@#$%^&*()_+-={}[]|\\:;\"'<>?,./\r\n")
+ .unwrap()
+ .0,
+ "!@#$%^&*()_+-={}[]|\\:;\"'<>?,./"
+ );
+
+ // large string
+ let large_content = "x".repeat(1000);
+ let large_bulk = format!("$1000\r\n{}\r\n", large_content);
+ if let RespType::BulkString(bulk) = parse_bulk_strings(large_bulk.as_bytes()).unwrap().0 {}
+
+ assert_eq!(
+ parse_bulk_strings(large_bulk.as_bytes()).unwrap().0,
+ large_content
+ );
+
+ // string with only whitespace
+ assert_eq!(parse_bulk_strings(b"$3\r\n \r\n").unwrap().0, " ");
+
+ // string with tabs and newlines
+ assert_eq!(
+ parse_bulk_strings(b"$7\r\nhe\tllo\n\r\n").unwrap().0,
+ "he\tllo\n"
+ );
+}
+
+#[test]
+fn test_null_bulk_string() {
+ // Null bulk string
+ let (result, remaining) = parse_bulk_strings(b"$-1\r\n").unwrap();
+ assert_eq!(result, RespType::Null());
+ assert_eq!(remaining, b"");
+
+ // Null bulk string with remaining data
+ let (result, remaining) = parse_bulk_strings(b"$-1\r\n+OK\r\n").unwrap();
+ assert_eq!(result, RespType::Null());
+ assert_eq!(remaining, b"+OK\r\n");
+}
+
+#[test]
+fn test_invalid_bulk_strings() {
+ // Wrong data type marker
+ assert_eq!(
+ parse_bulk_strings(b"+OK\r\n").err().unwrap().message(),
+ "ERR Invalid data type"
+ );
+
+ // Invalid length format
+ assert_eq!(
+ parse_bulk_strings(b"$abc\r\n").err().unwrap().message(),
+ "ERR invalid value"
+ );
+
+ // Negative length (other than -1)
+ assert_eq!(
+ parse_bulk_strings(b"$-5\r\n").err().unwrap().message(),
+ "ERR invalid value"
+ );
+
+ // Missing length
+ assert_eq!(
+ parse_bulk_strings(b"$\r\n").err().unwrap().message(),
+ "ERR invalid value"
+ );
+
+ // Missing first \r\n after length
+ assert_eq!(
+ parse_bulk_strings(b"$5hello\r\n").err().unwrap().message(),
+ "ERR invalid value"
+ );
+
+ // Content shorter than declared length
+ assert_eq!(
+ parse_bulk_strings(b"$5\r\nhi\r\n").err().unwrap().message(),
+ "ERR Unexpected end of input"
+ );
+
+ // Content longer than declared length (missing final \r\n)
+ assert_eq!(
+ parse_bulk_strings(b"$2\r\nhello\r\n")
+ .err()
+ .unwrap()
+ .message(),
+ "ERR Unexpected end of input"
+ );
+
+ // Missing final \r\n
+ assert_eq!(
+ parse_bulk_strings(b"$5\r\nhello").err().unwrap().message(),
+ "ERR Unexpected end of input"
+ );
+
+ // Only \r without \n at the end
+ assert_eq!(
+ parse_bulk_strings(b"$5\r\nhello\r")
+ .err()
+ .unwrap()
+ .message(),
+ "ERR Unexpected end of input"
+ );
+
+ // Only \n without \r at the end
+ assert_eq!(
+ parse_bulk_strings(b"$5\r\nhello\n")
+ .err()
+ .unwrap()
+ .message(),
+ "ERR Unexpected end of input"
+ );
+
+ // Empty input
+ assert_eq!(
+ parse_bulk_strings(b"").err().unwrap().message(),
+ "ERR Empty data"
+ );
+
+ // Just the marker
+ assert_eq!(
+ parse_bulk_strings(b"$").err().unwrap().message(),
+ "ERR Unexpected end of input"
+ );
+
+ // Length too large for available data
+ assert_eq!(
+ parse_bulk_strings(b"$100\r\nshort\r\n")
+ .err()
+ .unwrap()
+ .message(),
+ "ERR Unexpected end of input"
+ );
+
+ // Zero length but with content
+ assert_eq!(
+ parse_bulk_strings(b"$0\r\nhello\r\n")
+ .err()
+ .unwrap()
+ .message(),
+ "ERR Unexpected end of input"
+ );
+}
+
+#[test]
+fn test_bulk_string_remaining_bytes() {
+ // Test that remaining bytes are correctly returned
+ let (string, remaining) = parse_bulk_strings(b"$5\r\nhello\r\nnext data").unwrap();
+ assert_eq!(string, "hello");
+ assert_eq!(remaining, b"next data");
+
+ // Test with multiple commands
+ let (string, remaining) = parse_bulk_strings(b"$4\r\ntest\r\n:42\r\n").unwrap();
+ assert_eq!(string, "test");
+ assert_eq!(remaining, b":42\r\n");
+
+ // Test with no remaining data
+ let (string, remaining) = parse_bulk_strings(b"$3\r\nend\r\n").unwrap();
+ assert_eq!(string, "end");
+ assert_eq!(remaining, b"");
+
+ // Test null string with remaining data
+ let (result, remaining) = parse_bulk_strings(b"$-1\r\n+PONG\r\n").unwrap();
+ assert_eq!(result, RespType::Null());
+ assert_eq!(remaining, b"+PONG\r\n");
+}
+
+#[test]
+fn test_bulk_string_edge_cases() {
+ // String that contains the exact sequence that would end it
+ assert_eq!(
+ parse_bulk_strings(b"$8\r\ntest\r\n\r\n\r\n").unwrap().0,
+ "test\r\n"
+ );
+
+ // String with only \r\n
+ assert_eq!(parse_bulk_strings(b"$2\r\n\r\n\r\n").unwrap().0, "\r\n");
+
+ // String that starts with numbers
+ assert_eq!(parse_bulk_strings(b"$5\r\n12345\r\n").unwrap().0, "12345");
+
+ // String with control characters
+ assert_eq!(
+ parse_bulk_strings(b"$5\r\n\x01\x02\x03\x04\x05\r\n")
+ .unwrap()
+ .0,
+ "\x01\x02\x03\x04\x05"
+ );
+
+ // Maximum length value (within reason)
+ let content = "a".repeat(65535);
+ let bulk = format!("$65535\r\n{}\r\n", content);
+ assert_eq!(parse_bulk_strings(bulk.as_bytes()).unwrap().0, content);
+}