ic-build 0.18.0

Cargo and Wasm builds, exact inputs and retained artifact caches
Documentation
use super::*;
use crate::artifacts::test_support::fake_wasm_build_spec;

#[test]
fn export_preserves_exact_bytes_and_refuses_limits_and_changed_source() {
    let (root, spec) = fake_wasm_build_spec("wasm-evidence");
    let evidence = WasmBuildEvidence::acquire(
        spec.clone(),
        ic_host_process::child::OwnedChild::spawn_direct,
    )
    .unwrap();
    let destination = root.join("evidence.wasm");
    fs::write(&destination, b"previous").unwrap();
    assert!(evidence.export_artifact(0, &destination, 7).is_err());
    assert_eq!(fs::read(&destination).unwrap(), b"previous");
    assert!(evidence.export_artifact(1, &destination, 8).is_err());
    let receipt = evidence.export_artifact(0, &destination, 8).unwrap();
    assert_eq!(fs::read(&destination).unwrap(), b"\0asm\x01\0\0\0");
    assert_eq!(
        receipt.wasm_sha256,
        hash_reader(fs::File::open(&destination).unwrap(), 8)
            .unwrap()
            .sha256
            .to_string()
    );
    assert!(
        evidence
            .export_artifact(0, &evidence.build.record().artifacts()[0], 8)
            .is_err()
    );
    let reused =
        WasmBuildEvidence::acquire(spec, ic_host_process::child::OwnedChild::spawn_direct).unwrap();
    let second = reused.export_artifact(0, &destination, 8).unwrap();
    assert_eq!(receipt.build_fingerprint, second.build_fingerprint);
    let blocked = root.join("blocked");
    fs::create_dir(&blocked).unwrap();
    let error = reused.export_artifact(0, &blocked, 8).unwrap_err();
    let recovery = error
        .get_ref()
        .unwrap()
        .downcast_ref::<crate::artifacts::CaptureRetentionError>()
        .unwrap();
    assert_eq!(
        fs::read(&recovery.retained_path).unwrap(),
        b"\0asm\x01\0\0\0"
    );
    drop(reused);
    fs::write(root.join("fixture/src/lib.rs"), "pub fn changed() {}\n").unwrap();
    assert!(evidence.verify_current().is_err());
    assert!(evidence.export_artifact(0, &destination, 8).is_err());
    assert_eq!(fs::read(&destination).unwrap(), b"\0asm\x01\0\0\0");
    drop(evidence);
    fs::remove_dir_all(root).unwrap();
}

#[test]
fn raw_input_changes_and_mismatched_recipe_fingerprints_refuse_evidence() {
    let (root, spec) = fake_wasm_build_spec("evidence-raw-inputs");
    let mut evidence = WasmBuildEvidence::acquire(
        spec.clone(),
        ic_host_process::child::OwnedChild::spawn_direct,
    )
    .unwrap();
    let original = fs::read(root.join("Cargo.toml")).unwrap();
    let mut changed = original.clone();
    changed.extend_from_slice(b"\n# unrelated workspace edit\n");
    fs::write(root.join("Cargo.toml"), changed).unwrap();
    let current = resolve_cargo_build_inputs(&spec).unwrap();
    assert_eq!(current.fingerprint(), evidence.inputs.fingerprint());
    assert_ne!(
        current.validation_digest(),
        evidence.inputs.validation_digest()
    );
    assert!(evidence.verify_current().is_err());
    fs::write(root.join("Cargo.toml"), original).unwrap();
    let alternate = spec.with_environment(crate::cargo_fixture::environment_with([(
        "RECIPE_MARKER",
        "changed",
    )]));
    let other =
        build_wasm_canisters_cached(&alternate, ic_host_process::child::OwnedChild::spawn_direct)
            .unwrap();
    assert_eq!(
        other.record().input_digest(),
        evidence.build.record().input_digest()
    );
    assert_ne!(
        other.record().fingerprint(),
        evidence.build.record().fingerprint()
    );
    evidence.build = other;
    assert!(evidence.verify_current().is_err());
    drop(evidence);
    fs::remove_dir_all(root).unwrap();
}