use tomlini::{ValidationErrorKind, ValidationMode};
#[test]
fn test_duplicate_key_rejected() {
let mut doc = tomlini::parse("[a]\nb=1\n[a]\nc=2\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(!errors.is_empty(), "expected duplicate key error");
assert!(
errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::DuplicateKey)),
"expected DuplicateKey error, got: {:?}",
errors.iter().map(|e| e.kind).collect::<Vec<_>>(),
);
}
#[test]
fn test_duplicate_key_within_table() {
let mut doc = tomlini::parse("[table]\nkey = 1\nkey = 2\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::DuplicateKey)),
"expected DuplicateKey error"
);
}
#[test]
fn test_no_duplicate_distinct_tables() {
let mut doc = tomlini::parse("[a]\nkey = 1\n[b]\nkey = 2\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
!errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::DuplicateKey)),
"should not report duplicate for same key in different tables"
);
}
#[test]
fn test_table_conflict() {
let mut doc = tomlini::parse("a = 1\n[a.b]\nc = 2\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::TableConflict)),
"expected TableConflict error, got: {:?}",
errors.iter().map(|e| e.kind).collect::<Vec<_>>(),
);
}
#[test]
fn test_no_table_conflict_valid_nesting() {
let mut doc = tomlini::parse("[a]\nb = 1\n[a.c]\nd = 2\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
!errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::TableConflict)),
"should not report conflict for valid table nesting"
);
}
#[test]
fn test_table_conflict_dotted_key() {
let mut doc = tomlini::parse("a.b = 1\n[a]\nc = 2\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::TableConflict)),
"expected TableConflict error for dotted key + table conflict"
);
}
#[test]
fn test_aot_ordering_non_consecutive() {
let mut doc = tomlini::parse("[[a]]\nx = 1\n[[b]]\ny = 2\n[[a]]\nz = 3\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::AotOrdering)),
"expected AotOrdering error"
);
}
#[test]
fn test_aot_ordering_consecutive_ok() {
let mut doc = tomlini::parse("[[a]]\nx = 1\n[[a]]\ny = 2\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
!errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::AotOrdering)),
"consecutive AOT entries should be valid"
);
}
#[test]
fn test_ini_comment_accepted() {
let doc = tomlini::parse("; this is a comment\nkey = 1\n").unwrap();
let comment_spans: Vec<_> = doc
.spans
.iter()
.filter(|s| s.kind == tomlini::SpanKind::Comment)
.collect();
assert!(!comment_spans.is_empty(), "expected a comment span for `;`");
assert!(doc.spans.iter().any(|s| {
s.kind == tomlini::SpanKind::BareKey
&& &doc.source[s.start as usize..s.end as usize] == "key"
}));
}
#[test]
fn test_semicolon_comment_mid_line() {
let result = tomlini::parse("key = 1 ; trailing\n");
let _ = result;
}
#[test]
fn test_hash_comment_still_works() {
let doc = tomlini::parse("# this is a comment\nkey = 1\n").unwrap();
let comment_spans: Vec<_> = doc
.spans
.iter()
.filter(|s| s.kind == tomlini::SpanKind::Comment)
.collect();
assert!(!comment_spans.is_empty(), "expected a comment span for `#`");
}
#[test]
fn test_strict_rejects_control_chars() {
let input = "key = 1\n\x0C\n";
let mut doc = tomlini::parse(input).unwrap();
let errors = doc.validate(ValidationMode::Strict);
let has_control = errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::ControlCharacter));
assert!(
has_control,
"expected ControlCharacter error for form feed (0x0C)"
);
}
#[test]
fn test_strict_bare_key_validation() {
let mut doc = tomlini::parse("key! = 1\n").unwrap();
let errors = doc.validate(ValidationMode::Strict);
assert!(
errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::InvalidKey)),
"expected InvalidKey error for bare key with invalid char `!`, got: {:?}",
errors.iter().map(|e| (e.kind, &e.msg)).collect::<Vec<_>>(),
);
}
#[test]
fn test_strict_valid_bare_key() {
let mut doc = tomlini::parse("my_key = 1\n").unwrap();
let errors = doc.validate(ValidationMode::Strict);
assert!(
!errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::InvalidKey)),
"valid bare key should not produce InvalidKey error"
);
}
#[test]
fn test_lenient_accepts_everything() {
let mut doc = tomlini::parse("[ok]\nkey=1\n[other]\nval=2\n").unwrap();
let errors = doc.validate(ValidationMode::Lenient);
assert!(
errors.is_empty(),
"lenient mode should produce no errors for valid doc"
);
}
#[test]
fn test_relaxed_accepts_loose_keys() {
let mut doc = tomlini::parse("[ok]\nkey = 1\n").unwrap();
let errors = doc.validate(ValidationMode::Relaxed);
assert!(
!errors
.iter()
.any(|e| matches!(e.kind, ValidationErrorKind::InvalidKey)),
"relaxed mode should not check key syntax"
);
}