cloud-sdk 0.44.0

no_std-first provider-neutral cloud SDK foundations.
Documentation
use super::super::{
    CursorDigest, CursorHistory, MAX_OPAQUE_STATE_BYTES, PaginationCursor, PaginationError,
    PaginationLimits, PaginationMarker,
};
use super::assert_redacted;

fn limits(state_bytes: usize) -> PaginationLimits {
    PaginationLimits::new(8, 100, state_bytes).unwrap_or_else(|_| unreachable!())
}

fn cursor<'a>(
    source: &mut [u8],
    destination: &'a mut [u8],
) -> Result<PaginationCursor<'a>, PaginationError> {
    PaginationCursor::transfer_from(source, destination, limits(64))
}

#[test]
fn transfers_cursor_and_marker_atomically_and_clears_complete_storage() {
    let mut cursor_source = *b"opaque-cursor";
    let mut cursor_storage = [0xa5_u8; 32];
    {
        let cursor =
            cursor(&mut cursor_source, &mut cursor_storage).unwrap_or_else(|_| unreachable!());
        assert!(cursor.with_cursor(|value| value == b"opaque-cursor"));
        assert_eq!(cursor_source, [0; 13]);
        assert_redacted(&cursor);
    }
    assert_eq!(cursor_storage, [0; 32]);

    let mut marker_source = *b"marker-7";
    let mut marker_storage = [0xa5_u8; 24];
    {
        let marker =
            PaginationMarker::transfer_from(&mut marker_source, &mut marker_storage, limits(16))
                .unwrap_or_else(|_| unreachable!());
        assert!(marker.with_marker(|value| value == b"marker-7"));
    }
    assert_eq!(marker_source, [0; 8]);
    assert_eq!(marker_storage, [0; 24]);
}

#[test]
fn failed_transfer_clears_source_and_destination_without_partial_state() {
    let mut source = *b"too-long";
    let mut destination = [0xa5_u8; 4];
    assert!(matches!(
        PaginationCursor::transfer_from(&mut source, &mut destination, limits(32)),
        Err(PaginationError::OutputTooSmall)
    ));
    assert_eq!(source, [0; 8]);
    assert_eq!(destination, [0; 4]);

    let mut source = *b"oversized";
    let mut destination = [0xa5_u8; 16];
    assert!(matches!(
        PaginationCursor::transfer_from(&mut source, &mut destination, limits(4)),
        Err(PaginationError::StateTooLong)
    ));
    assert_eq!(source, [0; 9]);
    assert_eq!(destination, [0; 16]);

    let mut history_storage = [0xa5_u8; 16];
    assert!(matches!(
        CursorHistory::new(&mut history_storage, 0),
        Err(PaginationError::ZeroLimit)
    ));
    assert_eq!(history_storage, [0; 16]);
}

#[test]
fn exact_opaque_hard_bound_is_admitted_and_plus_one_limit_is_rejected() {
    let mut source = [b'x'; MAX_OPAQUE_STATE_BYTES];
    let mut destination = [0_u8; MAX_OPAQUE_STATE_BYTES];
    {
        let cursor = PaginationCursor::transfer_from(
            &mut source,
            &mut destination,
            limits(MAX_OPAQUE_STATE_BYTES),
        )
        .unwrap_or_else(|_| unreachable!());
        assert!(cursor.with_cursor(|value| value.len() == MAX_OPAQUE_STATE_BYTES));
    }
    assert!(source.iter().all(|byte| *byte == 0));
    assert!(destination.iter().all(|byte| *byte == 0));
    assert_eq!(
        PaginationLimits::new(1, 1, MAX_OPAQUE_STATE_BYTES + 1),
        Err(PaginationError::StateLimitTooLarge)
    );
}

#[test]
fn history_detects_exact_cycles_digest_collisions_and_digest_changes() {
    let mut history_storage = [0xa5_u8; 256];
    {
        let mut history =
            CursorHistory::new(&mut history_storage, 4).unwrap_or_else(|_| unreachable!());
        let mut first_source = *b"cursor-a";
        let mut first_storage = [0_u8; 16];
        let first =
            cursor(&mut first_source, &mut first_storage).unwrap_or_else(|_| unreachable!());
        let first_digest = CursorDigest::new([1; 32]);
        assert_eq!(history.observe(&first, first_digest), Ok(()));
        assert_eq!(history.entries(), 1);

        let mut repeated_source = *b"cursor-a";
        let mut repeated_storage = [0_u8; 16];
        let repeated =
            cursor(&mut repeated_source, &mut repeated_storage).unwrap_or_else(|_| unreachable!());
        assert_eq!(
            history.observe(&repeated, first_digest),
            Err(PaginationError::CursorCycle)
        );
        assert_eq!(
            history.observe(&repeated, CursorDigest::new([2; 32])),
            Err(PaginationError::CursorDigestChanged)
        );

        let mut collision_source = *b"cursor-b";
        let mut collision_storage = [0_u8; 16];
        let collision = cursor(&mut collision_source, &mut collision_storage)
            .unwrap_or_else(|_| unreachable!());
        assert_eq!(
            history.observe(&collision, first_digest),
            Err(PaginationError::CursorDigestCollision)
        );
        assert_eq!(history.entries(), 1);
    }
    assert_eq!(history_storage, [0; 256]);
}

#[test]
fn history_entry_and_byte_budgets_fail_without_appending() {
    let mut history_storage = [0_u8; 48];
    let mut history =
        CursorHistory::new(&mut history_storage, 1).unwrap_or_else(|_| unreachable!());
    let mut first_source = *b"first";
    let mut first_storage = [0_u8; 8];
    let first = cursor(&mut first_source, &mut first_storage).unwrap_or_else(|_| unreachable!());
    assert_eq!(history.observe(&first, CursorDigest::new([1; 32])), Ok(()));
    let mut second_source = *b"second";
    let mut second_storage = [0_u8; 8];
    let second = cursor(&mut second_source, &mut second_storage).unwrap_or_else(|_| unreachable!());
    assert_eq!(
        history.observe(&second, CursorDigest::new([2; 32])),
        Err(PaginationError::HistoryBudgetExceeded)
    );
    assert_eq!(history.entries(), 1);
}