minerva 0.2.0

Causal ordering for distributed systems
use crate::metis::{Rhapsody, RhapsodyDecodeError};

use super::canonical_frame;

#[test]
fn test_rhapsody_rejects_unknown_version() {
    let mut bytes = canonical_frame();
    bytes[0] = 0x03;
    assert_eq!(
        Rhapsody::from_bytes(&bytes),
        Err(RhapsodyDecodeError::UnknownVersion(0x03))
    );
}

#[test]
fn test_rhapsody_rejects_retired_v1_version() {
    let mut bytes = canonical_frame();
    bytes[0] = 0x01;
    assert_eq!(
        Rhapsody::from_bytes(&bytes),
        Err(RhapsodyDecodeError::UnknownVersion(0x01))
    );
}

/// The S199 gate review's finding, pinned at the testable codec: the
/// run-coalesced plane's live-entry ceiling (2^31 - 2) sits below the count
/// field's u32 domain, so a frame declaring more loci than any plane can
/// hold refuses with the typed capacity arm, up front, before any record
/// work, never folding into a silently truncated skeleton.
#[test]
fn test_rhapsody_rejects_over_capacity_locus_count() {
    let mut bytes = canonical_frame();
    bytes[1..5].copy_from_slice(&0x8000_0000u32.to_be_bytes());
    assert_eq!(
        Rhapsody::from_bytes(&bytes),
        Err(RhapsodyDecodeError::TooManyLoci {
            count: 0x8000_0000,
            capacity: 0x7FFF_FFFE,
        })
    );
    // One under the ceiling passes the capacity gate and fails only where
    // it honestly should, on the input's actual length.
    bytes[1..5].copy_from_slice(&0x7FFF_FFFEu32.to_be_bytes());
    assert!(matches!(
        Rhapsody::from_bytes(&bytes),
        Err(RhapsodyDecodeError::UnexpectedLength { .. })
    ));
}

#[test]
fn test_rhapsody_truncation_at_every_boundary() {
    let bytes = canonical_frame();
    for cut in 0..bytes.len() {
        let prefix = &bytes[..cut];
        if let Ok(rhapsody) = Rhapsody::from_bytes(prefix) {
            let reencoded = rhapsody.to_bytes();
            assert_eq!(reencoded.as_slice(), prefix);
        }
        if let Ok((rhapsody, tail)) = Rhapsody::from_prefix(prefix) {
            let consumed = prefix.len() - tail.len();
            let reencoded = rhapsody.to_bytes();
            assert_eq!(reencoded.as_slice(), &prefix[..consumed]);
        }
    }

    let (_, tail) = Rhapsody::from_prefix(&bytes).expect("whole frame decodes");
    assert!(tail.is_empty());

    let mut extra = bytes.clone();
    extra.push(0x00);
    assert!(matches!(
        Rhapsody::from_bytes(&extra),
        Err(RhapsodyDecodeError::UnexpectedLength { .. })
    ));
}