From ad9ea30fc2817b9e71c2aefbbec46b2b8a6f2029 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Wed, 8 Apr 2026 23:58:30 -0700 Subject: [PATCH] test(csv): add RFC 4180 edge case tests for CSV quote handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit confirms the csv crate correctly handles all RFC 4180 cases: - Embedded commas in quoted fields - Escaped quotes ("") within quoted fields - Embedded newlines in quoted fields - Combined commas + escaped quotes No bugs found — added 4 regression tests to document compliance. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/import/csv_parser.rs | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/import/csv_parser.rs b/src/import/csv_parser.rs index 2b4c37a..ed7e790 100644 --- a/src/import/csv_parser.rs +++ b/src/import/csv_parser.rs @@ -214,6 +214,70 @@ mod tests { assert_eq!(records[0]["Name"], Value::String("A".to_string())); } + // ── RFC 4180 edge cases ─────────────────────────────────────────── + + #[test] + fn rfc4180_embedded_comma_in_quoted_field() { + let (path, _dir) = create_temp_csv( + "Name,Address,Value\n\"Smith, John\",\"123 Main St, Apt 4\",100", + ); + let records = parse_csv(&path).unwrap(); + assert_eq!(records.len(), 1); + assert_eq!( + records[0]["Name"], + Value::String("Smith, John".to_string()) + ); + assert_eq!( + records[0]["Address"], + Value::String("123 Main St, Apt 4".to_string()) + ); + } + + #[test] + fn rfc4180_escaped_quotes_in_field() { + // RFC 4180: doubled quotes ("") inside a quoted field represent a literal quote + let (path, _dir) = create_temp_csv( + "Name,Description,Value\nWidget,\"A \"\"great\"\" product\",10", + ); + let records = parse_csv(&path).unwrap(); + assert_eq!(records.len(), 1); + assert_eq!( + records[0]["Description"], + Value::String("A \"great\" product".to_string()) + ); + } + + #[test] + fn rfc4180_newline_in_quoted_field() { + // RFC 4180: quoted fields may contain newlines + let (path, _dir) = create_temp_csv( + "Name,Notes,Value\n\"Widget\",\"Line 1\nLine 2\",10", + ); + let records = parse_csv(&path).unwrap(); + assert_eq!(records.len(), 1); + assert_eq!( + records[0]["Notes"], + Value::String("Line 1\nLine 2".to_string()) + ); + } + + #[test] + fn rfc4180_embedded_comma_and_quotes_combined() { + let (path, _dir) = create_temp_csv( + "Name,Desc\n\"Smith, \"\"Jr.\"\"\",\"Said \"\"hello, world\"\"\"", + ); + let records = parse_csv(&path).unwrap(); + assert_eq!(records.len(), 1); + assert_eq!( + records[0]["Name"], + Value::String("Smith, \"Jr.\"".to_string()) + ); + assert_eq!( + records[0]["Desc"], + Value::String("Said \"hello, world\"".to_string()) + ); + } + #[test] fn parse_checking_csv_format() { // Simulates the format of /Users/edwlan/Downloads/Checking1.csv