ic_test/icp/
user.rs

1//! Represents a user (principal) in the PocketIC test environment, capable of deploying and
2//! interacting with Internet Computer (IC) canisters.
3//!
4//! This abstraction enables writing tests from the perspective of a particular identity.
5//! It implements both the [`Caller`] and [`Deployer`] traits for use in IC test scenarios.
6
7use std::{marker::PhantomData, sync::Arc};
8
9use candid::{CandidType, Principal};
10use ic_cdk::api::management_canister::main::CanisterSettings;
11use pocket_ic::nonblocking::PocketIc;
12use serde::Deserialize;
13
14use crate::{CallBuilder, CallMode, Caller, DeployBuilder, DeployMode, Deployer};
15
16/// A simulated Internet Computer user for use in tests.
17///
18/// Provides a simplified API for calling methods and deploying canisters.
19/// Wraps a `Principal` identity and a reference to the [`PocketIc`] test environment.
20#[derive(Clone)]
21pub struct IcpUser {
22    /// The user's principal identity used for calls and deployments.
23    pub principal: Principal,
24
25    /// Shared reference to the underlying PocketIC instance.
26    pub(crate) pic: Arc<PocketIc>,
27}
28
29impl IcpUser {
30    /// Prepare a canister method call builder for the user.
31    ///
32    /// # Parameters
33    /// - `canister_id`: The principal ID of the target canister.
34    /// - `call_mode`: Whether the call is a query or an update.
35    /// - `method`: The name of the method to invoke.
36    /// - `args`: Encoded Candid arguments or an error.
37    ///
38    /// # Returns
39    /// A [`CallBuilder`] for the specified method, result type, and context.    
40    pub fn call<ResultType>(
41        &self,
42        canister_id: Principal,
43        call_mode: CallMode,
44        method: &str,
45        args: Result<Vec<u8>, candid::error::Error>,
46    ) -> CallBuilder<ResultType, Self>
47    where
48        ResultType: for<'a> Deserialize<'a> + CandidType,
49    {
50        CallBuilder {
51            provider: self.clone(),
52            canister_id,
53            call_mode,
54            method: method.to_string(),
55            args,
56            _result: PhantomData,
57        }
58    }
59
60    /// Prepare a new canister deployment as this user.
61    ///
62    /// # Parameters
63    /// - `args`: Encoded Candid arguments for the canister constructor, or an error.
64    /// - `new`: A function to instantiate the canister wrapper after deployment.
65    ///
66    /// # Returns
67    /// A [`DeployBuilder`] for the specified canister.
68    pub fn deploy<Canister>(
69        &self,
70        args: Result<Vec<u8>, candid::error::Error>,
71        new: fn(&Self, Principal) -> Canister,
72    ) -> DeployBuilder<Canister, Self> {
73        DeployBuilder {
74            provider: self.clone(),
75            caller: self.clone(),
76            canister_id: None,
77            mode: DeployMode::Create,
78            settings: CanisterSettings::default(),
79            cycles: u64::MAX as u128,
80            wasm: vec![],
81            args,
82            new,
83        }
84    }
85}
86
87impl Caller for IcpUser {
88    type Provider = IcpUser;
89
90    /// Dispatch a call via this user, required by the [`Caller`] trait.
91    fn call<ResultType>(
92        &self,
93        canister_id: Principal,
94        call_mode: CallMode,
95        method: &str,
96        args: Result<Vec<u8>, candid::error::Error>,
97    ) -> CallBuilder<ResultType, Self::Provider>
98    where
99        ResultType: for<'a> Deserialize<'a> + CandidType,
100    {
101        IcpUser::call(self, canister_id, call_mode, method, args)
102    }
103}
104
105impl Deployer for IcpUser {
106    type Caller = IcpUser;
107
108    /// Dispatch a deployment via this user, required by the [`Deployer`] trait.
109    fn deploy<Canister>(
110        &self,
111        args: Result<Vec<u8>, candid::error::Error>,
112        new: fn(&Self::Caller, Principal) -> Canister,
113    ) -> DeployBuilder<Canister, Self::Caller> {
114        IcpUser::deploy(self, args, new)
115    }
116}