ozzy-replication 0.7.0

Deterministic replication state for Ozzy.
Documentation
use super::*;
use crate::flow::ReceiveEpoch;
use crate::{Digest, OpNumber, Scope};
use ozzy_proto::GroupId;

fn receiver() -> Receiver {
    Receiver::new(
        Channel {
            scope: Scope {
                group_id: GroupId::from_bytes([1; 16]),
                configuration_epoch: 1,
                configuration_digest: Digest::from_bytes([2; 32]),
                view: 0,
            },
            epoch: ReceiveEpoch::new(1).unwrap(),
        },
        Prefix::GENESIS,
        PipelineLimits {
            max_operations: 4,
            max_body_bytes: 100,
        },
    )
    .unwrap()
}

fn next(previous: Prefix) -> Operation {
    Operation {
        prefix: Prefix {
            op: OpNumber(previous.op.0 + 1),
            digest: Digest::from_bytes([1; 32]),
        },
        previous_digest: previous.digest,
        body_bytes: 1,
    }
}

#[test]
fn startup_metadata_reservations_survive_repeated_window_turnover() {
    let mut receiver = receiver();
    let receiver_capacity = receiver.retained.capacity();
    for _ in 0..10_000 {
        let op = next(receiver.report().received);
        receiver.retain(receiver.report().channel, &[op]).unwrap();
        receiver.release(op.prefix).unwrap();
        assert_eq!(receiver.retained.capacity(), receiver_capacity);
    }
}

#[test]
fn revision_exhaustion_cannot_publish_partial_retention_or_release() {
    let mut receiver = receiver();
    let op = next(Prefix::GENESIS);
    receiver.report.revision = u64::MAX;
    let before = receiver.report();
    assert_eq!(
        receiver.retain(before.channel, &[op]),
        Err(FlowError::Exhausted)
    );
    assert_eq!(receiver.report(), before);
    assert!(receiver.retained.is_empty());
    receiver.report.revision -= 1;
    receiver.retain(before.channel, &[op]).unwrap();
    let before = receiver.report();
    receiver.release(op.prefix).unwrap();
    assert_eq!(receiver.report(), before);
    assert!(receiver.retained.is_empty());
}

#[test]
fn retraction_reuses_capacity_and_recovers_exhausted_revision() {
    let mut receiver = receiver();
    let capacity = receiver.retained.capacity();
    for epoch in 2..10_002 {
        let op = next(receiver.report().received);
        receiver.retain(receiver.report().channel, &[op]).unwrap();
        let successor = next(op.prefix);
        receiver
            .retain(receiver.report().channel, &[successor])
            .unwrap();
        receiver.report.revision = u64::MAX;
        receiver
            .retract(ReceiveEpoch::new(epoch).unwrap(), op.prefix)
            .unwrap();
        assert_eq!(receiver.report().revision, 1);
        assert_eq!(receiver.report().received_bytes, 1);
        assert_eq!(receiver.retained.len(), 1);
        assert_eq!(receiver.retained.capacity(), capacity);
        receiver.release(op.prefix).unwrap();
    }
}

#[test]
fn local_capacity_release_does_not_change_the_wire_receipt() {
    let mut receiver = receiver();
    let channel = receiver.report().channel;
    let mut operation = next(Prefix::GENESIS);
    operation.body_bytes = 100;
    receiver.retain(channel, &[operation]).unwrap();
    let report = receiver.report();
    let next = next(operation.prefix);
    assert_eq!(receiver.retain(channel, &[next]), Err(FlowError::Capacity));
    assert_eq!(receiver.report(), report);
    receiver.release(operation.prefix).unwrap();
    assert_eq!(receiver.report(), report);
    assert_eq!(receiver.available().max_body_bytes, 100);
    receiver.retain(channel, &[next]).unwrap();
}