use proptest::prelude::*;
use sdivi_patterns::normalize::{normalize_and_hash, NormalizeNode};
fn arb_node_kind() -> impl Strategy<Value = String> {
proptest::string::string_regex("[a-z_]{3,20}").unwrap()
}
fn arb_leaf() -> impl Strategy<Value = NormalizeNode> {
arb_node_kind().prop_map(|kind| NormalizeNode {
kind,
children: vec![],
})
}
fn arb_node(depth: usize) -> impl Strategy<Value = NormalizeNode> {
if depth == 0 {
arb_leaf().boxed()
} else {
(
arb_node_kind(),
proptest::collection::vec(arb_node(depth - 1), 0..4),
)
.prop_map(|(kind, children)| NormalizeNode { kind, children })
.boxed()
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(500))]
#[test]
fn prop_test_normalize_and_hash_stable(
node in arb_node(3),
) {
let h1 = normalize_and_hash(&node.kind, &node.children);
let h2 = normalize_and_hash(&node.kind, &node.children);
prop_assert_eq!(h1, h2, "same inputs must produce same blake3 digest");
}
#[test]
fn prop_result_is_64_char_hex(node in arb_node(2)) {
let h = normalize_and_hash(&node.kind, &node.children);
prop_assert_eq!(h.len(), 64, "digest must be 64 hex chars");
prop_assert!(h.chars().all(|c| c.is_ascii_hexdigit()), "must be hex");
}
#[test]
fn prop_different_kinds_differ(
k1 in proptest::string::string_regex("[a-z]{4,12}").unwrap(),
k2 in proptest::string::string_regex("[a-z]{4,12}").unwrap(),
) {
prop_assume!(k1 != k2);
let h1 = normalize_and_hash(&k1, &[]);
let h2 = normalize_and_hash(&k2, &[]);
prop_assert_ne!(h1, h2, "different kinds must produce different digests");
}
}