use iroh_http_core::{
base32_encode, generate_secret_key, parse_direct_addrs, parse_node_addr, public_key_verify,
respond, secret_key_sign, HandleStore, NodeAddrInfo, StoreConfig,
};
use proptest::prelude::*;
proptest! {
#[test]
fn parse_node_addr_never_panics(s in "\\PC{0,512}") {
let _ = parse_node_addr(&s);
}
#[test]
fn parse_node_addr_json_arbitrary_addrs(
key_bytes in prop::array::uniform32(any::<u8>()),
addrs in prop::collection::vec("\\PC{0,128}", 0..8),
) {
let b32 = base32_encode(&key_bytes);
let info = NodeAddrInfo { id: b32, addrs };
if let Ok(ticket) = serde_json::to_string(&info) {
let _ = parse_node_addr(&ticket);
}
}
}
proptest! {
#[test]
fn base32_encode_never_panics(bytes in prop::collection::vec(any::<u8>(), 0..256)) {
let encoded = base32_encode(&bytes);
if bytes.is_empty() {
prop_assert!(encoded.is_empty());
} else {
prop_assert!(!encoded.is_empty());
}
}
}
proptest! {
#[test]
fn sign_verify_roundtrip(data in prop::collection::vec(any::<u8>(), 0..512)) {
let sk_bytes = generate_secret_key().unwrap();
let sig = secret_key_sign(&sk_bytes, &data).unwrap();
let sk = iroh::SecretKey::from_bytes(&sk_bytes);
let pk_bytes = sk.public().as_bytes().clone();
prop_assert!(public_key_verify(&pk_bytes, &data, &sig));
}
#[test]
fn public_key_verify_never_panics(
pk in prop::array::uniform32(any::<u8>()),
data in prop::collection::vec(any::<u8>(), 0..128),
sig_lo in prop::array::uniform32(any::<u8>()),
sig_hi in prop::array::uniform32(any::<u8>()),
) {
let mut sig = [0u8; 64];
sig[..32].copy_from_slice(&sig_lo);
sig[32..].copy_from_slice(&sig_hi);
let _ = public_key_verify(&pk, &data, &sig);
}
#[test]
fn secret_key_sign_never_panics(
sk in prop::array::uniform32(any::<u8>()),
data in prop::collection::vec(any::<u8>(), 0..128),
) {
let _ = secret_key_sign(&sk, &data);
}
}
proptest! {
#[test]
fn parse_direct_addrs_never_panics(
addrs in prop::collection::vec("\\PC{0,64}", 0..8),
) {
let _ = parse_direct_addrs(&Some(addrs));
}
#[test]
fn parse_direct_addrs_none_is_none(_dummy in 0u8..1) {
assert!(parse_direct_addrs(&None).unwrap().is_none());
}
}
proptest! {
#[test]
fn respond_never_panics(
status in any::<u16>(),
names in prop::collection::vec("[a-z0-9\\-]{1,32}", 0..8),
values in prop::collection::vec("\\PC{0,64}", 0..8),
) {
let store = HandleStore::new(StoreConfig::default());
let headers: Vec<_> = names.into_iter().zip(values).map(|(n, v)| (n, v)).collect();
let _ = respond(&store, 0, status, headers);
}
}
proptest! {
#[test]
fn handle_store_capacity_bounded(count in 1usize..=200) {
let store = HandleStore::new(StoreConfig {
max_handles: 64,
..Default::default()
});
let mut ok_count = 0u64;
for _ in 0..count {
let (_, reader) = store.make_body_channel();
match store.insert_reader(reader) {
Ok(_) => ok_count += 1,
Err(_) => break,
}
}
prop_assert!(ok_count <= 64);
}
#[test]
fn handle_store_invalid_handle_safe(handle in any::<u64>()) {
let store = HandleStore::new(StoreConfig::default());
assert!(store.take_req_sender(handle).is_none());
store.cancel_reader(handle);
store.finish_body(handle).ok();
store.cancel_in_flight(handle);
store.remove_fetch_token(handle);
assert!(store.lookup_session(handle).is_none());
assert!(store.remove_session(handle).is_none());
assert!(store.claim_pending_reader(handle).is_none());
}
#[test]
fn handle_store_reader_roundtrip(count in 1usize..=32) {
let store = HandleStore::new(StoreConfig::default());
let mut handles = Vec::new();
for _ in 0..count {
let (_, reader) = store.make_body_channel();
handles.push(store.insert_reader(reader).unwrap());
}
for h in &handles {
store.cancel_reader(*h);
}
for h in &handles {
store.finish_body(*h).unwrap_err();
}
}
#[test]
fn handle_store_pending_reader_roundtrip(count in 1usize..=16) {
let store = HandleStore::new(StoreConfig::default());
let mut writer_handles = Vec::new();
for _ in 0..count {
let (wh, reader) = store.alloc_body_writer().unwrap();
store.store_pending_reader(wh, reader);
writer_handles.push(wh);
}
for wh in &writer_handles {
prop_assert!(store.claim_pending_reader(*wh).is_some());
prop_assert!(store.claim_pending_reader(*wh).is_none());
}
}
}