ic_test/
lib.rs

1//! # ic-test
2//!
3//! `ic-test` is a utility for building high-level integration tests for cross-chain projects
4//! on the Internet Computer (IC). It bridges IC and EVM environments by generating appropriate
5//! interfaces and managing runtime test orchestration.
6//!
7//! This crate supports:
8//! - Automatic handling of IC canisters and EVM contracts.
9//! - Simplified test writing using high-level APIs.
10//! - Optional EVM support via feature flag `"evm"`.
11//!
12
13#[cfg(feature = "evm")]
14use icp::http_outcalls::handle_http_outcalls;
15#[cfg(feature = "evm")]
16use std::sync::Arc;
17#[cfg(feature = "evm")]
18use tokio::task;
19
20use candid::{decode_one, encode_one, CandidType};
21
22use serde::Deserialize;
23
24mod icp;
25
26#[cfg(feature = "evm")]
27mod evm;
28#[cfg(feature = "evm")]
29pub use crate::evm::{Evm, EvmUser};
30
31pub use crate::{
32    icp::caller::{CallBuilder, CallError, CallMode, Caller},
33    icp::deployer::{DeployBuilder, DeployError, DeployMode, Deployer},
34    icp::user::IcpUser,
35    icp::Icp,
36};
37
38/// Helper structure combining test environments
39pub struct IcpTest {
40    /// Internet Computer environment for canister interaction.
41    pub icp: Icp,
42
43    /// EVM testing environment, only available when the `evm` feature is enabled.
44    #[cfg(feature = "evm")]
45    pub evm: Evm,
46}
47
48impl IcpTest {
49    /// Create a new `IcpTest` instance.
50    ///
51    /// Initializes the IC environment and, if the `evm` feature is enabled,
52    /// also spawns a background task to handle EVM outcalls via Pocket-IC.
53    pub async fn new() -> Self {
54        let result = Self {
55            icp: Icp::new().await,
56            #[cfg(feature = "evm")]
57            evm: Evm::new(),
58        };
59
60        #[cfg(feature = "evm")]
61        let pic = Arc::downgrade(&result.icp.pic);
62
63        #[cfg(feature = "evm")]
64        task::spawn(handle_http_outcalls(
65            pic,
66            result.evm.rpc_url(),
67            vec![result.evm.rpc_url().to_string()],
68        ));
69        result
70    }
71
72    /// Advance both the IC and EVM environments.
73    ///
74    /// - For IC, triggers a single tick cycle (e.g., canister heartbeat and timer).
75    /// - For EVM (if enabled), mines a new block.
76    pub async fn tick(&self) {
77        self.icp.tick().await;
78        #[cfg(feature = "evm")]
79        self.evm.mine_block().await;
80    }
81}
82
83/// Utility function to convert between types via Candid encoding/decoding.
84pub fn convert<F, T>(value: F) -> T
85where
86    F: CandidType,
87    T: for<'a> Deserialize<'a> + CandidType,
88{
89    decode_one(&encode_one(&value).unwrap()).unwrap()
90}