ozzy-proto 0.7.0

Sans-I/O application protocol and message types for Ozzy.
Documentation
use super::*;
use crate::GroupId;
use crate::{LinkSessionId, NodeId, RequestId, decode_packet};

fn envelope(opcode: Opcode) -> Envelope {
    Envelope {
        opcode,
        response: opcode == Opcode::ProducerOpened,
        request_id: Some(RequestId::from_bytes([4; 16])),
        sender: NodeId::from_bytes([5; 16]),
        session: Some(LinkSessionId::from_bytes([6; 16])),
    }
}

fn open() -> Open {
    Open {
        authority: Authority {
            group_id: GroupId::from_bytes([1; 16]),
            config_epoch: 1,
            view: 3,
        },
        partition: PartitionIncarnation::from_bytes([2; 16]),
        producer: ProducerId::from_bytes([3; 16]),
        mode: Mode::Resume,
        expected_epoch: None,
        operation: OperationId::from_bytes([7; 16]),
    }
}

fn packet<'a>(header: &'a [u8; ENVELOPE_BYTES], metadata: &'a [u8]) -> Packet<'a> {
    decode_packet(&[header, metadata, &[]], EnvelopeLimits::default()).unwrap()
}

#[test]
fn open_and_opened_round_trip() {
    let mut metadata = Vec::with_capacity(128);
    let limits = EnvelopeLimits::default();
    let request = open();
    let header = encode_open(
        envelope(Opcode::OpenProducer),
        request,
        &mut metadata,
        limits,
    )
    .unwrap();
    assert_eq!(metadata.len(), OPEN_BYTES);
    assert_eq!(decode_open(packet(&header, &metadata), limits), Ok(request));

    let mut fenced = request;
    fenced.mode = Mode::Fence;
    fenced.expected_epoch = Some(7);
    let header = encode_open(
        envelope(Opcode::OpenProducer),
        fenced,
        &mut metadata,
        limits,
    )
    .unwrap();
    assert_eq!(decode_open(packet(&header, &metadata), limits), Ok(fenced));

    let response = Opened {
        authority: request.authority,
        partition: request.partition,
        producer: request.producer,
        epoch: 8,
        next_sequence: 42,
        retry_floor: 21,
        policy: Policy::QuorumDurable,
    };
    let header = encode_opened(
        envelope(Opcode::ProducerOpened),
        response,
        &mut metadata,
        limits,
    )
    .unwrap();
    assert_eq!(metadata.len(), OPENED_BYTES);
    assert_eq!(
        decode_opened(packet(&header, &metadata), limits),
        Ok(response)
    );
}

#[test]
fn malformed_fences_and_unconfirmed_policy_fail_closed() {
    let mut metadata = Vec::with_capacity(128);
    let limits = EnvelopeLimits::default();
    let mut request = open();
    request.mode = Mode::Fence;
    assert_eq!(
        encode_open(
            envelope(Opcode::OpenProducer),
            request,
            &mut metadata,
            limits
        ),
        Err(CodecError::Identity)
    );
    request = open();
    request.operation = OperationId::from_bytes([0; 16]);
    assert_eq!(
        encode_open(
            envelope(Opcode::OpenProducer),
            request,
            &mut metadata,
            limits
        ),
        Err(CodecError::Identity)
    );
    let request = open();
    let header = encode_open(
        envelope(Opcode::OpenProducer),
        request,
        &mut metadata,
        limits,
    )
    .unwrap();
    metadata[65] = 9;
    assert_eq!(
        decode_open(packet(&header, &metadata), limits),
        Err(CodecError::Profile)
    );
    let response = Opened {
        authority: request.authority,
        partition: request.partition,
        producer: request.producer,
        epoch: 1,
        next_sequence: 2,
        retry_floor: 3,
        policy: Policy::LocalDurable,
    };
    assert_eq!(
        encode_opened(
            envelope(Opcode::ProducerOpened),
            response,
            &mut metadata,
            limits
        ),
        Err(CodecError::Profile)
    );
}