Skip to main content

ic_testkit/pic/
standalone.rs

1use candid::{CandidType, Principal, utils::ArgumentEncoder};
2use serde::de::DeserializeOwned;
3
4use super::{
5    Pic, PicCallError, PicSerialGuard, StandaloneCanisterFixtureError,
6    try_acquire_pic_serial_guard, try_pic,
7};
8
9const DEFAULT_EXTRA_INSTALL_CYCLES: u128 = 0;
10
11///
12/// StandaloneCanisterFixture
13///
14
15pub struct StandaloneCanisterFixture {
16    pic: Pic,
17    canister_id: Principal,
18    _serial_guard: PicSerialGuard,
19}
20
21impl StandaloneCanisterFixture {
22    /// Borrow the PocketIC instance that owns this standalone fixture.
23    #[must_use]
24    pub const fn pic(&self) -> &Pic {
25        &self.pic
26    }
27
28    /// Mutably borrow the PocketIC instance that owns this standalone fixture.
29    #[must_use]
30    pub const fn pic_mut(&mut self) -> &mut Pic {
31        &mut self.pic
32    }
33
34    /// Read the installed canister id for this standalone fixture.
35    #[must_use]
36    pub const fn canister_id(&self) -> Principal {
37        self.canister_id
38    }
39
40    /// Consume the fixture and return the owned PocketIC instance and canister id.
41    #[must_use]
42    pub fn into_parts(self) -> (Pic, Principal) {
43        (self.pic, self.canister_id)
44    }
45
46    /// Forward one typed update call to this fixture's canister id.
47    pub fn update_call<T, A>(&self, method: &str, args: A) -> Result<T, PicCallError>
48    where
49        T: CandidType + DeserializeOwned,
50        A: ArgumentEncoder,
51    {
52        self.pic.update_call(self.canister_id, method, args)
53    }
54
55    /// Forward one typed update call to this fixture's canister id, panicking
56    /// on transport or Candid codec failure.
57    ///
58    /// This does not unwrap application-level results. For example,
59    /// `update_call_or_panic::<Result<T, E>, _>(...)` returns `Result<T, E>`.
60    #[track_caller]
61    pub fn update_call_or_panic<T, A>(&self, method: &str, args: A) -> T
62    where
63        T: CandidType + DeserializeOwned,
64        A: ArgumentEncoder,
65    {
66        self.pic
67            .update_call_or_panic(self.canister_id, method, args)
68    }
69
70    /// Forward one typed update call with an explicit caller to this fixture's canister id.
71    pub fn update_call_as<T, A>(
72        &self,
73        caller: Principal,
74        method: &str,
75        args: A,
76    ) -> Result<T, PicCallError>
77    where
78        T: CandidType + DeserializeOwned,
79        A: ArgumentEncoder,
80    {
81        self.pic
82            .update_call_as(self.canister_id, caller, method, args)
83    }
84
85    /// Forward one typed update call with an explicit caller to this fixture's
86    /// canister id, panicking on transport or Candid codec failure.
87    ///
88    /// This does not unwrap application-level results. For example,
89    /// `update_call_as_or_panic::<Result<T, E>, _>(...)` returns `Result<T, E>`.
90    #[track_caller]
91    pub fn update_call_as_or_panic<T, A>(&self, caller: Principal, method: &str, args: A) -> T
92    where
93        T: CandidType + DeserializeOwned,
94        A: ArgumentEncoder,
95    {
96        self.pic
97            .update_call_as_or_panic(self.canister_id, caller, method, args)
98    }
99
100    /// Forward one typed query call to this fixture's canister id.
101    pub fn query_call<T, A>(&self, method: &str, args: A) -> Result<T, PicCallError>
102    where
103        T: CandidType + DeserializeOwned,
104        A: ArgumentEncoder,
105    {
106        self.pic.query_call(self.canister_id, method, args)
107    }
108
109    /// Forward one typed query call to this fixture's canister id, panicking on
110    /// transport or Candid codec failure.
111    ///
112    /// This does not unwrap application-level results. For example,
113    /// `query_call_or_panic::<Result<T, E>, _>(...)` returns `Result<T, E>`.
114    #[track_caller]
115    pub fn query_call_or_panic<T, A>(&self, method: &str, args: A) -> T
116    where
117        T: CandidType + DeserializeOwned,
118        A: ArgumentEncoder,
119    {
120        self.pic.query_call_or_panic(self.canister_id, method, args)
121    }
122
123    /// Forward one typed query call with an explicit caller to this fixture's canister id.
124    pub fn query_call_as<T, A>(
125        &self,
126        caller: Principal,
127        method: &str,
128        args: A,
129    ) -> Result<T, PicCallError>
130    where
131        T: CandidType + DeserializeOwned,
132        A: ArgumentEncoder,
133    {
134        self.pic
135            .query_call_as(self.canister_id, caller, method, args)
136    }
137
138    /// Forward one typed query call with an explicit caller to this fixture's
139    /// canister id, panicking on transport or Candid codec failure.
140    ///
141    /// This does not unwrap application-level results. For example,
142    /// `query_call_as_or_panic::<Result<T, E>, _>(...)` returns `Result<T, E>`.
143    #[track_caller]
144    pub fn query_call_as_or_panic<T, A>(&self, caller: Principal, method: &str, args: A) -> T
145    where
146        T: CandidType + DeserializeOwned,
147        A: ArgumentEncoder,
148    {
149        self.pic
150            .query_call_as_or_panic(self.canister_id, caller, method, args)
151    }
152}
153
154// Install one already-built wasm module into a fresh PocketIC instance with
155// caller-provided init args and no application-specific bootstrap assumptions.
156#[must_use]
157pub fn install_prebuilt_canister(wasm: Vec<u8>, init_bytes: Vec<u8>) -> StandaloneCanisterFixture {
158    try_install_prebuilt_canister(wasm, init_bytes)
159        .unwrap_or_else(|err| panic!("failed to install prebuilt canister fixture: {err}"))
160}
161
162// Install one already-built wasm module into a fresh PocketIC instance with
163// caller-provided init args and no application-specific bootstrap assumptions.
164pub fn try_install_prebuilt_canister(
165    wasm: Vec<u8>,
166    init_bytes: Vec<u8>,
167) -> Result<StandaloneCanisterFixture, StandaloneCanisterFixtureError> {
168    try_install_prebuilt_canister_with_cycles(wasm, init_bytes, DEFAULT_EXTRA_INSTALL_CYCLES)
169}
170
171// Install one already-built wasm module into a fresh PocketIC instance with
172// caller-provided init args and explicit install cycles.
173#[must_use]
174pub fn install_prebuilt_canister_with_cycles(
175    wasm: Vec<u8>,
176    init_bytes: Vec<u8>,
177    install_cycles: u128,
178) -> StandaloneCanisterFixture {
179    try_install_prebuilt_canister_with_cycles(wasm, init_bytes, install_cycles)
180        .unwrap_or_else(|err| panic!("failed to install prebuilt canister fixture: {err}"))
181}
182
183// Install one already-built wasm module into a fresh PocketIC instance with
184// caller-provided init args and explicit install cycles.
185pub fn try_install_prebuilt_canister_with_cycles(
186    wasm: Vec<u8>,
187    init_bytes: Vec<u8>,
188    install_cycles: u128,
189) -> Result<StandaloneCanisterFixture, StandaloneCanisterFixtureError> {
190    let serial_guard =
191        try_acquire_pic_serial_guard().map_err(StandaloneCanisterFixtureError::SerialGuard)?;
192    let pic = try_pic().map_err(StandaloneCanisterFixtureError::Start)?;
193    let canister_id = pic
194        .try_create_and_install_with_args(wasm, init_bytes, install_cycles)
195        .map_err(StandaloneCanisterFixtureError::Install)?;
196
197    Ok(StandaloneCanisterFixture {
198        pic,
199        canister_id,
200        _serial_guard: serial_guard,
201    })
202}