use netbat as nb;
use proptest::prelude::*;
fn arb_operation_name() -> impl Strategy<Value = String> {
proptest::collection::vec("[A-Za-z0-9_-]{1,16}", 1..=4).prop_map(|segments| segments.join("."))
}
fn arb_payload_bytes() -> impl Strategy<Value = Vec<u8>> {
proptest::collection::vec(any::<u8>(), 0..=1024)
}
proptest! {
#[test]
fn encode_request_decode_line_roundtrip(
op in arb_operation_name(),
payload in arb_payload_bytes(),
) {
let frame = nb::encode_request(&op, &payload);
let parsed = nb::decode_line(&frame, &nb::Limits::default())
.expect("encoder output must decode");
prop_assert_eq!(parsed.operation(), op.as_str());
prop_assert_eq!(parsed.input(), payload.as_slice());
}
#[test]
fn encoded_request_frame_shape(
op in arb_operation_name(),
payload in arb_payload_bytes(),
) {
let frame = nb::encode_request(&op, &payload);
prop_assert!(frame.starts_with(b"NETBAT/1 CALL "));
prop_assert!(frame.ends_with(b"\n"));
prop_assert!(!frame.ends_with(b"\r\n"));
let interior = &frame[..frame.len() - 1];
prop_assert!(!interior.contains(&b'\n'));
}
#[test]
fn encode_request_is_deterministic(
op in arb_operation_name(),
payload in arb_payload_bytes(),
) {
let a = nb::encode_request(&op, &payload);
let b = nb::encode_request(&op, &payload);
prop_assert_eq!(a, b);
}
}
proptest! {
#[test]
fn decode_line_is_total_on_arbitrary_bytes(
line in proptest::collection::vec(any::<u8>(), 0..=4096),
) {
let _ = nb::decode_line(&line, &nb::Limits::default());
}
#[test]
fn decode_line_is_total_on_prefix_plus_garbage(
suffix in proptest::collection::vec(any::<u8>(), 0..=512),
) {
let mut frame = b"NETBAT/1 CALL ".to_vec();
frame.extend_from_slice(&suffix);
let _ = nb::decode_line(&frame, &nb::Limits::default());
}
}
proptest! {
#[test]
fn encode_response_ok_roundtrips(payload in arb_payload_bytes()) {
let frame = nb::encode_response(Ok(&payload));
prop_assert!(frame.starts_with(b"OK "));
prop_assert!(frame.ends_with(b"\n"));
let hex_segment = std::str::from_utf8(&frame[3..frame.len() - 1])
.expect("encode_response emits ASCII hex");
let decoded = nb::decode_hex_str(hex_segment).expect("decodes");
prop_assert_eq!(decoded, payload);
}
#[test]
fn encode_response_err_carries_stable_code(
reason_idx in 0_usize..3_usize,
) {
let reason = match reason_idx {
0 => "bad",
1 => "operation has invalid bytes",
_ => "missing input",
};
let err = nb::NetbatError::MalformedRequest { reason };
let frame = nb::encode_response(Err(&err));
prop_assert!(frame.starts_with(b"ERR malformed_request "));
prop_assert!(frame.ends_with(b"\n"));
}
}