use hedl::{canonicalize, lint, parse, to_json, validate};
use proptest::prelude::*;
fn arb_simple_hedl() -> impl Strategy<Value = String> {
prop_oneof![
Just("%VERSION: 1.0\n---\nname: Alice\nage: 30\n".to_string()),
Just("%VERSION: 1.0\n---\nuser:\n name: Bob\n email: bob@example.com\n".to_string()),
Just("%VERSION: 1.0\n%STRUCT: User: [id, name]\n---\nusers:@User\n | alice, Alice\n | bob, Bob\n".to_string()),
Just("%VERSION: 1.0\n---\ncount: 42\nactive: true\nratio: 3.14\n".to_string()),
Just("%VERSION: 1.0\n---\n".to_string()),
]
}
proptest! {
#[test]
fn prop_parse_success(hedl in arb_simple_hedl()) {
let result = parse(&hedl);
prop_assert!(result.is_ok(), "Failed to parse valid HEDL: {:?}", result.err());
}
#[test]
fn prop_roundtrip_canonical(hedl in arb_simple_hedl()) {
let doc1 = parse(&hedl).unwrap();
let canonical = canonicalize(&doc1).unwrap();
let doc2 = parse(&canonical).unwrap();
prop_assert_eq!(doc1.version, doc2.version);
prop_assert_eq!(doc1.root.len(), doc2.root.len());
}
#[test]
fn prop_roundtrip_json(hedl in arb_simple_hedl()) {
let doc1 = parse(&hedl).unwrap();
let json_str = to_json(&doc1).unwrap();
prop_assert!(!json_str.is_empty());
let json_value: serde_json::Value = serde_json::from_str(&json_str).unwrap();
prop_assert!(json_value.is_object());
}
#[test]
fn prop_lint_always_succeeds(hedl in arb_simple_hedl()) {
let doc = parse(&hedl).unwrap();
let diagnostics = lint(&doc);
let _ = diagnostics.len();
}
#[test]
fn prop_validate_no_panic(hedl in arb_simple_hedl()) {
let _ = validate(&hedl);
}
#[test]
fn prop_canonical_is_valid(hedl in arb_simple_hedl()) {
let doc = parse(&hedl).unwrap();
let canonical = canonicalize(&doc).unwrap();
let reparsed = parse(&canonical);
prop_assert!(reparsed.is_ok(), "Canonical output failed to parse: {:?}", reparsed.err());
}
#[test]
fn prop_canonical_idempotent(hedl in arb_simple_hedl()) {
let doc = parse(&hedl).unwrap();
let canonical1 = canonicalize(&doc).unwrap();
let doc2 = parse(&canonical1).unwrap();
let canonical2 = canonicalize(&doc2).unwrap();
prop_assert_eq!(canonical1, canonical2, "Canonicalization not idempotent");
}
#[test]
fn prop_json_is_valid(hedl in arb_simple_hedl()) {
let doc = parse(&hedl).unwrap();
let json_str = to_json(&doc).unwrap();
let parse_result: Result<serde_json::Value, _> = serde_json::from_str(&json_str);
prop_assert!(parse_result.is_ok(), "Invalid JSON produced: {:?}", parse_result.err());
}
}
#[cfg(test)]
mod edge_cases {
use super::*;
#[test]
fn test_empty_document() {
let hedl = "%VERSION: 1.0\n---\n";
let doc = parse(hedl).unwrap();
assert_eq!(doc.root.len(), 0);
let canonical = canonicalize(&doc).unwrap();
assert!(canonical.contains("%VERSION: 1.0"));
}
#[test]
fn test_single_scalar() {
let hedl = "%VERSION: 1.0\n---\nname: Alice\n";
let doc = parse(hedl).unwrap();
assert_eq!(doc.root.len(), 1);
let json = to_json(&doc).unwrap();
assert!(json.contains("Alice"));
}
#[test]
fn test_canonical_deterministic() {
let hedl = "%VERSION: 1.0\n---\nname: Bob\nage: 30\n";
let doc = parse(hedl).unwrap();
let canon1 = canonicalize(&doc).unwrap();
let canon2 = canonicalize(&doc).unwrap();
assert_eq!(canon1, canon2);
}
}