haematite 0.6.2

Content-addressed, branchable, actor-native storage engine
Documentation
use crate::carrier::{
    CarrierRefusal, FrameDecoder, MAX_CARRIER_FRAME_BYTES, OpaqueBytes, decode_envelope,
    encode_frame,
};

#[test]
fn envelope_is_big_endian_and_preserves_empty_and_opaque_payloads() {
    assert_eq!(encode_frame(&[]), Ok(vec![0, 0, 0, 0]));

    let payload = [0x1f, 0x8b, 0x08, 0x00, 0xff, 0x00];
    let encoded = encode_frame(&payload);
    assert_eq!(
        encoded,
        Ok(vec![0, 0, 0, 6, 0x1f, 0x8b, 0x08, 0x00, 0xff, 0x00])
    );
    if let Ok(envelope) = encoded {
        assert_eq!(
            decode_envelope(&envelope),
            Ok(OpaqueBytes::from(&payload[..]))
        );
    }
}

#[test]
fn strict_decoder_reports_every_truncated_prefix_length() {
    let prefix = [0, 0, 0, 1];
    for received in 0..4 {
        assert_eq!(
            decode_envelope(&prefix[..received]),
            Err(CarrierRefusal::TruncatedLengthPrefix { received })
        );
    }
}

#[test]
fn strict_decoder_distinguishes_short_and_trailing_bodies() {
    assert_eq!(
        decode_envelope(&[0, 0, 0, 3, 1, 2]),
        Err(CarrierRefusal::TruncatedFrame {
            announced: 3,
            received: 2,
        })
    );
    assert_eq!(
        decode_envelope(&[0, 0, 0, 1, 7, 8]),
        Err(CarrierRefusal::TrailingFrameBytes {
            announced: 1,
            received: 2,
        })
    );
}

#[test]
fn incremental_decoder_handles_arbitrary_splits_and_concatenated_stream_frames() {
    let first = encode_frame(&[1, 2, 3]);
    let second = encode_frame(&[4, 5]);
    let (Ok(first), Ok(second)) = (first, second) else {
        return;
    };
    let stream: Vec<u8> = first.into_iter().chain(second).collect();
    let mut decoder = FrameDecoder::new();
    let mut frames = Vec::new();
    for byte in &stream {
        if let Ok(decoded) = decoder.push(std::slice::from_ref(byte)) {
            frames.extend(decoded);
        }
    }
    assert_eq!(
        frames,
        vec![
            OpaqueBytes::from(&[1, 2, 3][..]),
            OpaqueBytes::from(&[4, 5][..])
        ]
    );
    assert_eq!(decoder.finish(), Ok(()));
}

#[test]
fn oversized_prefix_is_refused_before_payload_allocation_and_is_fatal() {
    let announced = u32::try_from(MAX_CARRIER_FRAME_BYTES + 1).unwrap_or(u32::MAX);
    let mut decoder = FrameDecoder::new();
    let refusal = CarrierRefusal::FrameTooLarge {
        announced,
        maximum: MAX_CARRIER_FRAME_BYTES,
    };

    assert_eq!(decoder.push(&announced.to_be_bytes()), Err(refusal));
    assert_eq!(decoder.payload_allocation_count(), 0);
    assert_eq!(decoder.buffered_payload_capacity(), 0);
    assert_eq!(decoder.push(&[0, 0, 0, 0]), Err(refusal));
    assert_eq!(decoder.finish(), Err(refusal));
}

#[test]
fn truncated_incremental_body_becomes_fatal_at_end_of_input() {
    let mut decoder = FrameDecoder::new();
    assert_eq!(decoder.push(&[0, 0, 0, 2, 9]), Ok(Vec::new()));
    let refusal = CarrierRefusal::TruncatedFrame {
        announced: 2,
        received: 1,
    };
    assert_eq!(decoder.finish(), Err(refusal));
    assert_eq!(decoder.push(&[10]), Err(refusal));
}