minerva 0.2.0

Causal ordering for distributed systems
use super::super::super::support::vv;
use crate::metis::{DecodeError, VersionVector};

#[test]
fn test_vv_to_bytes_layout() {
    // The frame for {1: 1, 2: 5}, frozen byte-for-byte (PRD 0007).
    let frame = vv(&[(1, 1), (2, 5)]).to_bytes();
    assert_eq!(
        frame,
        [
            0x01, // version
            0x00, 0x00, 0x00, 0x02, // count = 2
            0x00, 0x00, 0x00, 0x01, // station 1
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // counter 1
            0x00, 0x00, 0x00, 0x02, // station 2
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, // counter 5
        ]
    );
    // The empty vector (lattice bottom) is the five-byte header with a zero count.
    assert_eq!(VersionVector::new().to_bytes(), [0x01u8, 0, 0, 0, 0]);
}

#[test]
fn test_vv_from_bytes_rejects_unknown_version() {
    let mut frame = vv(&[(1, 1)]).to_bytes();
    frame[0] = 0x02;
    assert_eq!(
        VersionVector::from_bytes(&frame),
        Err(DecodeError::UnknownVersion(0x02))
    );
}

#[test]
fn test_vv_from_bytes_rejects_bad_length() {
    // A structurally valid frame plus one trailing byte: from_bytes is exact.
    let mut trailing = vv(&[(1, 1)]).to_bytes();
    trailing.push(0xFF);
    assert!(matches!(
        VersionVector::from_bytes(&trailing),
        Err(DecodeError::UnexpectedLength { .. })
    ));

    // Shorter than the 5-byte header.
    assert!(matches!(
        VersionVector::from_bytes(&[0x01, 0x00]),
        Err(DecodeError::UnexpectedLength { .. })
    ));

    // Bounded allocation: a five-byte frame claiming u32::MAX entries is rejected in
    // O(1), without trying to allocate count * 12 bytes, because the buffer cannot
    // hold the claim.
    assert!(matches!(
        VersionVector::from_bytes(&[0x01, 0xFF, 0xFF, 0xFF, 0xFF]),
        Err(DecodeError::UnexpectedLength { .. })
    ));
}

#[test]
fn test_vv_from_bytes_rejects_zero_counter() {
    // One entry, counter 0: a value no encoder produces (canonical form holds it absent).
    let frame = [
        0x01, 0x00, 0x00, 0x00, 0x01, // version, count = 1
        0x00, 0x00, 0x00, 0x07, // station 7
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // counter 0
    ];
    assert_eq!(
        VersionVector::from_bytes(&frame),
        Err(DecodeError::ZeroCounter { station: 7 })
    );
}

#[test]
fn test_vv_from_bytes_rejects_unsorted_stations() {
    // Stations 5 then 3: not strictly ascending.
    let unsorted = [
        0x01, 0x00, 0x00, 0x00, 0x02, // version, count = 2
        0x00, 0x00, 0x00, 0x05, // station 5
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // counter 1
        0x00, 0x00, 0x00, 0x03, // station 3
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // counter 1
    ];
    assert_eq!(
        VersionVector::from_bytes(&unsorted),
        Err(DecodeError::NonAscendingStations {
            previous: 5,
            found: 3
        })
    );

    // A duplicate station is also non-ascending (not strictly greater).
    let duplicate = [
        0x01, 0x00, 0x00, 0x00, 0x02, // version, count = 2
        0x00, 0x00, 0x00, 0x04, // station 4
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // counter 1
        0x00, 0x00, 0x00, 0x04, // station 4 again
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // counter 2
    ];
    assert_eq!(
        VersionVector::from_bytes(&duplicate),
        Err(DecodeError::NonAscendingStations {
            previous: 4,
            found: 4
        })
    );
}