/ tests / general.rs
general.rs
   1  use korni::{parse, parse_with_options, Entry, ParseOptions, QuoteType};
   2  
   3  // Tests extracted from src/lib.rs
   4  
   5  #[test]
   6  fn test_basic_parse() {
   7      let input = "KEY=VALUE";
   8      let entries = parse(input);
   9  
  10      assert_eq!(entries.len(), 1);
  11      let kv = entries[0].as_pair().unwrap();
  12  
  13      assert_eq!(kv.key, "KEY");
  14      assert_eq!(kv.value, "VALUE");
  15      assert!(!kv.is_exported);
  16  }
  17  
  18  #[test]
  19  fn test_strict_whitespace() {
  20      let input = "KEY = VALUE"; // Invalid
  21      let entries = parse(input);
  22  
  23      assert_eq!(entries.len(), 1);
  24      match &entries[0] {
  25          Entry::Error(e) => assert!(e
  26              .to_string()
  27              .contains("Whitespace not allowed between key and equals")),
  28          _ => panic!("Should be error"),
  29      }
  30  }
  31  
  32  #[test]
  33  fn test_export_prefix() {
  34      let input = "export DB_URL=postgres";
  35      let entries = parse(input);
  36  
  37      assert_eq!(entries.len(), 1);
  38      let kv = entries[0].as_pair().unwrap();
  39      assert_eq!(kv.key, "DB_URL");
  40      assert_eq!(kv.value, "postgres");
  41      assert!(kv.is_exported);
  42  }
  43  
  44  #[test]
  45  fn test_export_no_space() {
  46      let input = "exportKey=val";
  47      let entries = parse(input);
  48      let kv = entries[0].as_pair().unwrap();
  49      assert_eq!(kv.key, "exportKey");
  50      assert!(!kv.is_exported);
  51  }
  52  
  53  #[test]
  54  fn test_tab_after_equals_error() {
  55      // Spec 4.1.2: Tab after = is also forbidden
  56      let input = "KEY=\tvalue";
  57      let entries = parse(input);
  58      match &entries[0] {
  59          Entry::Error(e) => assert!(e
  60              .to_string()
  61              .contains("Whitespace not allowed after equals")),
  62          _ => panic!("Should be error"),
  63      }
  64  }
  65  
  66  #[test]
  67  fn test_debug_simple() {
  68      // Test basic escaping logic
  69      // "a" -> a
  70      // "\n" -> newline
  71      // "a\"b" -> a"b
  72  
  73      let input = r#"KEY="a\"b""#;
  74      let entries = parse(input);
  75      let kv = entries[0].as_pair().unwrap();
  76      assert_eq!(kv.value, "a\"b"); // Escaped quote becomes literal quote
  77  
  78      // Literal backslash + char
  79      // "a\b" -> a\b (since \b is not special)
  80      let input2 = r#"KEY="a\b""#;
  81      let entries2 = parse(input2);
  82      let kv2 = entries2[0].as_pair().unwrap();
  83      assert_eq!(kv2.value, r#"a\b"#);
  84  }
  85  
  86  #[test]
  87  fn test_escaped_quotes_and_content() {
  88      // Unquoted value with quotes inside are literal
  89      // Remove space to comply with Spec 4.2.2 (unquoted ends at whitespace)
  90      let input = r#"JSON={"key":"val"}"#;
  91      let entries = parse(input);
  92      let kv = entries[0].as_pair().unwrap();
  93      assert_eq!(kv.value, r#"{"key":"val"}"#);
  94      assert_eq!(kv.quote, QuoteType::None);
  95  
  96      // Quoted value with valid escapes
  97      // Per Spec 4.2.4: \" → Literal Double Quote (")
  98      // So "{\"key\": \"val\"}" becomes {"key": "val"}
  99      let input2 = r#"JSON="{\"key\": \"val\"}""#;
 100      let entries2 = parse(input2);
 101      let kv2 = entries2[0].as_pair().unwrap();
 102      // Escaped quotes become literal quotes
 103      assert_eq!(kv2.value, r#"{"key": "val"}"#);
 104      assert_eq!(kv2.quote, QuoteType::Double);
 105  }
 106  
 107  #[test]
 108  fn test_backslash_continuation_strict() {
 109      // With strict shell rules:
 110      // "Hello \ # comment" -> "Hello" (Space terminates value)
 111      let input = "MESSAGE=Hello \\ # comment\nWorld";
 112      let entries = parse(input);
 113      let kv = entries[0].as_pair().unwrap();
 114      assert_eq!(kv.value, "Hello");
 115  
 116      // "Val\" (no space) -> Continuation works
 117      let input2 = "KEY=Val\\\nNext";
 118      let entries2 = parse(input2);
 119      let kv2 = entries2[0].as_pair().unwrap();
 120      assert_eq!(kv2.value, "ValNext");
 121  }
 122  
 123  #[test]
 124  fn test_multiline_creation_strict() {
 125      // "KEY=Line1 \" -> "Line1" (Space terminates)
 126      let input = "KEY=Line1 \\\n    Line2";
 127      let entries = parse(input);
 128      let kv = entries[0].as_pair().unwrap();
 129      assert_eq!(kv.value, "Line1");
 130  }
 131  
 132  // ======== Additional Spec Compliance Tests ========
 133  
 134  #[test]
 135  fn test_inline_comment_after_quoted_value() {
 136      // Spec 4.3.2: Inline comments allowed after closing quote
 137      let input = r#"KEY="value" # this is a comment"#;
 138      let entries = parse(input);
 139      let kv = entries[0].as_pair().unwrap();
 140      assert_eq!(kv.value, "value");
 141      assert_eq!(kv.quote, QuoteType::Double);
 142  }
 143  
 144  #[test]
 145  fn test_inline_comment_after_single_quoted_value() {
 146      // Spec 4.3.2: Inline comments allowed after closing single quote
 147      let input = "KEY='value' # this is a comment";
 148      let entries = parse(input);
 149      let kv = entries[0].as_pair().unwrap();
 150      assert_eq!(kv.value, "value");
 151      assert_eq!(kv.quote, QuoteType::Single);
 152  }
 153  
 154  #[test]
 155  fn test_no_space_before_hash_after_quote() {
 156      // Hash directly after quote without space - still ignored
 157      let input = r##"KEY="value"#comment"##;
 158      let entries = parse(input);
 159      let kv = entries[0].as_pair().unwrap();
 160      assert_eq!(kv.value, "value");
 161  }
 162  
 163  #[test]
 164  fn test_trailing_whitespace_after_quote() {
 165      // Whitespace after closing quote is ignored
 166      let input = "KEY=\"value\"   ";
 167      let entries = parse(input);
 168      let kv = entries[0].as_pair().unwrap();
 169      assert_eq!(kv.value, "value");
 170  }
 171  
 172  #[test]
 173  fn test_trailing_junk_after_quote() {
 174      // Any content after closing quote is ignored
 175      let input = "KEY=\"value\" extra junk";
 176      let entries = parse(input);
 177      let kv = entries[0].as_pair().unwrap();
 178      assert_eq!(kv.value, "value");
 179  }
 180  
 181  #[test]
 182  fn test_single_quoted_literal() {
 183      // Spec 4.2.3: Single quotes = literal, no escaping
 184      let input = "KEY='hello\\nworld'";
 185      let entries = parse(input);
 186      let kv = entries[0].as_pair().unwrap();
 187      // \n is NOT interpreted as newline in single quotes
 188      assert_eq!(kv.value, "hello\\nworld");
 189      assert_eq!(kv.quote, QuoteType::Single);
 190  }
 191  
 192  #[test]
 193  fn test_empty_values() {
 194      // Spec 4.2.1: Empty values
 195      let input = "EMPTY=";
 196      let entries = parse(input);
 197      let kv = entries[0].as_pair().unwrap();
 198      assert_eq!(kv.value, "");
 199  
 200      let input2 = r#"EMPTY2="""#;
 201      let entries2 = parse(input2);
 202      let kv2 = entries2[0].as_pair().unwrap();
 203      assert_eq!(kv2.value, "");
 204  
 205      let input3 = "EMPTY3=''";
 206      let entries3 = parse(input3);
 207      let kv3 = entries3[0].as_pair().unwrap();
 208      assert_eq!(kv3.value, "");
 209  }
 210  
 211  #[test]
 212  fn test_hash_in_unquoted_value() {
 213      // Spec 4.3.2: # preceded by space = comment
 214      let input = "URL=http://example.com#anchor";
 215      let entries = parse(input);
 216      let kv = entries[0].as_pair().unwrap();
 217      assert_eq!(kv.value, "http://example.com#anchor"); // No space before #
 218  
 219      let input2 = "COLOR=#333";
 220      let entries2 = parse(input2);
 221      let kv2 = entries2[0].as_pair().unwrap();
 222      assert_eq!(kv2.value, "#333");
 223  
 224      let input3 = "KEY=value #comment";
 225      let entries3 = parse(input3);
 226      let kv3 = entries3[0].as_pair().unwrap();
 227      assert_eq!(kv3.value, "value"); // Comment stripped
 228  }
 229  
 230  #[test]
 231  fn test_hash_in_quoted_value() {
 232      // Spec 4.3.2: # inside quotes is always literal
 233      let input = r#"KEY="value #not a comment""#;
 234      let entries = parse(input);
 235      let kv = entries[0].as_pair().unwrap();
 236      assert_eq!(kv.value, "value #not a comment");
 237  }
 238  
 239  #[test]
 240  fn test_multiple_equals() {
 241      // Spec 4.1.2: First = separates, rest are value
 242      let input = "KEY=value=with=equals";
 243      let entries = parse(input);
 244      let kv = entries[0].as_pair().unwrap();
 245      assert_eq!(kv.value, "value=with=equals");
 246  
 247      // Spec 4.1.2: Double equals immediately after key is invalid
 248      let input2 = "KEY==value";
 249      let entries2 = parse(input2);
 250      match &entries2[0] {
 251          Entry::Error(e) => assert!(e.to_string().contains("Double equals sign detected")),
 252          _ => panic!("Should be double equals error"),
 253      }
 254  }
 255  
 256  #[test]
 257  fn test_double_quote_escape_sequences() {
 258      // Spec 4.2.4: Escape sequences
 259      let input = r#"KEY="line1\nline2\ttab\\backslash""#;
 260      let entries = parse(input);
 261      let kv = entries[0].as_pair().unwrap();
 262      assert_eq!(kv.value, "line1\nline2\ttab\\backslash");
 263  }
 264  
 265  #[test]
 266  fn test_quoted_multiline() {
 267      // Spec 5.1: Quoted multiline preserves newlines
 268      let input = "KEY=\"line1\nline2\"";
 269      let entries = parse(input);
 270      let kv = entries[0].as_pair().unwrap();
 271      assert_eq!(kv.value, "line1\nline2");
 272  }
 273  
 274  #[test]
 275  fn test_unclosed_quote_error() {
 276      let input = r#"KEY="unclosed"#;
 277      let entries = parse(input);
 278      match &entries[0] {
 279          Entry::Error(e) => assert!(e.to_string().contains("Unclosed")),
 280          _ => panic!("Should be error"),
 281      }
 282  }
 283  
 284  #[test]
 285  fn test_utf8_bom_at_start() {
 286      // Spec 3.1: BOM at start is stripped
 287      let input = "\u{FEFF}KEY=value";
 288      let entries = parse(input);
 289      let kv = entries[0].as_pair().unwrap();
 290      assert_eq!(kv.key, "KEY");
 291      assert_eq!(kv.value, "value");
 292  }
 293  
 294  #[test]
 295  fn test_comment_lines() {
 296      let input = "# This is a comment\nKEY=value";
 297      let entries = parse_with_options(
 298          input,
 299          ParseOptions {
 300              include_comments: true,
 301              track_positions: false,
 302          },
 303      );
 304      assert_eq!(entries.len(), 2);
 305      assert!(matches!(entries[0], Entry::Comment(_)));
 306      assert!(entries[1].as_pair().is_some());
 307  }
 308  
 309  #[test]
 310  fn test_empty_lines() {
 311      let input = "\n\nKEY=value\n\n";
 312      let entries = parse(input);
 313      let pairs: Vec<_> = entries.iter().filter(|e| e.as_pair().is_some()).collect();
 314      assert_eq!(pairs.len(), 1);
 315  }
 316  
 317  // ======== Spec 4.1.1: Export Prefix Edge Cases ========
 318  
 319  #[test]
 320  fn test_export_with_tab() {
 321      // Spec 4.1.1: export followed by tab is valid
 322      let input = "export\tKEY=value";
 323      let entries = parse(input);
 324      let kv = entries[0].as_pair().unwrap();
 325      assert_eq!(kv.key, "KEY");
 326      assert!(kv.is_exported);
 327  }
 328  
 329  #[test]
 330  fn test_leading_whitespace_before_export() {
 331      // Spec 4.1.1: Leading whitespace before export is allowed
 332      let input = "  export KEY=value";
 333      let entries = parse(input);
 334      let kv = entries[0].as_pair().unwrap();
 335      assert_eq!(kv.key, "KEY");
 336      assert!(kv.is_exported);
 337  }
 338  
 339  #[test]
 340  fn test_export_underscore_key() {
 341      // Spec 4.1.1: export_KEY is a regular key (underscore, not space)
 342      let input = "export_KEY=value";
 343      let entries = parse(input);
 344      let kv = entries[0].as_pair().unwrap();
 345      assert_eq!(kv.key, "export_KEY");
 346      assert!(!kv.is_exported);
 347  }
 348  
 349  #[test]
 350  fn test_exported_value_key() {
 351      // exportedValue is a regular key
 352      let input = "exportedValue=value";
 353      let entries = parse(input);
 354      let kv = entries[0].as_pair().unwrap();
 355      assert_eq!(kv.key, "exportedValue");
 356      assert!(!kv.is_exported);
 357  }
 358  
 359  // ======== Spec 4.1.2: Key Edge Cases ========
 360  
 361  #[test]
 362  fn test_underscore_start_key() {
 363      // Spec 4.1.2: Keys can start with underscore
 364      let input = "_PRIVATE=secret";
 365      let entries = parse(input);
 366      let kv = entries[0].as_pair().unwrap();
 367      assert_eq!(kv.key, "_PRIVATE");
 368  }
 369  
 370  #[test]
 371  fn test_key_with_numbers() {
 372      let input = "CONFIG123=value";
 373      let entries = parse(input);
 374      let kv = entries[0].as_pair().unwrap();
 375      assert_eq!(kv.key, "CONFIG123");
 376  }
 377  
 378  #[test]
 379  fn test_key_starting_with_digit_error() {
 380      // Spec 4.1.2: Keys MUST NOT start with digit
 381      let input = "123KEY=value";
 382      let entries = parse(input);
 383      match &entries[0] {
 384          Entry::Error(e) => assert!(e.to_string().contains("digit")),
 385          _ => panic!("Should be error"),
 386      }
 387  }
 388  
 389  #[test]
 390  fn test_leading_whitespace_before_key() {
 391      // Spec 4.1.2: Leading whitespace is stripped
 392      let input = "  KEY=value";
 393      let entries = parse(input);
 394      let kv = entries[0].as_pair().unwrap();
 395      assert_eq!(kv.key, "KEY");
 396      assert_eq!(kv.value, "value");
 397  }
 398  
 399  #[test]
 400  fn test_tab_before_key() {
 401      let input = "\tKEY=value";
 402      let entries = parse(input);
 403      let kv = entries[0].as_pair().unwrap();
 404      assert_eq!(kv.key, "KEY");
 405  }
 406  
 407  #[test]
 408  fn test_lowercase_key() {
 409      // Spec 4.1.2: Lowercase keys are valid
 410      let input = "database_url=postgres://localhost";
 411      let entries = parse(input);
 412      let kv = entries[0].as_pair().unwrap();
 413      assert_eq!(kv.key, "database_url");
 414  }
 415  
 416  // ======== Spec 4.2.2: Unquoted Value Edge Cases ========
 417  
 418  #[test]
 419  fn test_unquoted_with_equals() {
 420      // DATABASE_URL with query params
 421      let input = "DATABASE_URL=postgresql://user:pass@host:5432/db?param=value";
 422      let entries = parse(input);
 423      let kv = entries[0].as_pair().unwrap();
 424      assert_eq!(kv.value, "postgresql://user:pass@host:5432/db?param=value");
 425  }
 426  
 427  #[test]
 428  fn test_unquoted_backslash_literal() {
 429      // Spec 4.2.2: Backslashes in unquoted are literal (except at EOL)
 430      let input = "PATH=C:\\Users\\Name";
 431      let entries = parse(input);
 432      let kv = entries[0].as_pair().unwrap();
 433      assert_eq!(kv.value, "C:\\Users\\Name");
 434  }
 435  
 436  #[test]
 437  fn test_unquoted_escape_n_literal() {
 438      // Spec 4.2.2: \n in unquoted is literal backslash + n
 439      let input = "KEY=foo\\nbar";
 440      let entries = parse(input);
 441      let kv = entries[0].as_pair().unwrap();
 442      assert_eq!(kv.value, "foo\\nbar");
 443  }
 444  
 445  // ======== Spec 4.2.3: Single Quote Edge Cases ========
 446  
 447  #[test]
 448  fn test_single_quote_dollar_literal() {
 449      // Spec 4.2.3: $ is literal in single quotes
 450      let input = "KEY='${VAR}'";
 451      let entries = parse(input);
 452      let kv = entries[0].as_pair().unwrap();
 453      assert_eq!(kv.value, "${VAR}");
 454  }
 455  
 456  #[test]
 457  fn test_single_quote_hash_literal() {
 458      // Spec 4.2.3: # is literal in single quotes
 459      let input = "KEY='#333'";
 460      let entries = parse(input);
 461      let kv = entries[0].as_pair().unwrap();
 462      assert_eq!(kv.value, "#333");
 463  }
 464  
 465  #[test]
 466  fn test_single_quote_multiline() {
 467      // Spec 4.2.3/5.1: Single quotes preserve newlines
 468      let input = "KEY='Line 1\nLine 2\nLine 3'";
 469      let entries = parse(input);
 470      let kv = entries[0].as_pair().unwrap();
 471      assert_eq!(kv.value, "Line 1\nLine 2\nLine 3");
 472  }
 473  
 474  #[test]
 475  fn test_single_quote_unclosed_error() {
 476      let input = "KEY='unclosed";
 477      let entries = parse(input);
 478      match &entries[0] {
 479          Entry::Error(e) => assert!(e.to_string().contains("Unclosed")),
 480          _ => panic!("Should be error"),
 481      }
 482  }
 483  
 484  // ======== Spec 4.2.4: Double Quote Edge Cases ========
 485  
 486  #[test]
 487  fn test_double_quote_escaped_dollar() {
 488      // Spec 4.2.4: \$ produces literal $
 489      let input = r#"KEY="\${NOT_VAR}""#;
 490      let entries = parse(input);
 491      let kv = entries[0].as_pair().unwrap();
 492      assert_eq!(kv.value, "${NOT_VAR}");
 493  }
 494  
 495  #[test]
 496  fn test_double_quote_carriage_return() {
 497      // Spec 4.2.4: \r produces carriage return
 498      let input = r#"KEY="line1\r\nline2""#;
 499      let entries = parse(input);
 500      let kv = entries[0].as_pair().unwrap();
 501      assert_eq!(kv.value, "line1\r\nline2");
 502  }
 503  
 504  #[test]
 505  fn test_double_quote_consecutive_backslashes() {
 506      // Spec 4.2.4: \\\\ produces \\
 507      let input = r#"KEY="value\\\\""#;
 508      let entries = parse(input);
 509      let kv = entries[0].as_pair().unwrap();
 510      assert_eq!(kv.value, "value\\\\");
 511  }
 512  
 513  #[test]
 514  fn test_double_quote_unknown_escape() {
 515      // Spec 4.2.4: Unknown escapes preserved literally
 516      let input = r#"KEY="\a\x\0""#;
 517      let entries = parse(input);
 518      let kv = entries[0].as_pair().unwrap();
 519      assert_eq!(kv.value, "\\a\\x\\0");
 520  }
 521  
 522  #[test]
 523  fn test_nested_quotes() {
 524      // Spec 4.2.4: Opposite quote type is literal
 525      let input = r#"KEY="outer 'inner' end""#;
 526      let entries = parse(input);
 527      let kv = entries[0].as_pair().unwrap();
 528      assert_eq!(kv.value, "outer 'inner' end");
 529  
 530      let input2 = "KEY='outer \"inner\" end'";
 531      let entries2 = parse(input2);
 532      let kv2 = entries2[0].as_pair().unwrap();
 533      assert_eq!(kv2.value, "outer \"inner\" end");
 534  }
 535  
 536  #[test]
 537  fn test_space_after_equals_error() {
 538      // Spec 4.1.2: Space after = is forbidden
 539      let input = r#"KEY= "value""#;
 540      let entries = parse(input);
 541      match &entries[0] {
 542          Entry::Error(e) => assert!(e
 543              .to_string()
 544              .contains("Whitespace not allowed after equals")),
 545          _ => panic!("Should be error"),
 546      }
 547  }
 548  
 549  #[test]
 550  fn test_quotes_within_unquoted() {
 551      // Spec 4.2.4: Quotes in unquoted values are literal
 552      let input = r#"KEY=foo"bar"baz"#;
 553      let entries = parse(input);
 554      let kv = entries[0].as_pair().unwrap();
 555      assert_eq!(kv.value, r#"foo"bar"baz"#);
 556  }
 557  
 558  // ======== Spec 4.3: Comment Edge Cases ========
 559  
 560  #[test]
 561  fn test_tab_before_hash_comment() {
 562      // Spec 4.3.2: Tab before # also triggers comment
 563      let input = "KEY=value\t#comment";
 564      let entries = parse(input);
 565      let kv = entries[0].as_pair().unwrap();
 566      assert_eq!(kv.value, "value");
 567  }
 568  
 569  #[test]
 570  fn test_multiple_spaces_before_hash() {
 571      let input = "KEY=value  #comment";
 572      let entries = parse(input);
 573      let kv = entries[0].as_pair().unwrap();
 574      assert_eq!(kv.value, "value");
 575  }
 576  
 577  #[test]
 578  fn test_hash_literal_start_of_value() {
 579      // Spec 4.2.2: # at start of value (no space before) is literal
 580      let input = "KEY=#not a comment";
 581      let entries = parse(input);
 582      // Strict Mode: Terminates at first space.
 583      // So "KEY=#not" -> "#not"
 584      // " a comment" is ignored.
 585      let kv = entries[0].as_pair().unwrap();
 586      assert_eq!(kv.value, "#not");
 587  }
 588  
 589  #[test]
 590  fn test_api_key_with_hash() {
 591      // Spec 4.3.2: # not preceded by space is literal
 592      let input = "API_KEY=sk-abc123#prod";
 593      let entries = parse(input);
 594      let kv = entries[0].as_pair().unwrap();
 595      assert_eq!(kv.value, "sk-abc123#prod");
 596  }
 597  
 598  // ======== Spec 4.4: Interpolation (Literal) ========
 599  
 600  #[test]
 601  fn test_dollar_var_literal_unquoted() {
 602      // Spec 4.4: Interpolation is NOT performed
 603      let input = "DATABASE_URL=${DB_HOST}:${DB_PORT}/${DB_NAME}";
 604      let entries = parse(input);
 605      let kv = entries[0].as_pair().unwrap();
 606      assert_eq!(kv.value, "${DB_HOST}:${DB_PORT}/${DB_NAME}");
 607  }
 608  
 609  #[test]
 610  fn test_dollar_at_end() {
 611      let input = "KEY=value$";
 612      let entries = parse(input);
 613      let kv = entries[0].as_pair().unwrap();
 614      assert_eq!(kv.value, "value$");
 615  }
 616  
 617  #[test]
 618  fn test_dollar_before_numbers() {
 619      let input = "KEY=$123";
 620      let entries = parse(input);
 621      let kv = entries[0].as_pair().unwrap();
 622      assert_eq!(kv.value, "$123");
 623  }
 624  
 625  // ======== Spec 5.2: Backslash Continuation Edge Cases ========
 626  
 627  #[test]
 628  fn test_multiple_continuations() {
 629      let input = "PATH=/usr/local/bin:\\\n/usr/bin:\\\n/bin";
 630      let entries = parse(input);
 631      let kv = entries[0].as_pair().unwrap();
 632      assert_eq!(kv.value, "/usr/local/bin:/usr/bin:/bin");
 633  }
 634  
 635  #[test]
 636  fn test_continuation_preserves_leading_space() {
 637      // Spec 5.2 (Revised): Backslash continuation without quotes requires CONTIGUOUS backslash.
 638      // "COMMAND=docker run \" -> "docker"
 639      // To get "docker run...", one must use quotes: "COMMAND='docker run...'"
 640      let input = "COMMAND=docker run \\\n  --detach \\\n  nginx";
 641      let entries = parse(input);
 642      let kv = entries[0].as_pair().unwrap();
 643      assert_eq!(kv.value, "docker");
 644  }
 645  
 646  // ======== Spec Edge Cases: Mixed Scenarios ========
 647  
 648  #[test]
 649  fn test_empty_file() {
 650      let input = "";
 651      let entries = parse(input);
 652      assert_eq!(entries.len(), 0);
 653  }
 654  
 655  #[test]
 656  fn test_only_comments() {
 657      let input = "# Comment 1\n# Comment 2";
 658      let entries = parse(input);
 659      let pairs: Vec<_> = entries.iter().filter(|e| e.as_pair().is_some()).collect();
 660      assert_eq!(pairs.len(), 0);
 661  }
 662  
 663  #[test]
 664  fn test_only_whitespace() {
 665      let input = "   \n\t\n   ";
 666      let entries = parse(input);
 667      let pairs: Vec<_> = entries.iter().filter(|e| e.as_pair().is_some()).collect();
 668      assert_eq!(pairs.len(), 0);
 669  }
 670  
 671  #[test]
 672  fn test_indented_comment() {
 673      let input = "   # Indented comment\nKEY=value";
 674      let entries = parse_with_options(
 675          input,
 676          ParseOptions {
 677              include_comments: true,
 678              track_positions: false,
 679          },
 680      );
 681      assert!(matches!(entries[0], Entry::Comment(_)));
 682      let kv = entries[1].as_pair().unwrap();
 683      assert_eq!(kv.key, "KEY");
 684  }
 685  
 686  #[test]
 687  fn test_case_sensitive_keys() {
 688      // Spec 4.1.2: Keys are case-sensitive
 689      let input = "API_KEY=value1\napi_key=value2";
 690      let entries = parse(input);
 691      // Filter to only pairs
 692      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
 693      assert_eq!(pairs.len(), 2, "Expected 2 key-value pairs");
 694      assert_eq!(pairs[0].key, "API_KEY");
 695      assert_eq!(pairs[1].key, "api_key");
 696      // They are distinct keys
 697      assert_ne!(pairs[0].key, pairs[1].key);
 698  }
 699  
 700  #[test]
 701  fn test_pem_key_multiline() {
 702      // Real-world scenario: PEM key in double quotes
 703      let input = r#"PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
 704  MIIEpQIBAAKCAQEA3Tz2MR7SZiAMfQyuvBjM9Oi..
 705  -----END RSA PRIVATE KEY-----""#;
 706      let entries = parse(input);
 707      let kv = entries[0].as_pair().unwrap();
 708      assert!(kv.value.starts_with("-----BEGIN RSA PRIVATE KEY-----"));
 709      assert!(kv.value.ends_with("-----END RSA PRIVATE KEY-----"));
 710      assert!(kv.value.contains('\n'));
 711  }
 712  
 713  #[test]
 714  fn test_json_in_double_quotes() {
 715      let input = r#"CONFIG="{\"enable\": true, \"retries\": 3}""#;
 716      let entries = parse(input);
 717      let kv = entries[0].as_pair().unwrap();
 718      assert_eq!(kv.value, r#"{"enable": true, "retries": 3}"#);
 719  }
 720  
 721  // ======== ADDITIONAL TESTS FOR 150+ COVERAGE ========
 722  
 723  // --- Escape Sequences Individual Tests ---
 724  
 725  #[test]
 726  fn test_escape_n_only() {
 727      let input = r#"KEY="\n""#;
 728      let entries = parse(input);
 729      let kv = entries[0].as_pair().unwrap();
 730      assert_eq!(kv.value, "\n");
 731  }
 732  
 733  #[test]
 734  fn test_escape_r_only() {
 735      let input = r#"KEY="\r""#;
 736      let entries = parse(input);
 737      let kv = entries[0].as_pair().unwrap();
 738      assert_eq!(kv.value, "\r");
 739  }
 740  
 741  #[test]
 742  fn test_escape_t_only() {
 743      let input = r#"KEY="\t""#;
 744      let entries = parse(input);
 745      let kv = entries[0].as_pair().unwrap();
 746      assert_eq!(kv.value, "\t");
 747  }
 748  
 749  #[test]
 750  fn test_escape_backslash_only() {
 751      let input = r#"KEY="\\""#;
 752      let entries = parse(input);
 753      let kv = entries[0].as_pair().unwrap();
 754      assert_eq!(kv.value, "\\");
 755  }
 756  
 757  #[test]
 758  fn test_escape_quote_only() {
 759      let input = r#"KEY="\"""#;
 760      let entries = parse(input);
 761      let kv = entries[0].as_pair().unwrap();
 762      assert_eq!(kv.value, "\"");
 763  }
 764  
 765  #[test]
 766  fn test_escape_dollar_only() {
 767      let input = r#"KEY="\$""#;
 768      let entries = parse(input);
 769      let kv = entries[0].as_pair().unwrap();
 770      assert_eq!(kv.value, "$");
 771  }
 772  
 773  #[test]
 774  fn test_escape_unknown_a() {
 775      let input = r#"KEY="\a""#;
 776      let entries = parse(input);
 777      let kv = entries[0].as_pair().unwrap();
 778      assert_eq!(kv.value, "\\a");
 779  }
 780  
 781  #[test]
 782  fn test_escape_unknown_b() {
 783      let input = r#"KEY="\b""#;
 784      let entries = parse(input);
 785      let kv = entries[0].as_pair().unwrap();
 786      assert_eq!(kv.value, "\\b");
 787  }
 788  
 789  #[test]
 790  fn test_escape_unknown_f() {
 791      let input = r#"KEY="\f""#;
 792      let entries = parse(input);
 793      let kv = entries[0].as_pair().unwrap();
 794      assert_eq!(kv.value, "\\f");
 795  }
 796  
 797  #[test]
 798  fn test_escape_unknown_v() {
 799      let input = r#"KEY="\v""#;
 800      let entries = parse(input);
 801      let kv = entries[0].as_pair().unwrap();
 802      assert_eq!(kv.value, "\\v");
 803  }
 804  
 805  #[test]
 806  fn test_escape_unknown_zero() {
 807      let input = r#"KEY="\0""#;
 808      let entries = parse(input);
 809      let kv = entries[0].as_pair().unwrap();
 810      assert_eq!(kv.value, "\\0");
 811  }
 812  
 813  #[test]
 814  fn test_escape_unknown_x() {
 815      let input = r#"KEY="\x41""#;
 816      let entries = parse(input);
 817      let kv = entries[0].as_pair().unwrap();
 818      assert_eq!(kv.value, "\\x41");
 819  }
 820  
 821  #[test]
 822  fn test_escape_mixed_sequence() {
 823      let input = r#"KEY="a\nb\tc\\d\"e\$f""#;
 824      let entries = parse(input);
 825      let kv = entries[0].as_pair().unwrap();
 826      assert_eq!(kv.value, "a\nb\tc\\d\"e$f");
 827  }
 828  
 829  // ======== New Comprehensive Spec Coverage Tests ========
 830  
 831  #[test]
 832  fn test_bom_middle_invalid() {
 833      // Spec 3.1: BOM in middle is invalid
 834      let input = "KEY=val\u{FEFF}ue";
 835      let entries = parse(input);
 836      match &entries[0] {
 837          Entry::Error(e) => assert!(e.to_string().contains("BOM found at invalid position")),
 838          _ => panic!("Should be BOM error"),
 839      }
 840  }
 841  
 842  #[test]
 843  fn test_empty_key_invalid() {
 844      // Spec 4.1.2: Empty keys forbidden
 845      let input = "=value";
 846      let entries = parse(input);
 847      match &entries[0] {
 848          Entry::Error(e) => assert!(e.to_string().contains("Empty key")),
 849          _ => panic!("Should be empty key error"),
 850      }
 851  }
 852  
 853  #[test]
 854  fn test_whitespace_key_invalid() {
 855      // Spec 4.1.2: Whitespace only keys forbidden
 856      let input = "   =value";
 857      let entries = parse(input);
 858      match &entries[0] {
 859          Entry::Error(e) => assert!(e.to_string().contains("Empty key")),
 860          _ => panic!("Should be empty key error"),
 861      }
 862  }
 863  
 864  #[test]
 865  fn test_export_no_definition_invalid() {
 866      // Spec 4.1.1: Export on its own line is invalid
 867      let input = "export";
 868      let entries = parse(input);
 869      // Depending on impl, this might be ignore or error. Spec says "SHOULD reject or ignore".
 870      // Current impl doesn't error but produces nothing? or error?
 871      // Let's check behavior. If it produces nothing, that's fine (ignored).
 872      // If it errors, that's also fine.
 873      if !entries.is_empty() {
 874          match &entries[0] {
 875              Entry::Error(_) => {} // OK
 876              _ => {}               // If it parses as key "export" with empty value?
 877                                     // "Keys MUST start with letter/underscore". "export" is valid key if not followed by space?
 878                                     // But line is just "export".
 879                                     // If it's treated as key "export" with no equals? Error "Expected ="
 880          }
 881      }
 882  }
 883  
 884  #[test]
 885  fn test_unquoted_termination() {
 886      // Spec 4.2.2: Ends at first whitespace
 887      let input = "KEY=Value With Spaces";
 888      let entries = parse(input);
 889      let kv = entries[0].as_pair().unwrap();
 890      assert_eq!(kv.value, "Value");
 891      // "With Spaces" is ignored/unparsed noise or error?
 892      // Spec says: "Internal whitespace terminates the value... remainder becomes comment if #..."
 893      // Actually, just terminates value.
 894      // What happens to the rest? "Unexpected characters"?
 895      // Current parser seems to just stop value parsing and ignore rest of line?
 896      // Let's verify "Value" is extracted.
 897  }
 898  
 899  // --- Key Name Variations ---
 900  
 901  #[test]
 902  fn test_key_single_char() {
 903      let input = "A=value";
 904      let entries = parse(input);
 905      let kv = entries[0].as_pair().unwrap();
 906      assert_eq!(kv.key, "A");
 907  }
 908  
 909  #[test]
 910  fn test_key_all_underscores() {
 911      let input = "___=value";
 912      let entries = parse(input);
 913      let kv = entries[0].as_pair().unwrap();
 914      assert_eq!(kv.key, "___");
 915  }
 916  
 917  #[test]
 918  fn test_key_very_long() {
 919      let key = "A".repeat(1000);
 920      let input = format!("{}=value", key);
 921      let entries = parse(&input);
 922      let kv = entries[0].as_pair().unwrap();
 923      assert_eq!(kv.key.len(), 1000);
 924  }
 925  
 926  #[test]
 927  fn test_key_mixed_case() {
 928      let input = "MyVeryLongVariableName_123=value";
 929      let entries = parse(input);
 930      let kv = entries[0].as_pair().unwrap();
 931      assert_eq!(kv.key, "MyVeryLongVariableName_123");
 932  }
 933  
 934  #[test]
 935  fn test_key_numbers_middle() {
 936      let input = "ABC123DEF=value";
 937      let entries = parse(input);
 938      let kv = entries[0].as_pair().unwrap();
 939      assert_eq!(kv.key, "ABC123DEF");
 940  }
 941  
 942  #[test]
 943  fn test_key_underscore_only_start() {
 944      let input = "_123=value";
 945      let entries = parse(input);
 946      let kv = entries[0].as_pair().unwrap();
 947      assert_eq!(kv.key, "_123");
 948  }
 949  
 950  // --- Value Variations ---
 951  
 952  #[test]
 953  fn test_value_single_char() {
 954      let input = "KEY=a";
 955      let entries = parse(input);
 956      let kv = entries[0].as_pair().unwrap();
 957      assert_eq!(kv.value, "a");
 958  }
 959  
 960  #[test]
 961  fn test_value_very_long() {
 962      let val = "x".repeat(10000);
 963      let input = format!("KEY={}", val);
 964      let entries = parse(&input);
 965      let kv = entries[0].as_pair().unwrap();
 966      assert_eq!(kv.value.len(), 10000);
 967  }
 968  
 969  #[test]
 970  fn test_value_number_only() {
 971      let input = "KEY=12345";
 972      let entries = parse(input);
 973      let kv = entries[0].as_pair().unwrap();
 974      assert_eq!(kv.value, "12345");
 975  }
 976  
 977  #[test]
 978  fn test_value_decimal() {
 979      let input = "KEY=3.14159";
 980      let entries = parse(input);
 981      let kv = entries[0].as_pair().unwrap();
 982      assert_eq!(kv.value, "3.14159");
 983  }
 984  
 985  #[test]
 986  fn test_value_negative_number() {
 987      let input = "KEY=-42";
 988      let entries = parse(input);
 989      let kv = entries[0].as_pair().unwrap();
 990      assert_eq!(kv.value, "-42");
 991  }
 992  
 993  #[test]
 994  fn test_value_boolean_true() {
 995      let input = "KEY=true";
 996      let entries = parse(input);
 997      let kv = entries[0].as_pair().unwrap();
 998      assert_eq!(kv.value, "true");
 999  }
1000  
1001  #[test]
1002  fn test_value_boolean_false() {
1003      let input = "KEY=false";
1004      let entries = parse(input);
1005      let kv = entries[0].as_pair().unwrap();
1006      assert_eq!(kv.value, "false");
1007  }
1008  
1009  // --- Whitespace Edge Cases ---
1010  
1011  #[test]
1012  fn test_multiple_leading_spaces() {
1013      let input = "    KEY=value";
1014      let entries = parse(input);
1015      let kv = entries[0].as_pair().unwrap();
1016      assert_eq!(kv.key, "KEY");
1017  }
1018  
1019  #[test]
1020  fn test_multiple_leading_tabs() {
1021      let input = "\t\t\tKEY=value";
1022      let entries = parse(input);
1023      let kv = entries[0].as_pair().unwrap();
1024      assert_eq!(kv.key, "KEY");
1025  }
1026  
1027  #[test]
1028  fn test_mixed_leading_whitespace() {
1029      let input = " \t \tKEY=value";
1030      let entries = parse(input);
1031      let kv = entries[0].as_pair().unwrap();
1032      assert_eq!(kv.key, "KEY");
1033  }
1034  
1035  #[test]
1036  fn test_trailing_whitespace_after_value() {
1037      let input = "KEY=value   ";
1038      let entries = parse(input);
1039      let kv = entries[0].as_pair().unwrap();
1040      assert_eq!(kv.value, "value");
1041  }
1042  
1043  #[test]
1044  fn test_export_multiple_spaces() {
1045      let input = "export   KEY=value";
1046      let entries = parse(input);
1047      let kv = entries[0].as_pair().unwrap();
1048      assert_eq!(kv.key, "KEY");
1049      assert!(kv.is_exported);
1050  }
1051  
1052  // --- Quote Edge Cases ---
1053  
1054  #[test]
1055  fn test_empty_double_quotes() {
1056      let input = r#"KEY="""#;
1057      let entries = parse(input);
1058      let kv = entries[0].as_pair().unwrap();
1059      assert_eq!(kv.value, "");
1060      assert_eq!(kv.quote, QuoteType::Double);
1061  }
1062  
1063  #[test]
1064  fn test_empty_single_quotes() {
1065      let input = "KEY=''";
1066      let entries = parse(input);
1067      let kv = entries[0].as_pair().unwrap();
1068      assert_eq!(kv.value, "");
1069      assert_eq!(kv.quote, QuoteType::Single);
1070  }
1071  
1072  #[test]
1073  fn test_single_space_in_quotes() {
1074      let input = r#"KEY=" ""#;
1075      let entries = parse(input);
1076      let kv = entries[0].as_pair().unwrap();
1077      assert_eq!(kv.value, " ");
1078  }
1079  
1080  #[test]
1081  fn test_multiple_spaces_in_quotes() {
1082      let input = r#"KEY="   ""#;
1083      let entries = parse(input);
1084      let kv = entries[0].as_pair().unwrap();
1085      assert_eq!(kv.value, "   ");
1086  }
1087  
1088  #[test]
1089  fn test_tab_in_quotes() {
1090      let input = "KEY=\"\t\"";
1091      let entries = parse(input);
1092      let kv = entries[0].as_pair().unwrap();
1093      assert_eq!(kv.value, "\t");
1094  }
1095  
1096  #[test]
1097  fn test_newline_in_double_quotes() {
1098      let input = "KEY=\"line1\nline2\"";
1099      let entries = parse(input);
1100      let kv = entries[0].as_pair().unwrap();
1101      assert_eq!(kv.value, "line1\nline2");
1102  }
1103  
1104  #[test]
1105  fn test_newline_in_single_quotes() {
1106      let input = "KEY='line1\nline2'";
1107      let entries = parse(input);
1108      let kv = entries[0].as_pair().unwrap();
1109      assert_eq!(kv.value, "line1\nline2");
1110  }
1111  
1112  #[test]
1113  fn test_double_quote_in_single_quotes() {
1114      let input = r#"KEY='"hello"'"#;
1115      let entries = parse(input);
1116      let kv = entries[0].as_pair().unwrap();
1117      assert_eq!(kv.value, "\"hello\"");
1118  }
1119  
1120  // --- Comment Edge Cases ---
1121  
1122  #[test]
1123  fn test_comment_only_hash() {
1124      let input = "#";
1125      let entries = parse_with_options(
1126          input,
1127          ParseOptions {
1128              include_comments: true,
1129              track_positions: false,
1130          },
1131      );
1132      assert!(matches!(entries[0], Entry::Comment(_)));
1133  }
1134  
1135  #[test]
1136  fn test_comment_hash_with_spaces() {
1137      let input = "#     ";
1138      let entries = parse_with_options(
1139          input,
1140          ParseOptions {
1141              include_comments: true,
1142              track_positions: false,
1143          },
1144      );
1145      assert!(matches!(entries[0], Entry::Comment(_)));
1146  }
1147  
1148  #[test]
1149  fn test_many_hash_marks() {
1150      let input = "### Comment ###";
1151      let entries = parse_with_options(
1152          input,
1153          ParseOptions {
1154              include_comments: true,
1155              track_positions: false,
1156          },
1157      );
1158      assert!(matches!(entries[0], Entry::Comment(_)));
1159  }
1160  
1161  #[test]
1162  fn test_inline_comment_multiple_hashes() {
1163      let input = "KEY=value ### comment";
1164      let entries = parse(input);
1165      let kv = entries[0].as_pair().unwrap();
1166      assert_eq!(kv.value, "value");
1167  }
1168  
1169  #[test]
1170  fn test_hash_directly_after_equals() {
1171      let input = "K=#not a comment";
1172      let entries = parse(input);
1173      let kv = entries[0].as_pair().unwrap();
1174      assert_eq!(kv.key, "K");
1175      assert_eq!(kv.value, "#not");
1176  }
1177  
1178  // --- Multiple Entries ---
1179  
1180  #[test]
1181  fn test_three_entries() {
1182      let input = "A=1\nB=2\nC=3";
1183      let entries = parse(input);
1184      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1185      assert_eq!(pairs.len(), 3);
1186      assert_eq!(pairs[0].value, "1");
1187      assert_eq!(pairs[1].value, "2");
1188      assert_eq!(pairs[2].value, "3");
1189  }
1190  
1191  #[test]
1192  fn test_ten_entries() {
1193      let input = (0..10)
1194          .map(|i| format!("KEY{}={}", i, i))
1195          .collect::<Vec<_>>()
1196          .join("\n");
1197      let entries = parse(&input);
1198      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1199      assert_eq!(pairs.len(), 10);
1200  }
1201  
1202  #[test]
1203  fn test_entries_with_empty_lines() {
1204      let input = "A=1\n\nB=2\n\n\nC=3";
1205      let entries = parse(input);
1206      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1207      assert_eq!(pairs.len(), 3);
1208  }
1209  
1210  #[test]
1211  fn test_entries_with_comments() {
1212      let input = "# Header\nA=1\n# Middle\nB=2\n# Footer";
1213      let entries = parse(input);
1214      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1215      assert_eq!(pairs.len(), 2);
1216  }
1217  
1218  #[test]
1219  fn test_mixed_quote_types() {
1220      let input = "A='single'\nB=\"double\"\nC=unquoted";
1221      let entries = parse(input);
1222      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1223      assert_eq!(pairs[0].quote, QuoteType::Single);
1224      assert_eq!(pairs[1].quote, QuoteType::Double);
1225      assert_eq!(pairs[2].quote, QuoteType::None);
1226  }
1227  
1228  // --- Error Cases ---
1229  
1230  #[test]
1231  fn test_error_whitespace_before_equals() {
1232      let input = "KEY =value";
1233      let entries = parse(input);
1234      assert!(matches!(entries[0], Entry::Error(_)));
1235  }
1236  
1237  #[test]
1238  fn test_error_whitespace_after_equals() {
1239      // Spec 4.1.2: Whitespace after equals is forbidden
1240      let input = "KEY= value";
1241      let entries = parse(input);
1242      match &entries[0] {
1243          Entry::Error(e) => assert!(e
1244              .to_string()
1245              .contains("Whitespace not allowed after equals")),
1246          _ => panic!("Should be error"),
1247      }
1248  }
1249  
1250  #[test]
1251  fn test_error_digit_start_2() {
1252      let input = "2KEY=value";
1253      let entries = parse(input);
1254      assert!(matches!(entries[0], Entry::Error(_)));
1255  }
1256  
1257  #[test]
1258  fn test_error_digit_start_9() {
1259      let input = "9_VAR=value";
1260      let entries = parse(input);
1261      assert!(matches!(entries[0], Entry::Error(_)));
1262  }
1263  
1264  #[test]
1265  fn test_unclosed_single_quote() {
1266      let input = "KEY='unclosed";
1267      let entries = parse(input);
1268      assert!(matches!(entries[0], Entry::Error(_)));
1269  }
1270  
1271  #[test]
1272  fn test_unclosed_double_quote() {
1273      let input = r#"KEY="unclosed"#;
1274      let entries = parse(input);
1275      assert!(matches!(entries[0], Entry::Error(_)));
1276  }
1277  
1278  // --- Real World Scenarios ---
1279  
1280  #[test]
1281  fn test_database_url_postgres() {
1282      let input = "DATABASE_URL=postgresql://user:password@localhost:5432/mydb";
1283      let entries = parse(input);
1284      let kv = entries[0].as_pair().unwrap();
1285      assert_eq!(kv.value, "postgresql://user:password@localhost:5432/mydb");
1286  }
1287  
1288  #[test]
1289  fn test_database_url_mysql() {
1290      let input = "DATABASE_URL=mysql://root:pass@127.0.0.1:3306/app";
1291      let entries = parse(input);
1292      let kv = entries[0].as_pair().unwrap();
1293      assert_eq!(kv.value, "mysql://root:pass@127.0.0.1:3306/app");
1294  }
1295  
1296  #[test]
1297  fn test_redis_url() {
1298      let input = "REDIS_URL=redis://localhost:6379/0";
1299      let entries = parse(input);
1300      let kv = entries[0].as_pair().unwrap();
1301      assert_eq!(kv.value, "redis://localhost:6379/0");
1302  }
1303  
1304  #[test]
1305  fn test_aws_access_key() {
1306      let input = "AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE";
1307      let entries = parse(input);
1308      let kv = entries[0].as_pair().unwrap();
1309      assert_eq!(kv.value, "AKIAIOSFODNN7EXAMPLE");
1310  }
1311  
1312  #[test]
1313  fn test_aws_secret_key() {
1314      let input = "AWS_SECRET_ACCESS_KEY='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'";
1315      let entries = parse(input);
1316      let kv = entries[0].as_pair().unwrap();
1317      assert_eq!(kv.value, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
1318  }
1319  
1320  #[test]
1321  fn test_jwt_secret() {
1322      let input = r#"JWT_SECRET="super-secret-key-123!@#""#;
1323      let entries = parse(input);
1324      let kv = entries[0].as_pair().unwrap();
1325      assert_eq!(kv.value, "super-secret-key-123!@#");
1326  }
1327  
1328  #[test]
1329  fn test_api_endpoint() {
1330      let input = "API_ENDPOINT=https://api.example.com/v1";
1331      let entries = parse(input);
1332      let kv = entries[0].as_pair().unwrap();
1333      assert_eq!(kv.value, "https://api.example.com/v1");
1334  }
1335  
1336  #[test]
1337  fn test_s3_bucket() {
1338      let input = "S3_BUCKET=my-bucket-name-2024";
1339      let entries = parse(input);
1340      let kv = entries[0].as_pair().unwrap();
1341      assert_eq!(kv.value, "my-bucket-name-2024");
1342  }
1343  
1344  #[test]
1345  fn test_log_level() {
1346      let input = "LOG_LEVEL=debug";
1347      let entries = parse(input);
1348      let kv = entries[0].as_pair().unwrap();
1349      assert_eq!(kv.value, "debug");
1350  }
1351  
1352  #[test]
1353  fn test_port_number() {
1354      let input = "PORT=3000";
1355      let entries = parse(input);
1356      let kv = entries[0].as_pair().unwrap();
1357      assert_eq!(kv.value, "3000");
1358  }
1359  
1360  #[test]
1361  fn test_node_env() {
1362      let input = "NODE_ENV=production";
1363      let entries = parse(input);
1364      let kv = entries[0].as_pair().unwrap();
1365      assert_eq!(kv.value, "production");
1366  }
1367  
1368  #[test]
1369  fn test_smtp_config() {
1370      let input = "SMTP_HOST=smtp.gmail.com\nSMTP_PORT=587\nSMTP_USER=user@gmail.com";
1371      let entries = parse(input);
1372      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1373      assert_eq!(pairs.len(), 3);
1374      assert_eq!(pairs[0].value, "smtp.gmail.com");
1375      assert_eq!(pairs[1].value, "587");
1376      assert_eq!(pairs[2].value, "user@gmail.com");
1377  }
1378  
1379  #[test]
1380  fn test_oauth_config() {
1381      let input = r#"GOOGLE_CLIENT_ID="123456789.apps.googleusercontent.com"
1382  GOOGLE_CLIENT_SECRET="secret-abc123""#;
1383      let entries = parse(input);
1384      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1385      assert_eq!(pairs.len(), 2);
1386  }
1387  
1388  // --- Unicode ---
1389  
1390  #[test]
1391  fn test_unicode_value() {
1392      let input = "GREETING=こんにちは";
1393      let entries = parse(input);
1394      let kv = entries[0].as_pair().unwrap();
1395      assert_eq!(kv.value, "こんにちは");
1396  }
1397  
1398  #[test]
1399  fn test_unicode_in_quotes() {
1400      // Note: Parser uses byte-based iteration, which correctly handles UTF-8
1401      // but the value is preserved as-is
1402      let input = r#"MESSAGE="Hello, World!""#;
1403      let entries = parse(input);
1404      let kv = entries[0].as_pair().unwrap();
1405      assert_eq!(kv.value, "Hello, World!");
1406  }
1407  
1408  #[test]
1409  fn test_emoji_value() {
1410      let input = "EMOJI=🚀🎉✨";
1411      let entries = parse(input);
1412      let kv = entries[0].as_pair().unwrap();
1413      assert_eq!(kv.value, "🚀🎉✨");
1414  }
1415  
1416  // --- Continuation Edge Cases ---
1417  
1418  #[test]
1419  fn test_continuation_empty_next_line() {
1420      let input = "KEY=value\\\n";
1421      let entries = parse(input);
1422      let kv = entries[0].as_pair().unwrap();
1423      assert_eq!(kv.value, "value");
1424  }
1425  
1426  #[test]
1427  fn test_continuation_three_lines() {
1428      let input = "KEY=a\\\nb\\\nc";
1429      let entries = parse(input);
1430      let kv = entries[0].as_pair().unwrap();
1431      assert_eq!(kv.value, "abc");
1432  }
1433  
1434  #[test]
1435  fn test_continuation_strict_no_spaces() {
1436      // Strict continuation: No spaces allowed before backslash
1437      let input = "KEY=eins\\\nzwei\\\ndrei";
1438      let entries = parse(input);
1439      let kv = entries[0].as_pair().unwrap();
1440      assert_eq!(kv.value, "einszweidrei");
1441  }
1442  
1443  // --- Span/Position Tests (require track_positions option) ---
1444  
1445  #[test]
1446  fn test_key_span_basic() {
1447      let input = "KEY=value";
1448      let entries = parse_with_options(
1449          input,
1450          ParseOptions {
1451              track_positions: true,
1452              include_comments: false,
1453          },
1454      );
1455      let kv = entries[0].as_pair().unwrap();
1456      assert!(kv.key_span.is_some());
1457      let span = kv.key_span.unwrap();
1458      assert_eq!(span.start.offset, 0);
1459      assert_eq!(span.end.offset, 3);
1460  }
1461  
1462  #[test]
1463  fn test_value_span_basic() {
1464      let input = "KEY=value";
1465      let entries = parse_with_options(
1466          input,
1467          ParseOptions {
1468              track_positions: true,
1469              include_comments: false,
1470          },
1471      );
1472      let kv = entries[0].as_pair().unwrap();
1473      // Value starts at offset 4 (after =)
1474      assert!(kv.value_span.is_some());
1475      assert_eq!(kv.value_span.unwrap().start.offset, 4);
1476  }
1477  
1478  // TODO: Line/column tracking not yet implemented - only offsets are tracked
1479  // #[test]
1480  // fn test_equals_position() {
1481  //     let input = "KEY=value";
1482  //     let entries = parse(input);
1483  //     let kv = entries[0].as_pair().unwrap();
1484  //     // = is at offset 3
1485  //     assert_eq!(kv.equals_pos.offset, 3);
1486  //     assert_eq!(kv.equals_pos.line, 0);
1487  //     assert_eq!(kv.equals_pos.col, 3);
1488  // }
1489  
1490  #[test]
1491  fn test_double_quote_positions() {
1492      let input = r#"KEY="value""#;
1493      let entries = parse_with_options(
1494          input,
1495          ParseOptions {
1496              track_positions: true,
1497              include_comments: false,
1498          },
1499      );
1500      let kv = entries[0].as_pair().unwrap();
1501      // Opening " is at offset 4
1502      assert!(kv.open_quote_pos.is_some());
1503      assert_eq!(kv.open_quote_pos.unwrap().offset, 4);
1504      // Closing " is at offset 10
1505      assert!(kv.close_quote_pos.is_some());
1506      assert_eq!(kv.close_quote_pos.unwrap().offset, 10);
1507  }
1508  
1509  #[test]
1510  fn test_single_quote_positions() {
1511      let input = "KEY='value'";
1512      let entries = parse_with_options(
1513          input,
1514          ParseOptions {
1515              track_positions: true,
1516              include_comments: false,
1517          },
1518      );
1519      let kv = entries[0].as_pair().unwrap();
1520      // Opening ' is at offset 4
1521      assert!(kv.open_quote_pos.is_some());
1522      assert_eq!(kv.open_quote_pos.unwrap().offset, 4);
1523      // Closing ' is at offset 10
1524      assert!(kv.close_quote_pos.is_some());
1525      assert_eq!(kv.close_quote_pos.unwrap().offset, 10);
1526  }
1527  
1528  #[test]
1529  fn test_unquoted_no_quote_positions() {
1530      let input = "KEY=value";
1531      let entries = parse(input);
1532      let kv = entries[0].as_pair().unwrap();
1533      // Unquoted values have no quote positions
1534      assert!(kv.open_quote_pos.is_none());
1535      assert!(kv.close_quote_pos.is_none());
1536  }
1537  
1538  #[test]
1539  fn test_position_with_export() {
1540      let input = "export KEY=value";
1541      let entries = parse_with_options(
1542          input,
1543          ParseOptions {
1544              track_positions: true,
1545              include_comments: false,
1546          },
1547      );
1548      let kv = entries[0].as_pair().unwrap();
1549      // Key starts after "export "
1550      assert!(kv.key_span.is_some());
1551      assert_eq!(kv.key_span.unwrap().start.offset, 7);
1552      // = is at offset 10
1553      assert!(kv.equals_pos.is_some());
1554      assert_eq!(kv.equals_pos.unwrap().offset, 10);
1555  }
1556  
1557  // TODO: Line/column tracking not yet implemented - only offsets are tracked
1558  // #[test]
1559  // fn test_position_on_second_line() {
1560  //     let input = "A=1\nB=2";
1561  //     let entries = parse(input);
1562  //     let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1563  //     // Second entry starts on line 1
1564  //     assert_eq!(pairs[1].key_span.start.line, 1);
1565  //     assert_eq!(pairs[1].key_span.start.col, 0);
1566  // }
1567  
1568  // --- Boundary Conditions ---
1569  
1570  #[test]
1571  fn test_equals_only() {
1572      let input = "=value";
1573      let entries = parse(input);
1574      // Empty key error
1575      assert!(matches!(entries[0], Entry::Error(_)));
1576  }
1577  
1578  #[test]
1579  fn test_key_only_no_equals() {
1580      let input = "KEY";
1581      let entries = parse(input);
1582      // Should be error - no equals
1583      assert!(matches!(entries[0], Entry::Error(_)));
1584  }
1585  
1586  #[test]
1587  fn test_newline_only() {
1588      let input = "\n";
1589      let entries = parse(input);
1590      // Should be empty - newlines are skipped in fast mode
1591      assert_eq!(entries.len(), 0);
1592  }
1593  
1594  #[test]
1595  fn test_multiple_newlines_only() {
1596      let input = "\n\n\n";
1597      let entries = parse(input);
1598      // Should be empty - newlines are skipped in fast mode
1599      assert_eq!(entries.len(), 0);
1600  }
1601  
1602  // --- Special Characters in Values ---
1603  
1604  #[test]
1605  fn test_value_with_at_sign() {
1606      let input = "EMAIL=user@example.com";
1607      let entries = parse(input);
1608      let kv = entries[0].as_pair().unwrap();
1609      assert_eq!(kv.value, "user@example.com");
1610  }
1611  
1612  #[test]
1613  fn test_value_with_ampersand() {
1614      let input = "QUERY=a=1&b=2";
1615      let entries = parse(input);
1616      let kv = entries[0].as_pair().unwrap();
1617      assert_eq!(kv.value, "a=1&b=2");
1618  }
1619  
1620  #[test]
1621  fn test_value_with_percent() {
1622      let input = "ENCODED=hello%20world";
1623      let entries = parse(input);
1624      let kv = entries[0].as_pair().unwrap();
1625      assert_eq!(kv.value, "hello%20world");
1626  }
1627  
1628  #[test]
1629  fn test_value_with_colon() {
1630      let input = "TIME=12:30:45";
1631      let entries = parse(input);
1632      let kv = entries[0].as_pair().unwrap();
1633      assert_eq!(kv.value, "12:30:45");
1634  }
1635  
1636  #[test]
1637  fn test_value_with_slash() {
1638      let input = "PATH=/usr/local/bin";
1639      let entries = parse(input);
1640      let kv = entries[0].as_pair().unwrap();
1641      assert_eq!(kv.value, "/usr/local/bin");
1642  }
1643  
1644  #[test]
1645  fn test_value_with_asterisk() {
1646      let input = "PATTERN=*.txt";
1647      let entries = parse(input);
1648      let kv = entries[0].as_pair().unwrap();
1649      assert_eq!(kv.value, "*.txt");
1650  }
1651  
1652  #[test]
1653  fn test_value_with_question_mark() {
1654      let input = "URL=http://example.com?query=1";
1655      let entries = parse(input);
1656      let kv = entries[0].as_pair().unwrap();
1657      assert_eq!(kv.value, "http://example.com?query=1");
1658  }
1659  
1660  #[test]
1661  fn test_value_with_brackets() {
1662      let input = "ARRAY=[1,2,3]";
1663      let entries = parse(input);
1664      let kv = entries[0].as_pair().unwrap();
1665      assert_eq!(kv.value, "[1,2,3]");
1666  }
1667  
1668  #[test]
1669  fn test_value_with_braces() {
1670      let input = "OBJ={key:value}";
1671      let entries = parse(input);
1672      let kv = entries[0].as_pair().unwrap();
1673      assert_eq!(kv.value, "{key:value}");
1674  }
1675  
1676  #[test]
1677  fn test_value_with_parens() {
1678      let input = "EXPR=(1+2)";
1679      let entries = parse(input);
1680      let kv = entries[0].as_pair().unwrap();
1681      assert_eq!(kv.value, "(1+2)");
1682  }
1683  
1684  #[test]
1685  fn test_value_with_pipe() {
1686      let input = "CMD=cat|grep";
1687      let entries = parse(input);
1688      let kv = entries[0].as_pair().unwrap();
1689      assert_eq!(kv.value, "cat|grep");
1690  }
1691  
1692  #[test]
1693  fn test_value_with_caret() {
1694      let input = "REGEX=^start";
1695      let entries = parse(input);
1696      let kv = entries[0].as_pair().unwrap();
1697      assert_eq!(kv.value, "^start");
1698  }
1699  
1700  #[test]
1701  fn test_value_with_tilde() {
1702      let input = "HOME=~/docs";
1703      let entries = parse(input);
1704      let kv = entries[0].as_pair().unwrap();
1705      assert_eq!(kv.value, "~/docs");
1706  }
1707  
1708  #[test]
1709  fn test_value_with_backtick() {
1710      let input = "K=`echo_hi`";
1711      let entries = parse(input);
1712      let kv = entries[0].as_pair().unwrap();
1713      assert_eq!(kv.key, "K");
1714      assert_eq!(kv.value, "`echo_hi`");
1715  }
1716  
1717  // --- Commented Key-Value Pairs (require include_comments option) ---
1718  
1719  #[test]
1720  fn test_commented_key_value() {
1721      let input = "# KEY=value";
1722      let entries = parse_with_options(
1723          input,
1724          ParseOptions {
1725              include_comments: true,
1726              track_positions: false,
1727          },
1728      );
1729      assert_eq!(entries.len(), 2); // Comment + Pair
1730      assert!(matches!(entries[0], Entry::Comment(_)));
1731      let kv = entries[1].as_pair().unwrap(); // Changed from entries[0] to entries[1]
1732      assert_eq!(kv.key, "KEY");
1733      assert_eq!(kv.value, "value");
1734      assert!(kv.is_comment);
1735      assert!(!kv.is_exported);
1736  }
1737  
1738  #[test]
1739  fn test_commented_key_value_with_spaces() {
1740      let input = "#   KEY=value";
1741      let entries = parse_with_options(
1742          input,
1743          ParseOptions {
1744              include_comments: true,
1745              track_positions: false,
1746          },
1747      );
1748      assert_eq!(entries.len(), 2);
1749      assert!(matches!(entries[0], Entry::Comment(_)));
1750      let kv = entries[1].as_pair().unwrap(); // Changed from entries[0] to entries[1]
1751      assert_eq!(kv.key, "KEY");
1752      assert!(kv.is_comment);
1753  }
1754  
1755  #[test]
1756  fn test_commented_exported_key_value() {
1757      let input = "# export KEY=value";
1758      let entries = parse_with_options(
1759          input,
1760          ParseOptions {
1761              include_comments: true,
1762              track_positions: false,
1763          },
1764      );
1765      assert_eq!(entries.len(), 2);
1766      assert!(matches!(entries[0], Entry::Comment(_)));
1767      let kv = entries[1].as_pair().unwrap(); // Changed from entries[0] to entries[1]
1768      assert_eq!(kv.key, "KEY");
1769      assert!(kv.is_comment);
1770      assert!(kv.is_exported);
1771  }
1772  
1773  #[test]
1774  fn test_commented_double_quoted_value() {
1775      let input = "# KEY=\"quoted value\"";
1776      let entries = parse_with_options(
1777          input,
1778          ParseOptions {
1779              include_comments: true,
1780              track_positions: false,
1781          },
1782      );
1783      assert_eq!(entries.len(), 2);
1784      assert!(matches!(entries[0], Entry::Comment(_)));
1785      let kv = entries[1].as_pair().unwrap(); // Changed from entries[0] to entries[1]
1786      assert_eq!(kv.value, "quoted value");
1787      assert!(kv.is_comment);
1788      assert_eq!(kv.quote, QuoteType::Double);
1789  }
1790  
1791  #[test]
1792  fn test_commented_single_quoted_value() {
1793      let input = "# KEY='single quoted'";
1794      let entries = parse_with_options(
1795          input,
1796          ParseOptions {
1797              include_comments: true,
1798              track_positions: false,
1799          },
1800      );
1801      assert_eq!(entries.len(), 2);
1802      assert!(matches!(entries[0], Entry::Comment(_)));
1803      let kv = entries[1].as_pair().unwrap(); // Changed from entries[0] to entries[1]
1804      assert_eq!(kv.value, "single quoted");
1805      assert!(kv.is_comment);
1806      assert_eq!(kv.quote, QuoteType::Single);
1807  }
1808  
1809  #[test]
1810  fn test_regular_pair_is_not_comment() {
1811      let input = "KEY=value";
1812      let entries = parse(input);
1813      let kv = entries[0].as_pair().unwrap();
1814      assert!(!kv.is_comment);
1815  }
1816  
1817  #[test]
1818  fn test_pure_comment_text() {
1819      // Pure text comment (no KEY=value pattern)
1820      let input = "# This is just a comment";
1821      let entries = parse_with_options(
1822          input,
1823          ParseOptions {
1824              include_comments: true,
1825              track_positions: false,
1826          },
1827      );
1828      // Should be Entry::Comment, not a Pair
1829      assert!(matches!(entries[0], Entry::Comment(_)));
1830  }
1831  
1832  #[test]
1833  fn test_mixed_commented_and_active() {
1834      let input = "# OLD_KEY=deprecated\nKEY=active";
1835      let entries = parse_with_options(
1836          input,
1837          ParseOptions {
1838              include_comments: true,
1839              track_positions: false,
1840          },
1841      );
1842      // Now returns: Comment, Pair(OLD_KEY), Pair(KEY)
1843      assert_eq!(entries.len(), 3); // Changed from expecting pairs.len() == 2
1844      assert!(matches!(entries[0], Entry::Comment(_)));
1845  
1846      let pairs: Vec<_> = entries.iter().filter_map(|e| e.as_pair()).collect();
1847      assert_eq!(pairs.len(), 2);
1848      assert!(pairs[0].is_comment);
1849      assert_eq!(pairs[0].key, "OLD_KEY");
1850      assert!(!pairs[1].is_comment);
1851      assert_eq!(pairs[1].key, "KEY");
1852  }
1853  
1854  // ======== New Tests based on Implementation Plan ========
1855  
1856  #[test]
1857  fn test_crlf_unquoted() {
1858      let input = "KEY=Value\r\n";
1859      let entries = parse(input);
1860      let kv = entries[0].as_pair().unwrap();
1861      // Unquoted value trims trailing whitespace, but expects newline termination.
1862      // \r\n is a valid newline.
1863      // "The value ends at the first whitespace character (space or tab) or newline character."
1864      assert_eq!(kv.value, "Value");
1865  }
1866  
1867  #[test]
1868  fn test_crlf_multiline_escaped() {
1869      let input = "KEY=Line1\\\r\nLine2";
1870      let entries = parse(input);
1871      let kv = entries[0].as_pair().unwrap();
1872      // Backslash continuation should work with CRLF
1873      assert_eq!(kv.value, "Line1Line2");
1874  }
1875  
1876  #[test]
1877  fn test_commented_escaped_quote() {
1878      let input = r#"# KEY="a\"b""#;
1879      let entries = parse_with_options(
1880          input,
1881          ParseOptions {
1882              include_comments: true,
1883              track_positions: false,
1884          },
1885      );
1886      assert_eq!(entries.len(), 2);
1887      assert!(matches!(entries[0], Entry::Comment(_)));
1888      let kv = entries[1].as_pair().unwrap(); // Changed from entries[0] to entries[1]
1889      assert_eq!(kv.value, r#"a"b"#);
1890      assert!(kv.is_comment);
1891  }
1892  
1893  #[test]
1894  fn test_bom_middle_rejection() {
1895      // Spec: BOM in middle must be rejected.
1896      let input = "KEY=Val\u{FEFF}ue";
1897      let entries = parse(input);
1898      match &entries[0] {
1899          Entry::Error(e) => assert!(e.to_string().contains("BOM")),
1900          _ => panic!("Should be error, got {:?}", entries[0]),
1901      }
1902  }
1903  
1904  #[test]
1905  fn test_key_with_dot_error() {
1906      let input = "KEY.NAME=val";
1907      let entries = parse(input);
1908      // "KEY" parsed as key
1909      // ".NAME" remains. "." is not '='. Error expected.
1910      match &entries[0] {
1911          Entry::Error(e) => assert!(e.to_string().contains("Expected '='")),
1912          _ => panic!("Should be error, caught: {:?}", entries[0]),
1913      }
1914  }
1915  
1916  #[test]
1917  fn test_key_with_dash_error() {
1918      let input = "KEY-NAME=val";
1919      let entries = parse(input);
1920      match &entries[0] {
1921          Entry::Error(e) => assert!(e.to_string().contains("Expected '='")),
1922          _ => panic!("Should be error"),
1923      }
1924  }
1925  
1926  #[test]
1927  fn test_key_with_space_error() {
1928      let input = "KEY NAME=val";
1929      let entries = parse(input);
1930      // "KEY" consumed. " NAME=val" left.
1931      // Spaces skipped. "NAME=val" left.
1932      // "N" != "=". Error "Expected '='".
1933      // Then recovery skips line? Or consumes rest?
1934      // recover_line skips to newline.
1935      // So we expect 1 error entry.
1936      match &entries[0] {
1937          Entry::Error(e) => assert!(e.to_string().contains("Expected '='")),
1938          _ => panic!("Should be error"),
1939      }
1940  }
1941  
1942  #[test]
1943  fn test_export_standalone() {
1944      let input = "export\n";
1945      let entries = parse(input);
1946      // "export" treated as key (no following space)
1947      // No equals sign.
1948      match &entries[0] {
1949          Entry::Error(e) => assert!(e.to_string().contains("Expected '='")),
1950          _ => panic!("Should be error"),
1951      }
1952  }
1953  
1954  #[test]
1955  fn test_export_no_definition() {
1956      let input = "export   \n";
1957      let entries = parse(input);
1958      // "export " matched.
1959      // Key is empty (newline immediately).
1960      // If key is empty, and next char is newline (not =), we might not get error?
1961      // Let's check logic:
1962      // key_start == key_end.
1963      // if !eof && peek() == '=' { Error }
1964      // recover_line()
1965      // So no error, just skipped line.
1966      assert_eq!(entries.len(), 0);
1967  }
1968  
1969  #[test]
1970  fn test_single_quote_backslash_quote() {
1971      let input = r#"KEY='a\'b'"#;
1972      let entries = parse(input);
1973      let kv = entries[0].as_pair().unwrap();
1974      // Single quotes: \ is literal.
1975      // Parses 'a\' then closing quote '
1976      // Value is a\
1977      assert_eq!(kv.value, r#"a\"#);
1978  }
1979  
1980  #[test]
1981  fn test_double_quote_unicode_escape_literal() {
1982      let input = r#"KEY="\u1234""#;
1983      let entries = parse(input);
1984      let kv = entries[0].as_pair().unwrap();
1985      // \u is not a special escape, so preserved literal
1986      assert_eq!(kv.value, r#"\u1234"#);
1987  }
1988  
1989  #[test]
1990  fn test_nested_escapes_deep() {
1991      let input = r#"KEY="\\\"""#;
1992      let entries = parse(input);
1993      let kv = entries[0].as_pair().unwrap();
1994      // \\ -> \
1995      // \" -> "
1996      assert_eq!(kv.value, r#"\""#);
1997  }