use multi_codec::Codec;
use multi_hash::{Builder, HASH_CODECS, Multihash};
use multi_trait::TryDecodeFrom;
use multi_util::CodecInfo;
use proptest::prelude::*;
const fn xof_output_len(codec: Codec) -> Option<usize> {
match codec {
Codec::Blake3 | Codec::Shake128 => Some(32),
Codec::Shake256 => Some(64),
_ => None,
}
}
fn hash_bytes(codec: Codec, data: &[u8]) -> Multihash {
let mut builder = Builder::new(codec).unwrap();
builder.update(data);
if let Some(output_len) = xof_output_len(codec) {
builder.output_len(output_len);
}
builder.try_build().unwrap()
}
#[test]
fn test_multihash_roundtrip() {
proptest!(|(data in prop::collection::vec(any::<u8>(), 0..1024))| {
for &codec in &HASH_CODECS {
let mh1 = hash_bytes(codec, &data);
let bytes: Vec<u8> = mh1.clone().into();
let (mh2, remaining) = Multihash::try_decode_from(&bytes).unwrap();
prop_assert_eq!(mh1, mh2);
prop_assert!(remaining.is_empty());
}
});
}
#[test]
fn test_hash_deterministic() {
proptest!(|(data in prop::collection::vec(any::<u8>(), 0..256))| {
for &codec in HASH_CODECS.iter().take(5) {
let mh1 = hash_bytes(codec, &data);
let mh2 = hash_bytes(codec, &data);
prop_assert_eq!(&mh1, &mh2);
let bytes1: Vec<u8> = mh1.into();
let bytes2: Vec<u8> = mh2.into();
prop_assert_eq!(&bytes1, &bytes2);
}
});
}
#[test]
fn test_different_data_different_hash() {
proptest!(|(data1 in prop::collection::vec(any::<u8>(), 1..256),
data2 in prop::collection::vec(any::<u8>(), 1..256))| {
if data1 != data2 {
let mh1 = hash_bytes(Codec::Sha2256, &data1);
let mh2 = hash_bytes(Codec::Sha2256, &data2);
prop_assert_ne!(mh1, mh2);
}
});
}
#[test]
fn test_codec_preserved() {
proptest!(|(data in prop::collection::vec(any::<u8>(), 0..256))| {
for &codec in &HASH_CODECS {
let mh1 = hash_bytes(codec, &data);
let bytes: Vec<u8> = mh1.clone().into();
let mh2 = Multihash::try_from(bytes.as_ref()).unwrap();
prop_assert_eq!(mh1.codec(), codec);
prop_assert_eq!(mh2.codec(), codec);
prop_assert_eq!(mh1.codec(), mh2.codec());
}
});
}
#[test]
fn test_empty_data_valid() {
proptest!(|(_unit in 0..1u8)| {
for &codec in &HASH_CODECS {
let result = Builder::new(codec);
prop_assert!(result.is_ok());
let mut builder = result.unwrap();
builder.update([]);
if let Some(output_len) = xof_output_len(codec) {
builder.output_len(output_len);
}
prop_assert!(builder.try_build().is_ok());
}
});
}
#[test]
fn test_equality_reflexive() {
proptest!(|(data in prop::collection::vec(any::<u8>(), 0..256))| {
let mh = hash_bytes(Codec::Sha2256, &data);
prop_assert_eq!(&mh, &mh);
});
}
#[test]
fn test_equality_symmetric() {
proptest!(|(data in prop::collection::vec(any::<u8>(), 0..256))| {
let mh1 = hash_bytes(Codec::Sha2256, &data);
let mh2 = mh1.clone();
prop_assert_eq!(&mh1, &mh2);
prop_assert_eq!(&mh2, &mh1);
});
}
#[test]
fn test_ordering_consistent() {
proptest!(|(data1 in prop::collection::vec(any::<u8>(), 0..128),
data2 in prop::collection::vec(any::<u8>(), 0..128))| {
let mh1 = hash_bytes(Codec::Sha2256, &data1);
let mh2 = hash_bytes(Codec::Sha2256, &data2);
if mh1 < mh2 {
prop_assert!((mh2 >= mh1));
}
});
}