1use std::fmt::Debug;
2
3use cosmwasm_std::{testing::mock_env, DepsMut, Env, MessageInfo, Order, Response, Storage};
4use cw_storage_plus::{KeyDeserialize, Map, PrimaryKey};
5use derive_builder::Builder;
6use serde::{de::DeserializeOwned, Serialize};
7use serde_json::json;
8
9use crate::MockDeps;
10
11#[derive(Builder)]
12#[builder(pattern = "owned")]
13pub struct CwMapTester<ExecMsg, TError, K, V, UncheckedK, UncheckedV>
14where
15 K: KeyDeserialize + Debug,
16 K::Output: 'static,
17{
18 info: MessageInfo,
19 map: Map<K, V>,
20 execute:
21 fn(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecMsg) -> Result<Response, TError>,
22 msg_builder: fn(to_add: Vec<(UncheckedK, UncheckedV)>, to_remove: Vec<UncheckedK>) -> ExecMsg,
23 mock_entry: (UncheckedK, UncheckedV),
24 from_checked_entry: fn((K::Output, V)) -> (UncheckedK, UncheckedV),
25}
26
27fn sort_expected<K, V>(expected: &mut [(K, V)])
29where
30 K: Clone + PartialEq + Debug + Serialize,
31 V: Clone + PartialEq + Debug,
32{
33 expected.sort_by(|a, b| json!(a.0).to_string().cmp(&json!(&b.0).to_string()));
34}
35
36#[allow(clippy::ptr_arg)]
37pub fn determine_expected<K, V>(to_add: &Vec<(K, V)>, to_remove: &[K]) -> Vec<(K, V)>
38where
39 K: Clone + PartialEq + Debug + Serialize,
40 V: Clone + PartialEq + Debug,
41{
42 let mut expected = to_add.clone();
43 expected.retain(|(k, _)| !to_remove.contains(k));
44 sort_expected(&mut expected);
45 expected.dedup();
46 expected
47}
48
49impl<'a, ExecMsg, TError, K, V, UncheckedK, UncheckedV>
50 CwMapTester<ExecMsg, TError, K, V, UncheckedK, UncheckedV>
51where
52 V: Serialize + DeserializeOwned + Clone + Debug,
53 K: PrimaryKey<'a> + KeyDeserialize + Debug,
54 (&'a <K as KeyDeserialize>::Output, V): PartialEq<(K, V)>,
55 K::Output: 'static,
56 UncheckedK: Clone + PartialEq + Debug + Serialize,
57 UncheckedV: Clone + PartialEq + Debug,
58 <K as KeyDeserialize>::Output: Debug,
59{
60 pub fn new(
61 info: MessageInfo,
62 map: Map<K, V>,
63 execute: fn(
64 deps: DepsMut,
65 env: Env,
66 info: MessageInfo,
67 msg: ExecMsg,
68 ) -> Result<Response, TError>,
69 msg_builder: fn(
70 to_add: Vec<(UncheckedK, UncheckedV)>,
71 to_remove: Vec<UncheckedK>,
72 ) -> ExecMsg,
73 mock_entry: (UncheckedK, UncheckedV),
74 from_checked_entry: fn((K::Output, V)) -> (UncheckedK, UncheckedV),
75 ) -> Self {
76 Self {
77 info,
78 map,
79 execute,
80 msg_builder,
81 mock_entry,
82 from_checked_entry,
83 }
84 }
85
86 pub fn msg_builder(
87 &self,
88 to_add: Vec<(UncheckedK, UncheckedV)>,
89 to_remove: Vec<UncheckedK>,
90 ) -> ExecMsg {
91 (self.msg_builder)(to_add, to_remove)
92 }
93
94 fn mock_entry_builder(&self) -> (UncheckedK, UncheckedV) {
95 self.mock_entry.clone()
96 }
97
98 pub fn execute(&mut self, deps: DepsMut, msg: ExecMsg) -> Result<(), TError> {
100 (self.execute)(deps, mock_env(), self.info.clone(), msg)?;
101 Ok(())
102 }
103
104 pub fn execute_update(
105 &mut self,
106 deps: DepsMut,
107 (to_add, to_remove): (Vec<(UncheckedK, UncheckedV)>, Vec<UncheckedK>),
108 ) -> Result<(), TError> {
109 let msg = self.msg_builder(to_add, to_remove);
110 self.execute(deps, msg)
111 }
112
113 #[allow(clippy::wrong_self_convention)]
114 fn from_checked_entry(&self, entry: (K::Output, V)) -> (UncheckedK, UncheckedV) {
115 (self.from_checked_entry)(entry)
116 }
117
118 pub fn assert_expected_entries(
119 &self,
120 storage: &'_ dyn Storage,
121 expected: Vec<(UncheckedK, UncheckedV)>,
122 ) {
123 let res: Result<Vec<(K::Output, V)>, _> = self
124 .map
125 .range(storage, None, None, Order::Ascending)
126 .collect();
127
128 let actual = res
129 .unwrap()
130 .into_iter()
131 .map(|(k, v)| self.from_checked_entry((k, v)))
132 .collect::<Vec<_>>();
133
134 let mut expected = expected;
136 sort_expected(&mut expected);
137
138 assert_eq!(actual, expected)
139 }
140
141 pub fn test_add_one(&mut self, deps: &mut MockDeps) -> Result<(), TError> {
142 let entry = self.mock_entry_builder();
143
144 let to_add: Vec<(UncheckedK, UncheckedV)> = vec![entry];
145 let to_remove: Vec<UncheckedK> = vec![];
146 let msg = self.msg_builder(to_add.clone(), to_remove.clone());
147
148 let expected = determine_expected(&to_add, &to_remove);
149
150 self.execute(deps.as_mut(), msg)?;
151
152 self.assert_expected_entries(&deps.storage, expected);
153
154 Ok(())
155 }
156
157 pub fn test_add_one_twice(&mut self, deps: &mut MockDeps) -> Result<(), TError> {
158 self.test_add_one(deps)?;
159 self.test_add_one(deps)
160 }
161
162 pub fn test_add_two_same(&mut self, deps: &mut MockDeps) -> Result<(), TError> {
163 let entry = self.mock_entry_builder();
164
165 let to_add: Vec<(UncheckedK, UncheckedV)> = vec![entry.clone(), entry];
166 let to_remove: Vec<UncheckedK> = vec![];
167 let msg = self.msg_builder(to_add.clone(), to_remove.clone());
168
169 let expected: Vec<(UncheckedK, UncheckedV)> = determine_expected(&to_add, &to_remove);
170
171 self.execute(deps.as_mut(), msg)?;
172
173 self.assert_expected_entries(&deps.storage, expected);
174
175 Ok(())
176 }
177
178 pub fn test_add_and_remove_same(&mut self, deps: &mut MockDeps) -> Result<(), TError> {
179 let entry = self.mock_entry_builder();
180
181 let to_add: Vec<(UncheckedK, UncheckedV)> = vec![entry.clone()];
182 let to_remove: Vec<UncheckedK> = vec![entry.0];
183 let msg = self.msg_builder(to_add, to_remove);
184
185 let expected: Vec<(UncheckedK, UncheckedV)> = vec![];
186
187 self.execute(deps.as_mut(), msg)?;
188
189 self.assert_expected_entries(&deps.storage, expected);
190
191 Ok(())
192 }
193
194 pub fn test_remove_nonexistent(&mut self, deps: &mut MockDeps) -> Result<(), TError> {
195 let entry = self.mock_entry_builder();
196
197 let to_add: Vec<(UncheckedK, UncheckedV)> = vec![];
198 let to_remove: Vec<UncheckedK> = vec![entry.0];
199 let msg = self.msg_builder(to_add, to_remove);
200
201 let expected: Vec<(UncheckedK, UncheckedV)> = vec![];
202
203 self.execute(deps.as_mut(), msg)?;
204
205 self.assert_expected_entries(&deps.storage, expected);
206
207 Ok(())
208 }
209
210 pub fn test_all(&mut self, deps: &mut MockDeps) -> Result<(), TError> {
212 self.test_add_one(deps)?;
213 self.test_add_one_twice(deps)?;
214 self.test_add_two_same(deps)?;
215 self.test_add_and_remove_same(deps)?;
216 self.test_remove_nonexistent(deps)?;
217
218 Ok(())
219 }
220
221 pub fn test_update_auto_expect(
223 &mut self,
224 deps: &mut MockDeps,
225 update: (Vec<(UncheckedK, UncheckedV)>, Vec<UncheckedK>),
226 ) -> Result<(), TError> {
227 let (to_add, to_remove) = update;
228 let msg = self.msg_builder(to_add.clone(), to_remove.clone());
229
230 let expected: Vec<(UncheckedK, UncheckedV)> = determine_expected(&to_add, &to_remove);
231
232 self.execute(deps.as_mut(), msg)?;
233
234 self.assert_expected_entries(&deps.storage, expected);
235
236 Ok(())
237 }
238
239 pub fn test_update_with_expected(
241 &mut self,
242 deps: &mut MockDeps,
243 update: (Vec<(UncheckedK, UncheckedV)>, Vec<UncheckedK>),
244 expected: Vec<(UncheckedK, UncheckedV)>,
245 ) -> Result<(), TError> {
246 let (to_add, to_remove) = update;
247 let msg = self.msg_builder(to_add, to_remove);
248
249 self.execute(deps.as_mut(), msg)?;
250
251 self.assert_expected_entries(&deps.storage, expected);
252
253 Ok(())
254 }
255}
256
257#[cfg(test)]
258mod test {
259 #![allow(clippy::needless_borrows_for_generic_args)]
260 use super::*;
261
262 mod determine_expected {
263 use super::*;
264
265 #[test]
266 fn removes_in_to_add() {
267 let to_add = vec![("a".to_string(), 1), ("b".to_string(), 2)];
268 let to_remove = vec!["a".to_string()];
269
270 let expected = vec![("b".to_string(), 2)];
271
272 assert_eq!(determine_expected(&to_add, &to_remove), expected);
273 }
274
275 #[test]
276 fn removes_all() {
277 let to_add = vec![("a".to_string(), 1), ("b".to_string(), 2)];
278 let to_remove = vec!["a".to_string(), "b".to_string()];
279
280 let expected: Vec<(String, i32)> = vec![];
281
282 assert_eq!(determine_expected(&to_add, &to_remove), expected);
283 }
284
285 #[test]
286 fn empty() {
287 let to_add: Vec<(String, i32)> = vec![];
288 let to_remove: Vec<String> = vec![];
289
290 let expected: Vec<(String, i32)> = vec![];
291
292 assert_eq!(determine_expected(&to_add, &to_remove), expected);
293 }
294 }
295
296 mod sort_expected {}
297}