#![allow(clippy::expect_used, clippy::unwrap_used)]
use network_protocol::config::PROTOCOL_VERSION;
use network_protocol::core::packet::Packet;
use network_protocol::core::serialization::{MultiFormat, SerializationFormat};
use network_protocol::protocol::message::Message;
use network_protocol::utils::compression::{compress, decompress, CompressionKind};
use proptest::prelude::*;
proptest! {
#[test]
fn prop_packet_roundtrip(payload in prop::collection::vec(any::<u8>(), 0..10000)) {
let packet = Packet { version: PROTOCOL_VERSION, payload: payload.clone() };
let serialized = packet.to_bytes();
let deserialized = Packet::from_bytes(&serialized).expect("Deserialization should not fail");
prop_assert_eq!(deserialized.payload, payload);
}
}
proptest! {
#[test]
fn prop_packet_serialization_deterministic(payload in prop::collection::vec(any::<u8>(), 0..1000)) {
let packet = Packet { version: PROTOCOL_VERSION, payload };
let bytes1 = packet.to_bytes();
let bytes2 = packet.to_bytes();
prop_assert_eq!(bytes1, bytes2);
}
}
proptest! {
#[test]
fn prop_lz4_compression_roundtrip(data in prop::collection::vec(any::<u8>(), 0..50000)) {
let compressed = compress(&data, &CompressionKind::Lz4).expect("Compression should not fail");
let decompressed = decompress(&compressed, &CompressionKind::Lz4).expect("Decompression should not fail");
prop_assert_eq!(decompressed, data);
}
}
proptest! {
#[test]
fn prop_zstd_compression_roundtrip(data in prop::collection::vec(any::<u8>(), 0..50000)) {
let compressed = compress(&data, &CompressionKind::Zstd).expect("Compression should not fail");
let decompressed = decompress(&compressed, &CompressionKind::Zstd).expect("Decompression should not fail");
prop_assert_eq!(decompressed, data);
}
}
proptest! {
#[test]
fn prop_compression_never_panics(data in prop::collection::vec(any::<u8>(), 0..10000)) {
let lz4_result = compress(&data, &CompressionKind::Lz4);
prop_assert!(lz4_result.is_ok());
let zstd_result = compress(&data, &CompressionKind::Zstd);
prop_assert!(zstd_result.is_ok());
}
}
proptest! {
#[test]
fn prop_decompression_invalid_data_returns_error(data in prop::collection::vec(any::<u8>(), 0..1000)) {
let _ = decompress(&data, &CompressionKind::Lz4);
let _ = decompress(&data, &CompressionKind::Zstd);
prop_assert!(true);
}
}
proptest! {
#[test]
fn prop_packet_size_accurate(payload in prop::collection::vec(any::<u8>(), 0..10000)) {
let packet = Packet { version: PROTOCOL_VERSION, payload: payload.clone() };
let serialized = packet.to_bytes();
let expected_size = 9 + payload.len();
prop_assert_eq!(serialized.len(), expected_size);
}
}
proptest! {
#[test]
fn prop_empty_payload_handled(_seed in any::<u8>()) {
let packet = Packet { version: PROTOCOL_VERSION, payload: vec![] };
let serialized = packet.to_bytes();
let deserialized = Packet::from_bytes(&serialized).expect("Deserialization should not fail");
prop_assert!(deserialized.payload.is_empty());
}
}
proptest! {
#[test]
fn prop_large_payload_handled(size in 0usize..100_000) {
let payload = vec![0xAB; size];
let packet = Packet { version: PROTOCOL_VERSION, payload: payload.clone() };
let serialized = packet.to_bytes();
let deserialized = Packet::from_bytes(&serialized).expect("Deserialization should not fail");
prop_assert_eq!(deserialized.payload.len(), size);
if !payload.is_empty() {
prop_assert_eq!(deserialized.payload[0], 0xAB);
}
}
}
proptest! {
#[test]
fn prop_message_format_consistency(msg_type in 0u8..5) {
let message = match msg_type {
0 => Message::Ping,
1 => Message::Pong,
2 => Message::Disconnect,
3 => Message::Echo("test".to_string()),
_ => Message::Custom {
command: "cmd".to_string(),
payload: vec![1, 2, 3],
},
};
for format in [SerializationFormat::Bincode, SerializationFormat::Json, SerializationFormat::MessagePack] {
let serialized = message.serialize_format(format).expect("Serialization should not fail");
let deserialized = Message::deserialize_format(&serialized, format).expect("Deserialization should not fail");
prop_assert_eq!(&message, &deserialized);
}
}
}
proptest! {
#[test]
fn prop_packet_magic_bytes_correct(payload in prop::collection::vec(any::<u8>(), 0..1000)) {
let packet = Packet { version: PROTOCOL_VERSION, payload };
let serialized = packet.to_bytes();
prop_assert_eq!(serialized[0], 0x4E);
prop_assert_eq!(serialized[1], 0x50);
prop_assert_eq!(serialized[2], 0x52);
prop_assert_eq!(serialized[3], 0x4F);
}
}
proptest! {
#[test]
fn prop_packet_version_correct(payload in prop::collection::vec(any::<u8>(), 0..1000)) {
let packet = Packet { version: PROTOCOL_VERSION, payload };
let serialized = packet.to_bytes();
prop_assert_eq!(serialized[4], PROTOCOL_VERSION);
}
}
proptest! {
#[test]
fn prop_packet_length_field_correct(payload in prop::collection::vec(any::<u8>(), 0..10000)) {
let packet = Packet { version: PROTOCOL_VERSION, payload: payload.clone() };
let serialized = packet.to_bytes();
let length_bytes = [serialized[5], serialized[6], serialized[7], serialized[8]];
let length = u32::from_be_bytes(length_bytes) as usize;
prop_assert_eq!(length, payload.len());
}
}
proptest! {
#[test]
fn prop_compression_reduces_repetitive_data(byte in any::<u8>(), size in 1000usize..10000) {
let data = vec![byte; size];
let compressed = compress(&data, &CompressionKind::Lz4).expect("Compression should not fail");
prop_assert!(compressed.len() < data.len() / 2);
}
}
proptest! {
#[test]
fn prop_compression_handles_random_data(data in prop::collection::vec(any::<u8>(), 0..10000)) {
let lz4_compressed = compress(&data, &CompressionKind::Lz4).expect("LZ4 compression should not fail");
let lz4_decompressed = decompress(&lz4_compressed, &CompressionKind::Lz4).expect("LZ4 decompression should not fail");
prop_assert_eq!(&lz4_decompressed, &data);
let zstd_compressed = compress(&data, &CompressionKind::Zstd).expect("Zstd compression should not fail");
let zstd_decompressed = decompress(&zstd_compressed, &CompressionKind::Zstd).expect("Zstd decompression should not fail");
prop_assert_eq!(&zstd_decompressed, &data);
}
}
proptest! {
#[test]
fn prop_packet_rejects_invalid_magic(
b0 in any::<u8>(),
b1 in any::<u8>(),
b2 in any::<u8>(),
b3 in any::<u8>()
) {
prop_assume!([b0, b1, b2, b3] != [0x4E, 0x50, 0x52, 0x4F]);
let data = vec![b0, b1, b2, b3, 1, 0, 0, 0, 0];
let result = Packet::from_bytes(&data);
prop_assert!(result.is_err());
}
}
proptest! {
#[test]
fn prop_multiple_compressions_stable(data in prop::collection::vec(any::<u8>(), 100..1000)) {
let compressed1 = compress(&data, &CompressionKind::Lz4).expect("First compression failed");
let decompressed1 = decompress(&compressed1, &CompressionKind::Lz4).expect("First decompression failed");
let compressed2 = compress(&decompressed1, &CompressionKind::Lz4).expect("Second compression failed");
let decompressed2 = decompress(&compressed2, &CompressionKind::Lz4).expect("Second decompression failed");
prop_assert_eq!(data, decompressed2);
}
}
proptest! {
#[test]
fn prop_packet_content_independent(
payload1 in prop::collection::vec(any::<u8>(), 0..1000),
payload2 in prop::collection::vec(any::<u8>(), 0..1000)
) {
let packet1 = Packet { version: PROTOCOL_VERSION, payload: payload1.clone() };
let packet2 = Packet { version: PROTOCOL_VERSION, payload: payload2.clone() };
let bytes1 = packet1.to_bytes();
let bytes2 = packet2.to_bytes();
let recovered1 = Packet::from_bytes(&bytes1).expect("Deserialization should not fail");
let recovered2 = Packet::from_bytes(&bytes2).expect("Deserialization should not fail");
prop_assert_eq!(recovered1.payload, payload1);
prop_assert_eq!(recovered2.payload, payload2);
}
}