use proptest::prelude::*;
use proxy_protocol_rs::{HeaderBuilder, ParseError, parse};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
proptest! {
#[test]
fn prop_parse_never_panics(data in prop::collection::vec(any::<u8>(), 0..2048)) {
let _ = parse(&data);
}
#[test]
fn prop_v2_ipv4_roundtrip(
src_ip in any::<Ipv4Addr>(),
dst_ip in any::<Ipv4Addr>(),
src_port in any::<u16>(),
dst_port in any::<u16>(),
) {
let src = SocketAddr::new(src_ip.into(), src_port);
let dst = SocketAddr::new(dst_ip.into(), dst_port);
let bytes = HeaderBuilder::v2_proxy(src, dst).build();
let (info, consumed) = parse(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(info.source_inet().unwrap(), src);
assert_eq!(info.destination_inet().unwrap(), dst);
}
#[test]
fn prop_v2_ipv6_roundtrip(
src_ip in any::<Ipv6Addr>(),
dst_ip in any::<Ipv6Addr>(),
src_port in any::<u16>(),
dst_port in any::<u16>(),
) {
let src = SocketAddr::new(src_ip.into(), src_port);
let dst = SocketAddr::new(dst_ip.into(), dst_port);
let bytes = HeaderBuilder::v2_proxy(src, dst).build();
let (info, consumed) = parse(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(info.source_inet().unwrap(), src);
assert_eq!(info.destination_inet().unwrap(), dst);
}
#[test]
fn prop_v1_roundtrip(
src_ip in any::<Ipv4Addr>(),
dst_ip in any::<Ipv4Addr>(),
src_port in any::<u16>(),
dst_port in any::<u16>(),
) {
let src = SocketAddr::new(src_ip.into(), src_port);
let dst = SocketAddr::new(dst_ip.into(), dst_port);
let bytes = HeaderBuilder::v1_proxy(src, dst).build();
let (info, consumed) = parse(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(info.source_inet().unwrap(), src);
assert_eq!(info.destination_inet().unwrap(), dst);
}
#[test]
fn prop_v1_ipv6_roundtrip(
src_ip in any::<Ipv6Addr>(),
dst_ip in any::<Ipv6Addr>(),
src_port in any::<u16>(),
dst_port in any::<u16>(),
) {
let src = SocketAddr::new(src_ip.into(), src_port);
let dst = SocketAddr::new(dst_ip.into(), dst_port);
let bytes = HeaderBuilder::v1_proxy(src, dst).build();
let (info, consumed) = parse(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(info.source_inet().unwrap(), src);
assert_eq!(info.destination_inet().unwrap(), dst);
}
#[test]
fn prop_truncated_v2_is_incomplete(
src_ip in any::<Ipv4Addr>(),
dst_ip in any::<Ipv4Addr>(),
src_port in any::<u16>(),
dst_port in any::<u16>(),
cut_point in 1usize..28,
) {
let src = SocketAddr::new(src_ip.into(), src_port);
let dst = SocketAddr::new(dst_ip.into(), dst_port);
let full = HeaderBuilder::v2_proxy(src, dst).build();
let cut = cut_point.min(full.len() - 1);
let truncated = &full[..cut];
match parse(truncated) {
Err(ParseError::Incomplete) => {} other => panic!("expected Incomplete at cut_point={cut}, got {other:?}"),
}
}
#[test]
fn prop_non_pp_first_byte(
byte in any::<u8>().prop_filter("not PP start", |b| *b != 0x0D && *b != b'P'),
) {
match parse(&[byte]) {
Err(ParseError::NotProxyProtocol) => {}
other => panic!("expected NotProxyProtocol, got {other:?}"),
}
}
}