assertr/assertions/std/
hashmap.rs1use crate::{AssertThat, AssertrPartialEq, EqContext, Mode, tracking::AssertionTracking};
2use core::borrow::Borrow;
3use core::fmt::Debug;
4use core::fmt::Write;
5use indoc::writedoc;
6use std::{collections::HashMap, hash::Hash};
7
8pub trait HashMapAssertions<K, V> {
10 fn contains_key(self, expected: impl Borrow<K>) -> Self
11 where
12 K: Eq + Hash + Debug,
13 V: Debug;
14
15 fn does_not_contain_key(self, not_expected: impl Borrow<K>) -> Self
16 where
17 K: Eq + Hash + Debug,
18 V: Debug;
19
20 fn contains_value<E>(self, expected: E) -> Self
21 where
22 K: Debug,
23 V: AssertrPartialEq<E> + Debug,
24 E: Debug;
25
26 fn contains_entry<E>(self, key: impl Borrow<K>, value: impl Borrow<E>) -> Self
27 where
28 K: Eq + Hash + Debug,
29 V: AssertrPartialEq<E> + Debug,
30 E: Debug;
31}
32
33impl<K, V, M: Mode> HashMapAssertions<K, V> for AssertThat<'_, HashMap<K, V>, M> {
34 #[track_caller]
35 fn contains_key(self, expected: impl Borrow<K>) -> Self
36 where
37 K: Eq + Hash + Debug,
38 V: Debug,
39 {
40 self.track_assertion();
41
42 let expected = expected.borrow();
43
44 if !self.actual().contains_key(expected) {
45 self.fail(|w: &mut String| {
46 writedoc! {w, r#"
47 Actual: HashMap {actual:#?}
48
49 does not contain expected key: {expected:#?}
50 "#, actual = self.actual()}
51 });
52 }
53 self
54 }
55
56 #[track_caller]
57 fn does_not_contain_key(self, not_expected: impl Borrow<K>) -> Self
58 where
59 K: Eq + Hash + Debug,
60 V: Debug,
61 {
62 self.track_assertion();
63
64 let not_expected = not_expected.borrow();
65
66 if self.actual().contains_key(not_expected) {
67 self.fail(|w: &mut String| {
68 writedoc! {w, r#"
69 Actual: HashMap {actual:#?}
70
71 contains unexpected key: {not_expected:#?}
72 "#, actual = self.actual()}
73 });
74 }
75 self
76 }
77
78 #[track_caller]
79 fn contains_value<E>(self, expected: E) -> Self
80 where
81 K: Debug,
82 V: AssertrPartialEq<E> + Debug,
83 E: Debug,
84 {
85 self.track_assertion();
86
87 if !self
88 .actual()
89 .values()
90 .any(|it| AssertrPartialEq::eq(it, &expected, None))
91 {
92 self.fail(|w: &mut String| {
93 writedoc! {w, r#"
94 Actual: HashMap {actual:#?}
95
96 does not contain expected value: {expected:#?}
97 "#, actual = self.actual()}
98 });
99 }
100 self
101 }
102
103 #[track_caller]
104 fn contains_entry<E>(self, key: impl Borrow<K>, value: impl Borrow<E>) -> Self
105 where
106 K: Eq + Hash + Debug,
107 V: AssertrPartialEq<E> + Debug,
108 E: Debug,
109 {
110 let then = self.contains_key(key.borrow());
111
112 then.track_assertion();
113
114 let actual = then.actual();
115 let expected_key = key.borrow();
116 let expected_value = value.borrow();
117
118 match actual.get(expected_key) {
119 None => { }
120 Some(actual_value) => {
121 let mut ctx = EqContext::new();
122 if !AssertrPartialEq::eq(actual_value, expected_value, Some(&mut ctx)) {
123 if !ctx.differences.differences.is_empty() {
124 then.add_detail_message(format!("Differences: {:#?}", ctx.differences));
125 }
126 then.fail(|w: &mut String| {
127 writedoc! {w, r#"
128 Actual: HashMap {actual:#?}
129
130 does not contain expected value at key: {expected_key:#?}
131
132 Expected value: {expected_value:#?}
133 Actual value: {actual_value:#?}
134 "#,
135 }
136 });
137 }
138 }
139 }
140
141 then
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 mod contains_key {
148 use std::collections::HashMap;
149
150 use indoc::formatdoc;
151
152 use crate::prelude::*;
153
154 #[test]
155 fn succeeds_when_key_is_present() {
156 let mut map = HashMap::new();
157 map.insert("foo", "bar");
158 assert_that(map).contains_key("foo");
159 }
160
161 #[test]
162 fn panics_when_key_is_absent() {
163 assert_that_panic_by(|| {
164 let mut map = HashMap::new();
165 map.insert("foo", "bar");
166 assert_that(map).with_location(false).contains_key("baz");
167 })
168 .has_type::<String>()
169 .is_equal_to(formatdoc! {r#"
170 -------- assertr --------
171 Actual: HashMap {{
172 "foo": "bar",
173 }}
174
175 does not contain expected key: "baz"
176 -------- assertr --------
177 "#});
178 }
179 }
180
181 mod does_not_contain_key {
182 use std::collections::HashMap;
183
184 use indoc::formatdoc;
185
186 use crate::prelude::*;
187
188 #[test]
189 fn succeeds_when_key_is_absent() {
190 let mut map = HashMap::new();
191 map.insert("foo", "bar");
192 assert_that(map).does_not_contain_key("baz");
193 }
194
195 #[test]
196 fn panics_when_key_is_present() {
197 assert_that_panic_by(|| {
198 let mut map = HashMap::new();
199 map.insert("foo", "bar");
200 assert_that(map)
201 .with_location(false)
202 .does_not_contain_key("foo");
203 })
204 .has_type::<String>()
205 .is_equal_to(formatdoc! {r#"
206 -------- assertr --------
207 Actual: HashMap {{
208 "foo": "bar",
209 }}
210
211 contains unexpected key: "foo"
212 -------- assertr --------
213 "#});
214 }
215 }
216
217 mod contains_value {
218 use std::collections::HashMap;
219
220 use indoc::formatdoc;
221
222 use crate::prelude::*;
223
224 #[test]
225 fn succeeds_when_value_is_present() {
226 let mut map = HashMap::new();
227 map.insert("foo", "bar");
228 assert_that(map).contains_value("bar");
229 }
230
231 #[test]
232 fn panics_when_value_is_absent() {
233 assert_that_panic_by(|| {
234 let mut map = HashMap::new();
235 map.insert("foo", "bar");
236 assert_that(map).with_location(false).contains_value("baz");
237 })
238 .has_type::<String>()
239 .is_equal_to(formatdoc! {r#"
240 -------- assertr --------
241 Actual: HashMap {{
242 "foo": "bar",
243 }}
244
245 does not contain expected value: "baz"
246 -------- assertr --------
247 "#});
248 }
249
250 #[test]
251 fn compiles_with_any_type_comparable_to_the_actual_value_type() {
252 let mut map = HashMap::new();
253 map.insert("foo", "bar");
254 assert_that(map).contains_value("bar".to_string());
255 }
256 }
257
258 mod contains_entry {
259 use std::collections::HashMap;
260
261 use indoc::formatdoc;
262
263 use crate::prelude::*;
264
265 #[test]
266 fn succeeds_when_value_is_present() {
267 let mut map = HashMap::new();
268 map.insert("foo", "bar");
269 assert_that(map).contains_entry::<&str>("foo", "bar");
271 }
272
273 #[test]
274 fn succeeds_when_value_is_present_with_complex_type_with_borrowable_values() {
275 #[derive(Debug, PartialEq)]
276 struct Person {
277 age: u32,
278 }
279 let mut map = HashMap::<&str, Person>::new();
280 map.insert("foo", Person { age: 42 });
281 assert_that_ref(&map).contains_entry("foo", &Person { age: 42 });
282 assert_that_ref(&map).contains_entry("foo", Person { age: 42 });
283 assert_that_ref(&map).contains_entry("foo", Box::new(Person { age: 42 }));
284 }
285
286 #[test]
287 fn panics_when_key_is_absent() {
288 assert_that_panic_by(|| {
289 let mut map = HashMap::new();
290 map.insert("foo", "bar");
291 assert_that(map)
292 .with_location(false)
293 .contains_entry::<&str>("baz", "someValue");
294 })
295 .has_type::<String>()
296 .is_equal_to(formatdoc! {r#"
297 -------- assertr --------
298 Actual: HashMap {{
299 "foo": "bar",
300 }}
301
302 does not contain expected key: "baz"
303 -------- assertr --------
304 "#});
305 }
306
307 #[test]
308 fn panics_when_key_is_present_but_value_is_not_equal() {
309 assert_that_panic_by(|| {
310 let mut map = HashMap::new();
311 map.insert("foo", "bar");
312 assert_that(map)
313 .with_location(false)
314 .contains_entry::<&str>("foo", "someValue");
315 })
316 .has_type::<String>()
317 .is_equal_to(formatdoc! {r#"
318 -------- assertr --------
319 Actual: HashMap {{
320 "foo": "bar",
321 }}
322
323 does not contain expected value at key: "foo"
324
325 Expected value: "someValue"
326 Actual value: "bar"
327 -------- assertr --------
328 "#});
329 }
330 }
331}