ic_test/
lib.rs

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