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}