use batpak::prelude::*;
#[cfg(feature = "blake3")]
use proptest::prelude::*;
#[test]
fn genesis_has_zero_prev_hash() {
let chain = HashChain::default();
assert_eq!(
chain.prev_hash, [0u8; 32],
"GENESIS INVARIANT VIOLATED: default HashChain must have prev_hash = [0u8; 32]. \
Investigate: src/event/hash.rs Default impl."
);
}
#[test]
fn genesis_has_zero_event_hash() {
let chain = HashChain::default();
assert_eq!(
chain.event_hash, [0u8; 32],
"GENESIS INVARIANT VIOLATED: default HashChain must have event_hash = [0u8; 32]. \
Investigate: src/event/hash.rs Default impl."
);
}
#[cfg(not(feature = "blake3"))]
mod no_blake3_tests {
use super::*;
#[test]
fn no_blake3_hash_chain_is_zero() {
let chain = HashChain::default();
assert_eq!(
chain.event_hash, [0u8; 32],
"Without blake3 feature, hash chains should use zero hashes. \
Investigate: src/store/writer.rs STEP 5 #[cfg(not(feature = \"blake3\"))]."
);
assert_eq!(chain.prev_hash, [0u8; 32],
"NO-BLAKE3 FALLBACK: without blake3 feature, prev_hash must be [0u8; 32].\n\
Investigate: src/store/writer.rs STEP 5 #[cfg(not(feature = \"blake3\"))].\n\
Common causes: fallback path not zeroing prev_hash, blake3 feature accidentally enabled.\n\
Run: cargo test --test hash_chain no_blake3_hash_chain_is_zero");
}
}
#[cfg(feature = "blake3")]
mod blake3_tests {
use super::*;
use batpak::event::hash::{compute_hash, verify_chain};
#[test]
fn compute_hash_deterministic() {
let data = b"hello world";
let h1 = compute_hash(data);
let h2 = compute_hash(data);
assert_eq!(
h1, h2,
"HASH DETERMINISM VIOLATED: same input must produce same hash."
);
}
#[test]
fn compute_hash_different_inputs() {
let h1 = compute_hash(b"hello");
let h2 = compute_hash(b"world");
assert_ne!(
h1, h2,
"HASH COLLISION: different inputs must produce different hashes \
(with overwhelming probability)."
);
}
#[test]
fn compute_hash_empty_input() {
let h = compute_hash(b"");
assert_ne!(
h, [0u8; 32],
"Empty input should still produce a non-zero hash."
);
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(
std::env::var("PROPTEST_CASES")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(256)
))]
#[test]
fn verify_chain_accepts_valid(content in proptest::collection::vec(any::<u8>(), 0..100)) {
let prev_hash = [0u8; 32]; let event_hash = compute_hash(&content);
let chain = HashChain { prev_hash, event_hash };
prop_assert!(
verify_chain(&content, &chain, &prev_hash),
"CHAIN VERIFICATION FAILED: valid chain should verify. \
Investigate: src/event/hash.rs verify_chain."
);
}
#[test]
fn verify_chain_rejects_tampered_content(
content in proptest::collection::vec(any::<u8>(), 1..100),
) {
let prev_hash = [0u8; 32];
let event_hash = compute_hash(&content);
let chain = HashChain { prev_hash, event_hash };
let mut tampered = content.clone();
tampered[0] = tampered[0].wrapping_add(1);
prop_assert!(
!verify_chain(&tampered, &chain, &prev_hash),
"TAMPER DETECTION FAILED: tampered content should not verify. \
Investigate: src/event/hash.rs verify_chain."
);
}
#[test]
fn verify_chain_rejects_wrong_prev_hash(
content in proptest::collection::vec(any::<u8>(), 0..100),
) {
let prev_hash = [0u8; 32];
let event_hash = compute_hash(&content);
let chain = HashChain { prev_hash, event_hash };
let wrong_prev = [1u8; 32];
prop_assert!(
!verify_chain(&content, &chain, &wrong_prev),
"PREV_HASH CHECK FAILED: wrong prev_hash should not verify. \
Investigate: src/event/hash.rs verify_chain."
);
}
}
}