canic-host 0.111.0

Host-side App build and desired-state Fleet reconciliation library for Canic workspaces
Documentation
use super::*;
use flate2::{Compression, write::GzEncoder};
use std::io::Write;

fn gzip(bytes: &[u8]) -> Vec<u8> {
    let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
    encoder.write_all(bytes).unwrap();
    encoder.finish().unwrap()
}

#[test]
fn qualified_facts_bind_both_exact_representations() {
    let wasm = b"\0asm\x01\0\0\0";
    let compressed = gzip(wasm);
    let facts = qualify_representation(wasm, &compressed).unwrap();
    assert_eq!(facts.wasm_size_bytes, wasm.len() as u64);
    assert_eq!(facts.wasm_gz_size_bytes, compressed.len() as u64);
    assert_eq!(facts.wasm_sha256_hex, sha256_hex(wasm));
    assert_eq!(facts.wasm_gz_sha256_hex, sha256_hex(&compressed));
}

#[test]
fn empty_invalid_and_truncated_representations_have_typed_failures() {
    let wasm = b"\0asm\x01\0\0\0";
    std::assert_matches!(
        qualify_representation(&[], &[]),
        Err(RepresentationError::EmptyArtifact { kind: "raw Wasm" })
    );
    std::assert_matches!(
        qualify_representation(b"invalid", &[]),
        Err(RepresentationError::InvalidWasm)
    );
    std::assert_matches!(
        qualify_representation(wasm, &[]),
        Err(RepresentationError::EmptyArtifact { kind: "gzip Wasm" })
    );
    std::assert_matches!(
        qualify_representation(wasm, b"invalid"),
        Err(RepresentationError::InvalidGzip { .. })
    );
    let mut compressed = gzip(wasm);
    compressed.truncate(compressed.len() / 2);
    std::assert_matches!(
        qualify_representation(wasm, &compressed),
        Err(RepresentationError::InvalidGzip { .. })
    );
}

#[test]
fn decoded_difference_and_expansion_beyond_raw_size_are_rejected() {
    let wasm = b"\0asm\x01\0\0\0";
    for different in [b"different".to_vec(), vec![0; 1_000_000]] {
        std::assert_matches!(
            qualify_representation(wasm, &gzip(&different)),
            Err(RepresentationError::RepresentationMismatch)
        );
    }
}

#[test]
fn shorter_and_equal_length_differences_are_mismatches() {
    let wasm = b"\0asm\x01\0\0\0";
    for different in [b"short".as_slice(), b"\0asm\x01\0\0\x01".as_slice()] {
        std::assert_matches!(
            qualify_representation(wasm, &gzip(different)),
            Err(RepresentationError::RepresentationMismatch)
        );
    }
}

#[test]
fn a_content_mismatch_does_not_hide_a_corrupt_trailer() {
    let wasm = b"\0asm\x01\0\0\0";
    let mut compressed = gzip(b"\0asm\x01\0\0\x01");
    let crc_offset = compressed.len() - 8;
    compressed[crc_offset] ^= 1;
    std::assert_matches!(
        qualify_representation(wasm, &compressed),
        Err(RepresentationError::InvalidGzip { .. })
    );
}

#[test]
fn exact_raw_match_does_not_admit_trailing_data_or_an_extra_member() {
    let wasm = b"\0asm\x01\0\0\0";
    for trailing in [b"trailing".to_vec(), gzip(&[])] {
        let mut compressed = gzip(wasm);
        compressed.extend(trailing);
        std::assert_matches!(
            qualify_representation(wasm, &compressed),
            Err(RepresentationError::InvalidGzip { .. })
        );
    }
}