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}