/ tests / specification_compliance_complex.rs
specification_compliance_complex.rs
  1  use korni::{parse, Entry};
  2  
  3  // --- Security & Encoding Tests ---
  4  
  5  
  6  
  7  #[test]
  8  fn test_security_newline_injection_in_quotes() {
  9      // Spec 6.2: Newlines in quotes are literal, NOT new definitions
 10      let input = "SAFE=\"value\nKEY2=malicious\"";
 11      let entries = parse(input);
 12      assert_eq!(entries.len(), 1);
 13      let kv = entries[0].as_pair().unwrap();
 14      assert_eq!(kv.key, "SAFE");
 15      assert_eq!(kv.value, "value\nKEY2=malicious");
 16  }
 17  
 18  #[test]
 19  fn test_security_newline_injection_attempt_unquoted() {
 20      // Unquoted values end at newline.
 21      let input = "SAFE=value\nKEY2=malicious";
 22      let entries = parse(input);
 23      assert_eq!(entries.len(), 2);
 24      let kv1 = entries[0].as_pair().unwrap();
 25      assert_eq!(kv1.key, "SAFE");
 26      assert_eq!(kv1.value, "value");
 27      
 28      let kv2 = entries[1].as_pair().unwrap();
 29      assert_eq!(kv2.key, "KEY2");
 30      assert_eq!(kv2.value, "malicious");
 31  }
 32  
 33  // --- Complex Nesting & Escaping ---
 34  
 35  #[test]
 36  fn test_complex_nesting_deep() {
 37      // Spec 4.2.4: Quote types nest literally
 38      // Updated to use valid escaped quotes for inner quotes as parser tracks outermost only
 39      let input = r#"KEY="a 'b \"c\" d' e""#;
 40      let entries = parse(input);
 41      let kv = entries[0].as_pair().unwrap();
 42      assert_eq!(kv.value, r#"a 'b "c" d' e"#);
 43  }
 44  
 45  #[test]
 46  fn test_complex_escaping_madness() {
 47      // Spec 4.2.4: All escape rules combined
 48      // Input: KEY="tab:\t quote:\" slash:\\ dollar:\$ newline:\n"
 49      let input = r#"KEY="tab:\t quote:\" slash:\\ dollar:\$ newline:\n""#;
 50      let entries = parse(input);
 51      let kv = entries[0].as_pair().unwrap();
 52      assert_eq!(kv.value, "tab:\t quote:\" slash:\\ dollar:$ newline:\n");
 53  }
 54  
 55  #[test]
 56  fn test_variable_interpolation_is_literal() {
 57      // Spec 4.4: Interpolation is explicitly OUT OF SCOPE. Must return literal.
 58      let input = "Usage=${VAR} $VAR";
 59      let entries = parse(input);
 60      // Spec 4.2.2: Unquoted values end at first whitespace.
 61      // So `Usage=${VAR} $VAR` -> value is `${VAR}`. The rest is ignored junk.
 62      // This confirms interpolation is literal (no substitution) AND strict whitespace handling.
 63      let kv = entries[0].as_pair().unwrap();
 64      assert_eq!(kv.value, "${VAR}");
 65  }
 66  
 67  #[test]
 68  fn test_variable_interpolation_escaped_dollar() {
 69      // Spec 4.4: \$ becomes literal $
 70      let input = r#"KEY="\${VAR}""#;
 71      let entries = parse(input);
 72      let kv = entries[0].as_pair().unwrap();
 73      assert_eq!(kv.value, "${VAR}");
 74  }
 75  
 76  // --- Multiline Quirks ---
 77  
 78  #[test]
 79  fn test_multiline_empty_lines_preserved_quoted() {
 80      // Spec 5.1: Empty lines in quoted strings preserved
 81      let input = "TEXT=\"Line1\n\nLine3\"";
 82      let entries = parse(input);
 83      let kv = entries[0].as_pair().unwrap();
 84      assert_eq!(kv.value, "Line1\n\nLine3");
 85  }
 86  
 87  #[test]
 88  fn test_continuation_empty_lines_consumed() {
 89      // Spec 5.2 (Strict): Space terminates value.
 90      // "KEY=value\ \n\nmore" -> "value\". 
 91      // The space comes AFTER the backslash, so backslash is part of value.
 92      // Since we terminated at space (not newline), continuation is NOT triggered.
 93      let input = "KEY=value\\ \n\nmore";
 94      let entries = parse(input);
 95      let kv = entries[0].as_pair().unwrap();
 96      assert_eq!(kv.value, "value\\");
 97  }
 98  
 99  #[test]
100  fn test_continuation_space_preservation() {
101      // Spec 5.2 (Strict): Unquoted value ends at space.
102      // "KEY=val \ ..." -> "val".
103      let input = "KEY=val \\\n   ue";
104      let entries = parse(input);
105      let kv = entries[0].as_pair().unwrap();
106      assert_eq!(kv.value, "val");
107  }
108  
109  #[test]
110  fn test_continuation_with_inline_comment() {
111      // Spec 5.2 (Strict): Unquoted continuation CANNOT have inline comments
112      // because the space required for comment implies termination.
113      // "KEY=val \ # comment" -> "val"
114      let input = "KEY=val \\ # comment\n  ue";
115      let entries = parse(input);
116      let kv = entries[0].as_pair().unwrap();
117      assert_eq!(kv.value, "val");
118  }
119  
120  // ...
121  
122  #[test]
123  fn test_spec_5_2_continuation_before_comment() {
124      // Spec 5.2 (Strict): Backslash BEFORE comment requires space before #,
125      // which terminates value. So continuation is impossible here for unquoted.
126      // "KEY=val \ # comment" -> "val"
127      let input = "KEY=val \\ # comment\n  continued";
128      let entries = parse(input);
129      let kv = entries[0].as_pair().unwrap();
130      assert_eq!(kv.value, "val");
131  }
132  
133  #[test]
134  fn test_error_message_no_value_leak() {
135      // Spec 6.3: Error messages MUST NOT leak values.
136      // We create a case that causes error during value parsing?
137      // Or invalid key with sensitive data?
138      // "KEY with invalid char"
139      let input = "KEY-SENSITIVE=secret_value"; // Invalid key (dash)
140      let entries = parse(input);
141      match &entries[0] {
142          Entry::Error(e) => {
143              let msg = e.to_string();
144              // Error should define it's an invalid key, but NOT show full line content if possible?
145              // Actually spec says "Invalid character in key...".
146              // It shouldn't show "secret_value".
147              assert!(!msg.contains("secret_value"));
148          },
149          _ => panic!("Should be error"),
150      }
151  }
152  
153  #[test]
154  fn test_mixed_newline_normalization() {
155      // Spec 4.2.2: Parsers MAY normalize, but strictly preserving is fine.
156      // Check behavior consistency.
157      let input = "KEY=val\r\nNEXT=v\nLAST=x";
158      let entries = parse(input);
159      assert_eq!(entries.len(), 3);
160      assert_eq!(entries[0].as_pair().unwrap().value, "val");
161      assert_eq!(entries[1].as_pair().unwrap().value, "v");
162      assert_eq!(entries[2].as_pair().unwrap().value, "x");
163  }