crabka-protocol 0.3.9

Apache Kafka wire-protocol codec (4.3.0), with typed RecordBatch and zero-copy borrowed decode
Documentation
#![cfg(feature = "arbitrary")]

use arbitrary::{Arbitrary, Unstructured};

use crate::{
    UnknownTaggedFields,
    owned::{
        api_versions_request::ApiVersionsRequest,
        api_versions_response::{ApiVersion, ApiVersionsResponse},
    },
};

fn ascii(u: &mut Unstructured, min: usize, max: usize) -> arbitrary::Result<String> {
    let len = u.int_in_range(min..=max)?;
    let mut s = String::with_capacity(len);
    for _ in 0..len {
        let c: u8 = u.int_in_range(0x20..=0x7E)?;
        s.push(c as char);
    }
    Ok(s)
}

impl<'a> Arbitrary<'a> for ApiVersionsRequest {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        Ok(Self {
            client_software_name: ascii(u, 0, 32)?,
            client_software_version: ascii(u, 0, 32)?,
            unknown_tagged_fields: UnknownTaggedFields::default(),
        })
    }
}

impl<'a> Arbitrary<'a> for ApiVersion {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        Ok(Self {
            api_key: u.arbitrary()?,
            min_version: u.arbitrary()?,
            max_version: u.arbitrary()?,
            unknown_tagged_fields: UnknownTaggedFields::default(),
        })
    }
}

impl<'a> Arbitrary<'a> for ApiVersionsResponse {
    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
        let n = u.int_in_range(0..=8usize)?;
        let mut api_keys = Vec::with_capacity(n);
        for _ in 0..n {
            api_keys.push(ApiVersion::arbitrary(u)?);
        }
        Ok(Self {
            error_code: u.arbitrary()?,
            api_keys,
            throttle_time_ms: u.arbitrary()?,
            supported_features: Vec::new(),
            finalized_features_epoch: 0,
            finalized_features: Vec::new(),
            zk_migration_ready: false,
            unknown_tagged_fields: UnknownTaggedFields::default(),
        })
    }
}

#[cfg(test)]
mod tests {
    use assert2::assert;

    use super::*;

    #[test]
    fn ascii_uses_input_and_stays_printable() {
        let mut found = None;
        for seed in 0u8..=u8::MAX {
            let data = [seed, seed.wrapping_mul(37), seed.wrapping_add(91), 0x7E];
            let mut u = Unstructured::new(&data);
            if let Ok(s) = ascii(&mut u, 1, 8)
                && !s.is_empty()
                && s != "xyzzy"
            {
                found = Some(s);
                break;
            }
        }

        let s = found.expect("expected at least one generated string");
        assert!(s.bytes().all(|b| (0x20..=0x7E).contains(&b)));
    }

    #[test]
    fn arbitrary_api_versions_request_can_be_non_default() {
        let mut found = false;
        for seed in 0u8..=u8::MAX {
            let data = [seed, seed.wrapping_add(1), seed.wrapping_add(2), 0x55, 0xAA];
            let mut u = Unstructured::new(&data);
            if let Ok(req) = ApiVersionsRequest::arbitrary(&mut u)
                && req != ApiVersionsRequest::default()
            {
                assert!(req.client_software_name.bytes().all(|b| b.is_ascii()));
                assert!(req.client_software_version.bytes().all(|b| b.is_ascii()));
                found = true;
                break;
            }
        }

        assert!(found);
    }

    #[test]
    fn arbitrary_api_version_can_be_non_default() {
        let mut found = false;
        for seed in 0u8..=u8::MAX {
            let data = [
                seed,
                seed.wrapping_mul(3),
                seed.wrapping_add(5),
                seed.wrapping_add(8),
                seed.wrapping_add(13),
                seed.wrapping_add(21),
            ];
            let mut u = Unstructured::new(&data);
            if let Ok(version) = ApiVersion::arbitrary(&mut u)
                && version != ApiVersion::default()
            {
                found = true;
                break;
            }
        }

        assert!(found);
    }

    #[test]
    fn arbitrary_api_versions_response_can_be_non_default() {
        let mut found = false;
        for seed in 0u8..=u8::MAX {
            let data = [
                seed,
                seed.wrapping_add(1),
                seed.wrapping_add(2),
                seed.wrapping_add(3),
                seed.wrapping_add(4),
                seed.wrapping_add(5),
                seed.wrapping_add(6),
                seed.wrapping_add(7),
                seed.wrapping_add(8),
            ];
            let mut u = Unstructured::new(&data);
            if let Ok(resp) = ApiVersionsResponse::arbitrary(&mut u)
                && resp != ApiVersionsResponse::default()
            {
                found = true;
                break;
            }
        }

        assert!(found);
    }
}