minerva 0.2.0

Causal ordering for distributed systems
extern crate alloc;

use alloc::vec::Vec;

use super::super::super::super::support::dot;
use super::have;
use crate::metis::{DotSet, HaveSetDecodeBudget, HaveSetDecodeError};

/// `from_prefix` decodes one frame and hands back the untouched payload tail;
/// `from_bytes` over the same bytes rejects the trailing tail as an unexpected
/// length.
#[test]
fn test_have_set_from_prefix_splits_the_tail() {
    let set = have(&[(1, 1), (1, 2), (1, 4)]);
    let mut framed = set.to_bytes();
    let tail: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
    framed.extend_from_slice(tail);

    let (decoded, rest) = DotSet::from_prefix(&framed).expect("prefix decodes");
    assert_eq!(decoded, set);
    assert_eq!(rest, tail);

    assert!(matches!(
        DotSet::from_bytes(&framed),
        Err(HaveSetDecodeError::UnexpectedLength { .. })
    ));
}

#[test]
fn test_explicit_prefix_budget_is_independent_of_tail_length() {
    let mut set = DotSet::new();
    for counter in 2..=100 {
        assert!(set.insert(dot(1, counter)));
    }
    let frame = set.to_bytes();
    let budget = HaveSetDecodeBudget::new(set.exceptions_len());

    for tail in [&[][..], &[0; 1][..], &[0; 128][..]] {
        let mut bytes = frame.clone();
        bytes.extend_from_slice(tail);
        let (decoded, rest) = DotSet::from_prefix_with_budget(&bytes, budget)
            .expect("validated exception budget admits the dense frame");
        assert_eq!(decoded, set);
        assert_eq!(rest, tail);
    }
}

#[test]
fn test_have_set_from_bytes_rejects_unknown_version() {
    let mut frame = have(&[(1, 1)]).to_bytes();
    frame[0] = 0x02;
    assert_eq!(
        DotSet::from_bytes(&frame),
        Err(HaveSetDecodeError::UnknownVersion(0x02))
    );
}

#[test]
fn test_have_set_from_bytes_rejects_short_input() {
    // Shorter than the 5-byte header.
    assert!(matches!(
        DotSet::from_bytes(&[0x01, 0x00]),
        Err(HaveSetDecodeError::UnexpectedLength { .. })
    ));

    // Bounded allocation: a five-byte frame claiming u32::MAX stations is rejected
    // in O(1), without trying to allocate a station per declared count, because the
    // buffer cannot hold even one station header.
    assert!(matches!(
        DotSet::from_bytes(&[0x01, 0xFF, 0xFF, 0xFF, 0xFF]),
        Err(HaveSetDecodeError::UnexpectedLength { .. })
    ));

    // A station header declaring u32::MAX runs, with no run bytes: rejected in O(1)
    // against the buffer, never allocating a run per declared count.
    let mut runaway = Vec::new();
    runaway.push(0x01u8);
    runaway.extend_from_slice(&1u32.to_be_bytes()); // one station
    runaway.extend_from_slice(&7u32.to_be_bytes()); // station 7
    runaway.extend_from_slice(&0u64.to_be_bytes()); // floor 0
    runaway.extend_from_slice(&u32::MAX.to_be_bytes()); // run_count = u32::MAX
    assert!(matches!(
        DotSet::from_bytes(&runaway),
        Err(HaveSetDecodeError::UnexpectedLength { .. })
    ));
}