use aatp::codec::{Reader, Writer};
use aatp::message::{self, AgentMessage};
use aatp::{OVERHEAD, decode, dict, encode, shadow};
struct Rng(u64);
impl Rng {
fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn byte(&mut self) -> u8 {
(self.next_u64() & 0xFF) as u8
}
fn upto(&mut self, n: usize) -> usize {
(self.next_u64() % (n as u64 + 1)) as usize
}
fn blob(&mut self, max: usize) -> Vec<u8> {
let len = self.upto(max);
(0..len).map(|_| self.byte()).collect()
}
fn text(&mut self, max: usize) -> String {
let len = self.upto(max);
(0..len)
.map(|_| (b'a' + (self.byte() % 26)) as char)
.collect()
}
}
fn iters(default: usize) -> usize {
std::env::var("AATP_FUZZ_ITERS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(default)
}
#[test]
fn parsers_never_panic_on_arbitrary_bytes() {
let mut rng = Rng(0x1234_5678_9ABC_DEF0);
let mut out = vec![0u8; 8192];
for _ in 0..iters(20_000) {
let buf = rng.blob(400);
let _ = decode(&buf);
let _ = message::decode(&buf);
let _ = dict::decompress(&buf, &mut out);
let _ = shadow::mask(&buf, rng.next_u64(), &mut out);
let mut r = Reader::new(&buf);
let _ = r.u8();
let _ = r.u64();
let _ = r.str();
let _ = r.bytes();
let _ = r.u32();
}
}
#[test]
fn frame_and_full_chain_round_trips_hold() {
let mut rng = Rng(0xCAFE_F00D_1234_5678);
for _ in 0..iters(20_000) {
let payload = rng.blob(300);
let ty = rng.byte();
let mut frame = vec![0u8; OVERHEAD + payload.len()];
let n = encode(ty, &payload, &mut frame).unwrap();
let pkt = decode(&frame[..n]).unwrap();
assert_eq!(pkt.msg_type(), ty);
assert_eq!(pkt.payload(), &payload[..]);
if n > 0 {
let i = rng.upto(n - 1); frame[i] ^= 1 | rng.byte();
if let Ok(p) = decode(&frame[..n]) {
assert!(
p.msg_type() == ty && p.payload() == &payload[..],
"a byte flip produced a *different* valid frame"
);
}
}
}
}
#[test]
fn dict_and_shadow_are_invertible_over_random_data() {
let mut rng = Rng(0x0BAD_C0DE_F00D_BEEF);
for _ in 0..iters(20_000) {
let data = rng.blob(256);
let mut packed = vec![0u8; data.len() * 2 + 4];
let plen = dict::compress(&data, &mut packed).unwrap();
let mut back = vec![0u8; data.len() + 16];
let blen = dict::decompress(&packed[..plen], &mut back).unwrap();
assert_eq!(
&back[..blen],
&data[..],
"dict round trip must be the identity"
);
let key = rng.next_u64();
let mut once = vec![0u8; data.len()];
shadow::mask(&data, key, &mut once).unwrap();
let mut twice = vec![0u8; data.len()];
shadow::mask(&once, key, &mut twice).unwrap();
assert_eq!(&twice[..], &data[..], "mask twice must be the identity");
}
}
#[test]
fn cursor_and_message_round_trips_hold() {
let mut rng = Rng(0xF00D_01CE_C0FF_EE00);
for _ in 0..iters(20_000) {
let (a, b, c, d) = (
rng.byte(),
rng.next_u64() as u16,
rng.next_u64() as u32,
rng.next_u64(),
);
let blob = rng.blob(64);
let s = rng.text(40);
let mut buf = vec![0u8; 256];
let n = {
let mut w = Writer::new(&mut buf);
w.u8(a).unwrap();
w.u16(b).unwrap();
w.u32(c).unwrap();
w.u64(d).unwrap();
w.bytes(&blob).unwrap();
w.str(&s).unwrap();
w.position()
};
let mut r = Reader::new(&buf[..n]);
assert_eq!(r.u8().unwrap(), a);
assert_eq!(r.u16().unwrap(), b);
assert_eq!(r.u32().unwrap(), c);
assert_eq!(r.u64().unwrap(), d);
assert_eq!(r.bytes().unwrap(), &blob[..]);
assert_eq!(r.str().unwrap(), s);
let name = rng.text(30);
let content = rng.text(120);
let msg = AgentMessage {
id: rng.next_u64(),
role: rng.byte(),
name: &name,
content: &content,
token_count: rng.next_u64() as u32,
};
let mut mbuf = vec![0u8; message::encoded_len(&msg)];
message::encode(&msg, &mut mbuf).unwrap();
assert_eq!(message::decode(&mbuf).unwrap(), msg);
}
}