prikk-store 0.16.0

Prikk storage crate scaffold.
Documentation
use super::*;

#[test]
fn flat_sequences_with_only_commuting_cross_pairs_are_confluent() {
    let mut baseline = NodeLifecycleState::new();
    seed_binary(&mut baseline, node(1), "left.bin", blob(1), MODE_REGULAR);
    seed_binary(&mut baseline, node(2), "right.bin", blob(2), MODE_REGULAR);
    let left = [replace_binary(1, node(1), blob(1), blob(3))];
    let right = [change_perm(2, node(2), MODE_REGULAR, MODE_EXECUTABLE)];
    let evidence = TestTextResolver::empty().with_blob(blob(3), BlobKind::Binary, b"new".to_vec());

    match check_confluence_result(
        &baseline,
        &evidence,
        EvidenceScope::SealedCandidateRequired,
        &left,
        &right,
    )
    .expect("confluence evidence")
    {
        ConfluenceResult::Confluent { proof } => {
            assert_eq!(proof.left_len, 1);
            assert_eq!(proof.right_len, 1);
        }
        other => panic!("expected confluent, got {other:?}"),
    }
}

#[test]
fn cross_sequence_baseline_invalid_dependency_is_not_confluent() {
    let baseline = NodeLifecycleState::new();
    let left = [create_file(1, "fresh.bin", node(1), blob(1), MODE_REGULAR)];
    let right = [change_perm(2, node(1), MODE_REGULAR, MODE_EXECUTABLE)];
    let evidence =
        TestTextResolver::empty().with_blob(blob(1), BlobKind::Binary, b"fresh".to_vec());

    match check_confluence_result(
        &baseline,
        &evidence,
        EvidenceScope::UnsealedCandidateOptional,
        &left,
        &right,
    )
    .expect("confluence evidence")
    {
        ConfluenceResult::NotConfluent { witness } => {
            assert_eq!(witness.kind, ConfluenceWitnessKind::ReplayFailure);
            assert_eq!(witness.left_index, Some(0));
            assert_eq!(witness.right_index, None);
        }
        other => panic!("expected ordered not-confluent, got {other:?}"),
    }
}

#[test]
fn concrete_cross_sequence_conflict_is_not_confluent() {
    let mut baseline = NodeLifecycleState::new();
    seed_binary(&mut baseline, node(1), "tool", blob(1), MODE_REGULAR);
    let left = [change_perm(1, node(1), MODE_REGULAR, MODE_EXECUTABLE)];
    let right = [change_perm(2, node(1), MODE_REGULAR, 0o100600)];

    match check_confluence_result(
        &baseline,
        &TestTextResolver::empty(),
        EvidenceScope::SealedCandidateRequired,
        &left,
        &right,
    )
    .expect("confluence evidence")
    {
        ConfluenceResult::NotConfluent { witness } => {
            assert_eq!(witness.kind, ConfluenceWitnessKind::Conflict);
            assert_eq!(witness.left_index, Some(0));
            assert_eq!(witness.right_index, Some(0));
        }
        other => panic!("expected conflict not-confluent, got {other:?}"),
    }
}

#[test]
fn sequence_internal_dependency_is_deferred_before_cross_pair_checks() {
    let mut baseline = NodeLifecycleState::new();
    seed_binary(&mut baseline, node(9), "other.bin", blob(9), MODE_REGULAR);
    let left = [
        create_file(1, "fresh.bin", node(1), blob(1), MODE_REGULAR),
        change_perm(2, node(1), MODE_REGULAR, MODE_EXECUTABLE),
    ];
    let right = [replace_binary(3, node(9), blob(9), blob(10))];
    let evidence =
        TestTextResolver::empty().with_blob(blob(1), BlobKind::Binary, b"fresh".to_vec());

    match check_confluence_result(
        &baseline,
        &evidence,
        EvidenceScope::UnsealedCandidateOptional,
        &left,
        &right,
    )
    .expect("confluence evidence")
    {
        ConfluenceResult::Unknown { reason } => {
            assert_eq!(reason, UnknownReason::SequenceInternalDependencyDeferred);
        }
        other => panic!("expected internal dependency deferral, got {other:?}"),
    }
}

