Skip to main content

ic_testkit/pic/
standalone.rs

1use candid::{CandidType, Principal, utils::ArgumentEncoder};
2use serde::de::DeserializeOwned;
3
4use super::{
5    InstallSpec, 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_from_spec(InstallSpec::new(
169        wasm,
170        init_bytes,
171        DEFAULT_EXTRA_INSTALL_CYCLES,
172    ))
173}
174
175// Install one already-built wasm module into a fresh PocketIC instance with
176// caller-provided init args and explicit install cycles.
177#[must_use]
178pub fn install_prebuilt_canister_with_cycles(
179    wasm: Vec<u8>,
180    init_bytes: Vec<u8>,
181    install_cycles: u128,
182) -> StandaloneCanisterFixture {
183    try_install_prebuilt_canister_with_cycles(wasm, init_bytes, install_cycles)
184        .unwrap_or_else(|err| panic!("failed to install prebuilt canister fixture: {err}"))
185}
186
187// Install one already-built wasm module into a fresh PocketIC instance with
188// caller-provided init args and explicit install cycles.
189pub fn try_install_prebuilt_canister_with_cycles(
190    wasm: Vec<u8>,
191    init_bytes: Vec<u8>,
192    install_cycles: u128,
193) -> Result<StandaloneCanisterFixture, StandaloneCanisterFixtureError> {
194    try_install_prebuilt_canister_from_spec(InstallSpec::new(wasm, init_bytes, install_cycles))
195}
196
197// Install one already-built wasm module from a generic install specification
198// into a fresh PocketIC instance.
199#[must_use]
200pub fn install_prebuilt_canister_from_spec(spec: InstallSpec) -> StandaloneCanisterFixture {
201    try_install_prebuilt_canister_from_spec(spec)
202        .unwrap_or_else(|err| panic!("failed to install prebuilt canister fixture: {err}"))
203}
204
205// Install one already-built wasm module from a generic install specification
206// into a fresh PocketIC instance.
207pub fn try_install_prebuilt_canister_from_spec(
208    spec: InstallSpec,
209) -> Result<StandaloneCanisterFixture, StandaloneCanisterFixtureError> {
210    let serial_guard =
211        try_acquire_pic_serial_guard().map_err(StandaloneCanisterFixtureError::SerialGuard)?;
212    let pic = try_pic().map_err(StandaloneCanisterFixtureError::Start)?;
213    let canister_id = pic
214        .try_create_and_install(spec)
215        .map_err(StandaloneCanisterFixtureError::Install)?;
216
217    Ok(StandaloneCanisterFixture {
218        pic,
219        canister_id,
220        _serial_guard: serial_guard,
221    })
222}