Skip to main content

ic_testkit/pic/
calls.rs

1use candid::{CandidType, Principal, decode_one, encode_args, utils::ArgumentEncoder};
2use serde::de::DeserializeOwned;
3
4use super::{Pic, PicCallError};
5
6impl Pic {
7    /// Generic update call helper (serializes args + decodes result).
8    pub fn update_call<T, A>(
9        &self,
10        canister_id: Principal,
11        method: &str,
12        args: A,
13    ) -> Result<T, PicCallError>
14    where
15        T: CandidType + DeserializeOwned,
16        A: ArgumentEncoder,
17    {
18        self.update_call_as(canister_id, Principal::anonymous(), method, args)
19    }
20
21    /// Generic update call helper with an explicit caller principal.
22    pub fn update_call_as<T, A>(
23        &self,
24        canister_id: Principal,
25        caller: Principal,
26        method: &str,
27        args: A,
28    ) -> Result<T, PicCallError>
29    where
30        T: CandidType + DeserializeOwned,
31        A: ArgumentEncoder,
32    {
33        let bytes = encode_call_args(args)?;
34        let result = self
35            .inner
36            .update_call(canister_id, caller, method, bytes)
37            .map_err(|err| {
38                PicCallError::new(format!(
39                    "pocket_ic update_call failed (canister={canister_id}, method={method}): {err}"
40                ))
41            })?;
42
43        decode_call_result(&result)
44    }
45
46    /// Generic query call helper.
47    pub fn query_call<T, A>(
48        &self,
49        canister_id: Principal,
50        method: &str,
51        args: A,
52    ) -> Result<T, PicCallError>
53    where
54        T: CandidType + DeserializeOwned,
55        A: ArgumentEncoder,
56    {
57        self.query_call_as(canister_id, Principal::anonymous(), method, args)
58    }
59
60    /// Generic query call helper with an explicit caller principal.
61    pub fn query_call_as<T, A>(
62        &self,
63        canister_id: Principal,
64        caller: Principal,
65        method: &str,
66        args: A,
67    ) -> Result<T, PicCallError>
68    where
69        T: CandidType + DeserializeOwned,
70        A: ArgumentEncoder,
71    {
72        let bytes = encode_call_args(args)?;
73        let result = self
74            .inner
75            .query_call(canister_id, caller, method, bytes)
76            .map_err(|err| {
77                PicCallError::new(format!(
78                    "pocket_ic query_call failed (canister={canister_id}, method={method}): {err}"
79                ))
80            })?;
81
82        decode_call_result(&result)
83    }
84
85    /// Advance PocketIC by a fixed number of ticks.
86    pub fn tick_n(&self, times: usize) {
87        for _ in 0..times {
88            self.tick();
89        }
90    }
91}
92
93fn encode_call_args<A>(args: A) -> Result<Vec<u8>, PicCallError>
94where
95    A: ArgumentEncoder,
96{
97    encode_args(args).map_err(|err| PicCallError::new(format!("encode_args failed: {err}")))
98}
99
100fn decode_call_result<T>(result: &[u8]) -> Result<T, PicCallError>
101where
102    T: CandidType + DeserializeOwned,
103{
104    decode_one(result).map_err(|err| PicCallError::new(format!("decode_one failed: {err}")))
105}