#[test]
fn unsupported_cross_sequence_relation_is_unknown() {
    let mut baseline = NodeLifecycleState::new();
    seed_binary(&mut baseline, node(1), "left.bin", blob(1), MODE_REGULAR);
    seed_binary(&mut baseline, node(2), "right.bin", blob(2), MODE_REGULAR);
    let left = [rename_path(1, node(1), "left.bin", "moved.bin")];
    let right = [replace_binary(2, node(2), blob(2), blob(3))];
    let evidence = TestTextResolver::empty().with_blob(blob(3), BlobKind::Binary, b"new".to_vec());

    match check_confluence_result(
        &baseline,
        &evidence,
        EvidenceScope::SealedCandidateRequired,
        &left,
        &right,
    )
    .expect("confluence evidence")
    {
        ConfluenceResult::Unknown { reason } => assert_eq!(reason, UnknownReason::RenameDeferred),
        other => panic!("expected rename deferral, got {other:?}"),
    }
}

#[test]
fn sealed_candidate_evidence_error_wins_over_earlier_unknown_sequence() {
    let mut baseline = NodeLifecycleState::new();
    seed_binary(&mut baseline, node(1), "left.bin", blob(1), MODE_REGULAR);
    seed_binary(&mut baseline, node(2), "right.bin", blob(2), MODE_REGULAR);
    let left = [rename_path(1, node(1), "left.bin", "moved.bin")];
    let right = [replace_binary(2, node(2), blob(2), blob(3))];

    match check_confluence_result(
        &baseline,
        &TestTextResolver::empty(),
        EvidenceScope::SealedCandidateRequired,
        &left,
        &right,
    ) {
        Err(EvidenceError::Missing {
            scope,
            fact,
            object_id,
            ..
        }) => {
            assert_eq!(scope, EvidenceScope::SealedCandidateRequired);
            assert_eq!(fact, EvidenceFact::BlobKind);
            assert_eq!(object_id, Some(blob(3)));
        }
        other => panic!("expected sealed replacement evidence error, got {other:?}"),
    }
}

#[test]
fn same_sequence_evidence_error_wins_over_earlier_unknown_operation() {
    let mut baseline = NodeLifecycleState::new();
    seed_binary(&mut baseline, node(1), "left.bin", blob(1), MODE_REGULAR);
    seed_binary(&mut baseline, node(2), "right.bin", blob(2), MODE_REGULAR);
    seed_binary(&mut baseline, node(4), "valid.bin", blob(4), MODE_REGULAR);
    let left = [
        rename_path(1, node(1), "left.bin", "moved.bin"),
        replace_binary(2, node(2), blob(2), blob(3)),
    ];
    let right = [change_perm(3, node(4), MODE_REGULAR, MODE_EXECUTABLE)];

    match check_confluence_result(
        &baseline,
        &TestTextResolver::empty(),
        EvidenceScope::SealedCandidateRequired,
        &left,
        &right,
    ) {
        Err(EvidenceError::Missing {
            scope,
            fact,
            object_id,
            ..
        }) => {
            assert_eq!(scope, EvidenceScope::SealedCandidateRequired);
            assert_eq!(fact, EvidenceFact::BlobKind);
            assert_eq!(object_id, Some(blob(3)));
        }
        other => panic!("expected same-sequence sealed evidence error, got {other:?}"),
    }
}

#[test]
fn right_same_sequence_evidence_error_wins_over_earlier_unknown_operation() {
    let mut baseline = NodeLifecycleState::new();
    seed_binary(&mut baseline, node(1), "left.bin", blob(1), MODE_REGULAR);
    seed_binary(&mut baseline, node(2), "right.bin", blob(2), MODE_REGULAR);
    seed_binary(&mut baseline, node(4), "valid.bin", blob(4), MODE_REGULAR);
    let left = [change_perm(3, node(4), MODE_REGULAR, MODE_EXECUTABLE)];
    let right = [
        rename_path(1, node(1), "left.bin", "moved.bin"),
        replace_binary(2, node(2), blob(2), blob(3)),
    ];

    match check_confluence_result(
        &baseline,
        &TestTextResolver::empty(),
        EvidenceScope::SealedCandidateRequired,
        &left,
        &right,
    ) {
        Err(EvidenceError::Missing {
            scope,
            fact: EvidenceFact::BlobKind,
            object_id,
            ..
        }) => {
            assert_eq!(scope, EvidenceScope::SealedCandidateRequired);
            assert_eq!(object_id, Some(blob(3)));
        }
        other => panic!("expected right same-sequence sealed evidence error, got {other:?}"),
    }
}