ic-testkit 0.76.0

PocketIC-oriented test utilities for IC canister tests
Documentation
use super::*;
use ic_host_artifacts::wasm::{InspectionLimits, inspect, validate};

const LIMITS: RawReplyLimits = RawReplyLimits {
    methods: 2,
    reply_bytes: 65_537,
    method_name_bytes: 100,
};

#[test]
fn exports_both_modes_preserving_raw_binary_data_and_page_boundaries() {
    let reply = vec![0xff; 65_537];
    let methods = [
        RawReplyMethod {
            name: "read\"\\é",
            mode: ReplyMode::Query,
            reply: &reply,
        },
        RawReplyMethod {
            name: "read",
            mode: ReplyMode::Update,
            reply: b"not candid\0",
        },
    ];
    let wasm = raw_reply_canister(
        &methods,
        RawReplyLimits {
            reply_bytes: reply.len() + 11,
            ..LIMITS
        },
    )
    .unwrap();
    validate(&wasm, 100_000).unwrap();
    let facts = inspect(
        &wasm,
        InspectionLimits {
            module_bytes: 100_000,
            sections: 8,
            exports: 3,
            custom_sections: 0,
        },
    )
    .unwrap();
    assert!(facts.exports.contains_key("canister_query read\"\\é"));
    assert!(facts.exports.contains_key("canister_update read"));
    assert_eq!(facts.defined_functions, 2);
    assert_eq!(facts.imported_functions, 2);
    assert_eq!(facts.data_segments, 2);
    assert!(wasm.windows(reply.len()).any(|bytes| bytes == reply));
    assert!(wasm.windows(11).any(|bytes| bytes == b"not candid\0"));
}

#[test]
fn permits_empty_replies_and_an_empty_catalog() {
    let methods = [
        RawReplyMethod {
            name: "same",
            mode: ReplyMode::Query,
            reply: &[],
        },
        RawReplyMethod {
            name: "other",
            mode: ReplyMode::Update,
            reply: &[],
        },
    ];
    for selected in [&methods[..], &[][..]] {
        let wasm = raw_reply_canister(selected, LIMITS).unwrap();
        validate(&wasm, 100_000).unwrap();
        inspect(
            &wasm,
            InspectionLimits {
                module_bytes: 100_000,
                sections: 8,
                exports: 3,
                custom_sections: 0,
            },
        )
        .unwrap();
    }
}

#[test]
fn rejects_names_duplicates_and_each_aggregate_limit() {
    let method = RawReplyMethod {
        name: "method",
        mode: ReplyMode::Update,
        reply: b"raw",
    };
    assert_eq!(
        raw_reply_canister(&[method, method], LIMITS),
        Err(RawReplyError::DuplicateExport { index: 1 })
    );
    for name in ["", "bad\0name", "bad\nname"] {
        assert!(matches!(
            raw_reply_canister(&[RawReplyMethod { name, ..method }], LIMITS),
            Err(RawReplyError::InvalidName { index: 0 })
        ));
    }
    for limits in [
        RawReplyLimits {
            methods: 0,
            ..LIMITS
        },
        RawReplyLimits {
            reply_bytes: 2,
            ..LIMITS
        },
        RawReplyLimits {
            method_name_bytes: 5,
            ..LIMITS
        },
    ] {
        assert!(matches!(
            raw_reply_canister(&[method], limits),
            Err(RawReplyError::Budget { .. })
        ));
    }
    let pair = [
        method,
        RawReplyMethod {
            mode: ReplyMode::Query,
            ..method
        },
    ];
    assert_eq!(
        raw_reply_canister(&pair, LIMITS),
        Err(RawReplyError::DuplicateExport { index: 1 })
    );
    let pair = [
        method,
        RawReplyMethod {
            name: "second",
            mode: ReplyMode::Query,
            ..method
        },
    ];
    for limits in [
        RawReplyLimits {
            reply_bytes: 5,
            ..LIMITS
        },
        RawReplyLimits {
            method_name_bytes: 11,
            ..LIMITS
        },
    ] {
        assert!(matches!(
            raw_reply_canister(&pair, limits),
            Err(RawReplyError::Budget { .. })
        ));
    }
    assert!(matches!(
        charge(usize::MAX, 1, usize::MAX, "test"),
        Err(RawReplyError::Budget { .. })
    ));
}