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}