ic-backup 0.58.6

Host-side snapshot backup and same-release recovery for Internet Computer canisters
Documentation
//! Native original plan admission, retained evidence and independent metadata bounds.

use super::*;
use crate::tests::support::operation_plan::request;
use crate::{
    model::{
        attempt_journal::AttemptBudgetRecord,
        effect_graph::{
            EffectGraphRecord, EffectNodeRecord, EffectNodeRequest, MAX_EFFECT_OPERATIONS,
        },
        operation_plan::{PlanBudgetRecord, PlannedOperationRecord, PlannedOperationRequest},
    },
    tests::support::temp_dir,
};
use std::fs;
fn plan() -> OperationPlanRecord {
    OperationPlanRecord::new(request()).expect("plan")
}
fn layout() -> (std::path::PathBuf, BackupLayoutGuard) {
    let root = temp_dir("ic-backup-operation-plan");
    let guard = BackupLayoutGuard::acquire(&root).expect("layout exclusion");
    (root, guard)
}

#[test]
fn maximum_model_count_and_independent_persistence_bounds_preserve_original_absence() {
    let (root, layout) = layout();
    let mut input = request();
    input.graph = EffectGraphRecord::new(
        (0..u64::try_from(MAX_EFFECT_OPERATIONS).expect("bound"))
            .map(|id| {
                EffectNodeRecord::new(EffectNodeRequest {
                    operation_sequence: id,
                    depends_on: vec![],
                })
                .expect("node")
            })
            .collect(),
    )
    .expect("maximum graph");
    input.operations = (0..u64::try_from(MAX_EFFECT_OPERATIONS).expect("bound"))
        .map(|id| {
            PlannedOperationRecord::new(PlannedOperationRequest {
                operation_sequence: id,
                target: input.selected_targets[0].clone(),
                request: "ef".repeat(32),
                budget: AttemptBudgetRecord::new(0, 0).expect("zero"),
            })
            .expect("operation")
        })
        .collect();
    input.budget = PlanBudgetRecord::new(0, 0).expect("zero ceilings");
    let excessive = OperationPlanRecord::new(input).expect("valid maximum table");
    assert_eq!(excessive.operations().len(), MAX_EFFECT_OPERATIONS);
    let compact = serde_json::to_vec(&excessive).expect("encode maximum");
    assert_eq!(
        serde_json::from_slice::<OperationPlanRecord>(&compact).expect("maximum decode"),
        excessive
    );
    assert!(matches!(
        create_operation_plan(&layout, &excessive),
        Err(OperationPlanPersistenceError::Persistence(
            PersistenceError::RecordTooLarge {
                limit: MAX_OPERATION_PLAN_BYTES
            }
        ))
    ));
    let path = root.join("operation-plan.json");
    assert!(!path.exists());
    create_operation_plan(&layout, &plan()).expect("absence retained");
    fs::write(
        &path,
        vec![b' '; usize::try_from(MAX_OPERATION_PLAN_BYTES).expect("bound") + 1],
    )
    .expect("oversized evidence");
    assert!(matches!(
        read_operation_plan(&layout, &plan().digest()),
        Err(OperationPlanPersistenceError::Persistence(
            PersistenceError::RecordTooLarge {
                limit: MAX_OPERATION_PLAN_BYTES
            }
        ))
    ));
    assert_eq!(
        fs::metadata(&path).expect("retained input").len(),
        MAX_OPERATION_PLAN_BYTES + 1
    );
    drop(layout);
    fs::remove_dir_all(root).expect("clean successful fixture");
}