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