assertor/assertions/
map.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::borrow::Borrow;
16use std::fmt::{Debug, Formatter};
17use std::hash::Hash;
18
19use crate::assertions::basic::EqualityAssertion;
20use crate::assertions::iterator::{
21    check_contains, check_does_not_contain, check_is_empty, check_is_not_empty,
22};
23use crate::base::{AssertionApi, AssertionResult, AssertionStrategy, Subject};
24use crate::diff::iter::SequenceOrderComparison;
25use crate::diff::map::{MapComparison, MapLike, MapValueDiff, OrderedMapLike};
26
27/// Trait for map assertion.
28///
29/// # Example
30/// ```
31/// use std::collections::HashMap;
32/// use assertor::*;
33///
34/// let mut map = HashMap::new();
35/// assert_that!(map).is_empty();
36///
37/// map.insert("one", 1);
38/// map.insert("two", 2);
39/// map.insert("three", 3);
40///
41/// assert_that!(map).has_length(3);
42/// assert_that!(map).contains_key("one");
43/// assert_that!(map).key_set().contains_exactly(vec!["three","two","one"].iter());
44/// ```
45pub trait MapAssertion<'a, K: 'a + Eq, V, ML, R>
46where
47    AssertionResult: AssertionStrategy<R>,
48    ML: MapLike<K, V>,
49{
50    /// Checks that the subject has the given length.
51    #[track_caller]
52    fn has_length(&self, length: usize) -> R;
53
54    /// Checks that the subject is empty.
55    #[track_caller]
56    fn is_empty(&self) -> R
57    where
58        K: Debug;
59
60    /// Checks that the subject is not empty.
61    #[track_caller]
62    fn is_not_empty(&self) -> R
63    where
64        K: Debug;
65
66    /// Checks that the subject has the given `key`.
67    #[track_caller]
68    fn contains_key<BK>(&self, key: BK) -> R
69    where
70        BK: Borrow<K>,
71        K: Eq + Hash + Debug;
72
73    /// Checks that the subject does not have the given `key`.
74    #[track_caller]
75    fn does_not_contain_key<BK>(&self, key: BK) -> R
76    where
77        BK: Borrow<K>,
78        K: Eq + Hash + Debug;
79
80    /// Checks that the subject has entry with the given `key` and `value`.
81    #[track_caller]
82    fn contains_entry<BK, BV>(&self, key: BK, value: BV) -> R
83    where
84        BK: Borrow<K>,
85        BV: Borrow<V>,
86        K: Eq + Hash + Debug,
87        V: Eq + Debug;
88
89    /// Checks that the subject does not contain entry with the given `key` and `value`.
90    #[track_caller]
91    fn does_not_contain_entry<BK, BV>(&self, key: BK, value: BV) -> R
92    where
93        BK: Borrow<K>,
94        BV: Borrow<V>,
95        K: Eq + Hash + Debug,
96        V: Eq + Debug;
97
98    /// Checks that the subject contains all entries from `expected`.
99    #[track_caller]
100    fn contains_at_least<BM: 'a, OML: 'a>(&self, expected: BM) -> R
101    where
102        K: Eq + Hash + Debug,
103        V: Eq + Debug,
104        OML: MapLike<K, V> + 'a,
105        BM: Borrow<OML> + 'a;
106
107    /// Checks that the subject does not contain any entries from `expected`.
108    #[track_caller]
109    fn does_not_contain_any<BM: 'a, OML: 'a>(&self, expected: BM) -> R
110    where
111        K: Eq + Hash + Debug,
112        V: Eq + Debug,
113        OML: MapLike<K, V> + 'a,
114        BM: Borrow<OML> + 'a;
115
116    /// Checks that the subject contains only entries from `expected`.
117    #[track_caller]
118    fn contains_exactly<BM, OML>(&self, expected: BM) -> R
119    where
120        K: Eq + Hash + Debug,
121        V: Eq + Debug,
122        OML: MapLike<K, V> + 'a,
123        BM: Borrow<OML> + 'a;
124
125    /// Returns a new subject which is an key set of the subject and which implements
126    /// [`crate::IteratorAssertion`].
127    ///
128    /// # Example
129    /// ```
130    /// use std::collections::HashMap;
131    /// use assertor::*;
132    /// use assertor::IteratorAssertion;
133    ///
134    /// let mut map = HashMap::new();
135    /// map.insert("one", 1);
136    /// map.insert("two", 2);
137    /// map.insert("three", 3);
138    ///
139    /// assert_that!(map).key_set().contains(&"one");
140    /// assert_that!(map).key_set().contains_exactly(vec!["three","two","one"].iter());
141    /// assert_that!(map).key_set().contains_all_of(vec!["one", "two"].iter());
142    /// ```
143    fn key_set<'b>(&'b self) -> Subject<ML::It<'b>, (), R>
144    where
145        K: 'b;
146}
147
148/// Trait for ordered map assertion.
149///
150/// # Example
151/// ```
152/// use std::collections::BTreeMap;
153/// use assertor::*;
154///
155/// let mut map = BTreeMap::new();
156/// assert_that!(map).is_empty();
157///
158/// map.insert("one", 1);
159/// map.insert("two", 2);
160/// map.insert("three", 3);
161///
162/// assert_that!(map).has_length(3);
163/// assert_that!(map).contains_key("one");
164/// assert_that!(map).key_set().contains_exactly(vec!["three","two","one"].iter());
165/// assert_that!(map).contains_all_of_in_order(BTreeMap::from([("one", 1), ("three", 3)]));
166/// ```
167pub trait OrderedMapAssertion<'a, K: 'a + Ord + Eq, V, ML, R>:
168    MapAssertion<'a, K, V, ML, R>
169where
170    AssertionResult: AssertionStrategy<R>,
171    ML: OrderedMapLike<K, V>,
172{
173    /// Checks that the subject exactly contains `expected` in the same order.
174    #[track_caller]
175    fn contains_exactly_in_order<BM, OML>(&self, expected: BM) -> R
176    where
177        K: Eq + Ord + Debug,
178        V: Eq + Debug,
179        OML: OrderedMapLike<K, V> + 'a,
180        BM: Borrow<OML> + 'a;
181
182    /// Checks that the subject contains at least all elements of `expected` in the same order.
183    #[track_caller]
184    fn contains_all_of_in_order<BM, OML>(&self, expected: BM) -> R
185    where
186        K: Eq + Ord + Debug,
187        V: Eq + Debug,
188        OML: OrderedMapLike<K, V> + 'a,
189        BM: Borrow<OML> + 'a;
190}
191
192impl<'a, K, V, ML, R> MapAssertion<'a, K, V, ML, R> for Subject<'a, ML, (), R>
193where
194    AssertionResult: AssertionStrategy<R>,
195    K: 'a + Eq,
196    ML: MapLike<K, V>,
197{
198    fn has_length(&self, length: usize) -> R {
199        self.new_subject(
200            &self.actual().len(),
201            Some(format!("{}.len()", self.description_or_expr())),
202            (),
203        )
204        .is_equal_to(length)
205    }
206
207    fn is_empty(&self) -> R
208    where
209        K: Debug,
210    {
211        check_is_empty(self.new_result(), self.actual().keys().into_iter())
212    }
213
214    fn is_not_empty(&self) -> R
215    where
216        K: Debug,
217    {
218        check_is_not_empty(self.new_result(), self.actual().keys().into_iter())
219    }
220
221    fn contains_key<BK>(&self, key: BK) -> R
222    where
223        BK: Borrow<K>,
224        K: Eq + Hash + Debug,
225    {
226        check_contains(
227            self.new_result(),
228            self.actual().keys().into_iter(),
229            &key.borrow(),
230        )
231    }
232
233    fn does_not_contain_key<BK>(&self, key: BK) -> R
234    where
235        BK: Borrow<K>,
236        K: Eq + Hash + Debug,
237    {
238        check_does_not_contain(
239            self.new_result(),
240            self.actual().keys().into_iter(),
241            &key.borrow(),
242        )
243    }
244
245    fn contains_entry<BK, BV>(&self, key: BK, value: BV) -> R
246    where
247        BK: Borrow<K>,
248        BV: Borrow<V>,
249        K: Eq + Hash + Debug,
250        V: Eq + Debug,
251    {
252        let actual_value = self.actual().get(key.borrow());
253        if Some(value.borrow()) == actual_value {
254            self.new_result().do_ok()
255        } else if actual_value.is_none() {
256            self.new_result()
257                .add_formatted_fact(
258                    "expected key to be mapped to value",
259                    MapEntry::new(key.borrow(), value.borrow()),
260                )
261                .add_fact("but key was not found", format!("{:?}", key.borrow()))
262                .add_splitter()
263                .add_fact(
264                    "though it did contain keys",
265                    format!("{:?}", self.actual().keys()),
266                )
267                .do_fail()
268        } else {
269            self.new_result()
270                .add_formatted_fact(
271                    "expected key to be mapped to value",
272                    MapEntry::new(key.borrow(), value.borrow()),
273                )
274                .add_fact(
275                    "but key was mapped to a different value",
276                    format!("{:?}", actual_value.unwrap().borrow()),
277                )
278                .add_splitter()
279                .add_fact(
280                    "though it did contain keys",
281                    format!("{:?}", self.actual().keys()),
282                )
283                .do_fail()
284        }
285    }
286
287    fn does_not_contain_entry<BK, BV>(&self, key: BK, value: BV) -> R
288    where
289        BK: Borrow<K>,
290        BV: Borrow<V>,
291        K: Eq + Hash + Debug,
292        V: Eq + Debug,
293    {
294        let actual_value = self.actual().get(key.borrow());
295        if Some(value.borrow()) == actual_value {
296            self.new_result()
297                .add_formatted_fact(
298                    "expected to not contain entry",
299                    MapEntry::new(key.borrow(), value.borrow()),
300                )
301                .add_simple_fact("but entry was found")
302                .add_splitter()
303                // TODO: add better representation of the map
304                .add_fact(
305                    "though it did contain",
306                    format!("{:?}", self.actual().keys()),
307                )
308                .do_fail()
309        } else {
310            self.new_result().do_ok()
311        }
312    }
313
314    fn contains_at_least<BM, OML>(&self, expected: BM) -> R
315    where
316        K: Eq + Hash + Debug,
317        V: Eq + Debug,
318        OML: MapLike<K, V> + 'a,
319        BM: Borrow<OML> + 'a,
320    {
321        let expected_map = expected.borrow();
322        let diff = MapComparison::from_map_like(self.actual(), expected_map, None);
323        if diff.common.len() == expected_map.len() {
324            return self.new_result().do_ok();
325        }
326        let (result, splitter) = feed_missing_entries_facts(
327            "at least",
328            self.new_result(),
329            &diff,
330            expected_map.len(),
331            false,
332        );
333        feed_different_values_facts(result, &diff, splitter)
334            .0
335            .do_fail()
336    }
337
338    fn does_not_contain_any<BM: 'a, OML: 'a>(&self, expected: BM) -> R
339    where
340        K: Eq + Hash + Debug,
341        V: Eq + Debug,
342        OML: MapLike<K, V> + 'a,
343        BM: Borrow<OML> + 'a,
344    {
345        let expected_map = expected.borrow();
346        let diff = MapComparison::from_map_like(self.actual(), expected_map, None);
347        if !diff.common.is_empty() {
348            let mut result = self
349                .new_result()
350                .add_simple_fact(format!("found {} unexpected entries", diff.common.len()))
351                .add_splitter();
352            for (key, value) in diff.common {
353                result = result.add_simple_formatted_fact(MapEntry::new(key, value));
354            }
355            return result.do_fail();
356        }
357        return self.new_result().do_ok();
358    }
359
360    fn contains_exactly<BM, OML>(&self, expected: BM) -> R
361    where
362        K: Eq + Hash + Debug,
363        V: Eq + Debug,
364        OML: MapLike<K, V> + 'a,
365        BM: Borrow<OML> + 'a,
366    {
367        let expected_map = expected.borrow();
368        let diff = MapComparison::from_map_like(self.actual(), expected_map, None);
369        if diff.extra.is_empty() && diff.missing.is_empty() && diff.different_values.is_empty() {
370            return self.new_result().do_ok();
371        }
372        let (result, splitter) = feed_missing_entries_facts(
373            "exactly",
374            self.new_result(),
375            &diff,
376            expected_map.len(),
377            false,
378        );
379        let (result, splitter) = feed_extra_entries_facts(result, &diff, splitter);
380        feed_different_values_facts(result, &diff, splitter)
381            .0
382            .do_fail()
383    }
384
385    fn key_set<'b>(&'b self) -> Subject<ML::It<'b>, (), R>
386    where
387        K: 'b,
388    {
389        self.new_owned_subject(
390            self.actual().keys_iter(),
391            Some(format!("{}.keys()", self.description_or_expr())),
392            (),
393        )
394    }
395}
396
397impl<'a, K, V, ML, R> OrderedMapAssertion<'a, K, V, ML, R> for Subject<'a, ML, (), R>
398where
399    AssertionResult: AssertionStrategy<R>,
400    K: 'a + Eq + Ord,
401    ML: OrderedMapLike<K, V>,
402{
403    fn contains_exactly_in_order<BM, OML>(&self, expected: BM) -> R
404    where
405        K: Eq + Ord + Debug,
406        V: Eq + Debug,
407        OML: OrderedMapLike<K, V> + 'a,
408        BM: Borrow<OML> + 'a,
409    {
410        let map_diff = MapComparison::from_map_like(
411            self.actual(),
412            expected.borrow(),
413            Some(SequenceOrderComparison::Strict),
414        );
415        let (values_assertion_result, values_different) =
416            feed_different_values_facts(self.new_result(), &map_diff, false);
417        let key_order_comparison = map_diff.key_order_comparison.unwrap();
418        let (order_assertion_result, order_ok) = super::iterator::check_contains_exactly_in_order(
419            key_order_comparison,
420            self.actual().keys().into_iter(),
421            expected.borrow().keys().into_iter(),
422            values_assertion_result,
423        );
424
425        if order_ok && !values_different {
426            order_assertion_result.do_ok()
427        } else {
428            order_assertion_result.do_fail()
429        }
430    }
431
432    fn contains_all_of_in_order<BM, OML>(&self, expected: BM) -> R
433    where
434        K: Eq + Ord + Debug,
435        V: Eq + Debug,
436        OML: OrderedMapLike<K, V> + 'a,
437        BM: Borrow<OML> + 'a,
438    {
439        let map_diff = MapComparison::from_map_like(
440            self.actual(),
441            expected.borrow(),
442            Some(SequenceOrderComparison::Relative),
443        );
444        let (values_assertion_result, values_different) =
445            feed_different_values_facts(self.new_result(), &map_diff, false);
446        let key_order_comparison = map_diff.key_order_comparison.unwrap();
447        let (order_assertion_result, order_ok) = super::iterator::check_contains_all_of_in_order(
448            key_order_comparison,
449            self.actual().keys().into_iter(),
450            expected.borrow().keys().into_iter(),
451            values_assertion_result,
452        );
453
454        if order_ok && !values_different {
455            order_assertion_result.do_ok()
456        } else {
457            order_assertion_result.do_fail()
458        }
459    }
460}
461
462fn pluralize<'a>(count: usize, single: &'a str, plural: &'a str) -> &'a str {
463    if count == 1 {
464        single
465    } else {
466        plural
467    }
468}
469
470fn feed_different_values_facts<K: Eq + Debug, V: Eq + Debug>(
471    mut result: AssertionResult,
472    diff: &MapComparison<&K, &V>,
473    splitter: bool,
474) -> (AssertionResult, bool) {
475    let has_diffs = !diff.different_values.is_empty();
476    if has_diffs {
477        if splitter {
478            result = result.add_splitter();
479        }
480        result = result
481            .add_fact(
482                "expected to contain the same entries",
483                format!(
484                    "but found {} {} different",
485                    diff.different_values.len(),
486                    pluralize(
487                        diff.different_values.len(),
488                        "entry that is",
489                        "entries that are",
490                    )
491                ),
492            )
493            .add_splitter();
494        let mut ordered_diffs: Vec<_> = diff.different_values.iter().collect();
495        ordered_diffs.sort_by(|d1, d2| format!("{:?}", d1.key).cmp(&format!("{:?}", d2.key)));
496        result = result.add_formatted_values_fact(
497            format!(
498                "{} mapped to unexpected {}",
499                pluralize(diff.different_values.len(), "key was", "keys were"),
500                pluralize(diff.different_values.len(), "value", "values")
501            ),
502            ordered_diffs,
503        );
504    }
505    (result, has_diffs)
506}
507
508fn feed_missing_entries_facts<K: Eq + Debug, V: Eq + Debug>(
509    containment_spec: &str,
510    mut result: AssertionResult,
511    diff: &MapComparison<&K, &V>,
512    expected_length: usize,
513    splitter: bool,
514) -> (AssertionResult, bool) {
515    let has_diffs = !diff.missing.is_empty();
516    if has_diffs {
517        if splitter {
518            result = result.add_splitter();
519        }
520        result = result
521            .add_fact(
522                format!(
523                    "expected to contain {} {} provided {}",
524                    containment_spec,
525                    expected_length,
526                    pluralize(expected_length, "entry", "entries")
527                ),
528                format!(
529                    "but {} {} not found",
530                    diff.missing.len(),
531                    pluralize(diff.missing.len(), "entry", "entries")
532                ),
533            )
534            .add_splitter();
535        result = result.add_formatted_values_fact(
536            format!(
537                "{} not found",
538                pluralize(diff.missing.len(), "entry was", "entries were")
539            ),
540            (&diff.missing)
541                .into_iter()
542                .map(|(k, v)| MapEntry::new(k, v))
543                .collect(),
544        );
545    }
546    (result, has_diffs)
547}
548
549fn feed_extra_entries_facts<K: Eq + Debug, V: Eq + Debug>(
550    mut result: AssertionResult,
551    diff: &MapComparison<&K, &V>,
552    splitter: bool,
553) -> (AssertionResult, bool) {
554    let has_diffs = !diff.extra.is_empty();
555    if has_diffs {
556        if splitter {
557            result = result.add_splitter();
558        }
559        result = result
560            .add_fact(
561                "expected to not contain additional entries".to_string(),
562                format!(
563                    "but {} additional {} found",
564                    diff.extra.len(),
565                    pluralize(diff.extra.len(), "entry was", "entries were")
566                ),
567            )
568            .add_splitter();
569        result = result.add_formatted_values_fact(
570            format!(
571                "unexpected {} found",
572                pluralize(diff.extra.len(), "entry was", "entries were")
573            ),
574            (&diff.extra)
575                .into_iter()
576                .map(|(k, v)| MapEntry::new(k, v))
577                .collect(),
578        );
579    }
580    (result, has_diffs)
581}
582
583struct MapEntry<'a, K: Debug, V: Debug> {
584    key: &'a K,
585    value: &'a V,
586}
587
588impl<'a, K: Debug, V: Debug> MapEntry<'a, K, V> {
589    fn new(key: &'a K, value: &'a V) -> MapEntry<'a, K, V> {
590        Self { key, value }
591    }
592}
593
594impl<'a, K: Debug, V: Debug> Debug for MapEntry<'a, K, V> {
595    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
596        f.write_str(format!("{:?} ⟶ {:?}", self.key, self.value).as_str())
597    }
598}
599
600impl<K: Debug, V: PartialEq + Debug> Debug for MapValueDiff<&K, &V> {
601    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
602        f.write_str(
603            format!(
604                r#"{{ key: {:?}, expected: {:?}, actual: {:?} }}"#,
605                self.key, self.actual_value, self.expected_value
606            )
607            .as_str(),
608        )
609    }
610}
611
612#[cfg(test)]
613mod tests {
614    use crate::testing::*;
615    use crate::{assert_that, check_that, Fact, IteratorAssertion, SetAssertion};
616    use std::collections::{BTreeMap, HashMap};
617
618    use super::*;
619
620    #[test]
621    fn has_length() {
622        let map_empty: HashMap<&str, &str> = HashMap::new();
623        assert_that!(map_empty).has_length(0);
624
625        let mut map_with_two_entry = HashMap::new();
626        map_with_two_entry.insert("1", "y");
627        map_with_two_entry.insert("2", "z");
628        assert_that!(map_with_two_entry).has_length(2);
629
630        // failures
631        assert_that!(check_that!(map_empty).has_length(1)).facts_are(vec![
632            Fact::new("value of", "map_empty.len()"),
633            Fact::new("expected", "1"),
634            Fact::new("actual", "0"),
635        ])
636    }
637
638    #[test]
639    fn is_empty() {
640        let map_empty: HashMap<&str, &str> = HashMap::new();
641        assert_that!(map_empty).is_empty();
642
643        // failures
644        assert_that!(check_that!(HashMap::from([("a", "b")])).is_empty()).facts_are(vec![
645            Fact::new_simple_fact("expected to be empty"),
646            Fact::new_splitter(),
647            Fact::new_multi_value_fact("actual", vec![r#""a""#]),
648        ])
649    }
650
651    #[test]
652    fn is_not_empty() {
653        let non_empty: HashMap<&str, &str> = HashMap::from([("a", "b")]);
654        assert_that!(non_empty).is_not_empty();
655
656        // failures
657        let empty_map: HashMap<&str, &str> = HashMap::new();
658        assert_that!(check_that!(empty_map).is_not_empty()).facts_are(vec![
659            Fact::new_simple_fact("expected to be non-empty"),
660            Fact::new_splitter(),
661            Fact::new("actual", "[]"),
662        ])
663    }
664
665    #[test]
666    fn contains_key() {
667        let mut map_abc: HashMap<&str, &str> = HashMap::new();
668        map_abc.insert("a", "1");
669        map_abc.insert("b", "2");
670        map_abc.insert("c", "3");
671        assert_that!(map_abc).contains_key("a");
672        assert_that!(map_abc).contains_key("b");
673        assert_that!(map_abc).contains_key("c");
674
675        // failures
676        let result = check_that!(map_abc).contains_key("not exist");
677        assert_that!(result).facts_are_at_least(vec![
678            Fact::new("expected to contain", r#""not exist""#),
679            Fact::new_simple_fact("but did not"),
680        ]);
681        assert_that!(result)
682            .fact_keys()
683            .contains(&"though it did contain".to_string());
684        // Skip test for value because key order is not stable.
685    }
686
687    #[test]
688    fn does_not_contain_key() {
689        let mut map_abc: HashMap<&str, &str> = HashMap::new();
690        map_abc.insert("a", "1");
691        map_abc.insert("b", "2");
692        map_abc.insert("c", "3");
693        assert_that!(map_abc).does_not_contain_key("x");
694        assert_that!(map_abc).does_not_contain_key("y");
695
696        // failures
697        let result = check_that!(map_abc).does_not_contain_key("a");
698        assert_that!(result).facts_are_at_least(vec![
699            Fact::new("expected to not contain", r#""a""#),
700            Fact::new_simple_fact("but element was found"),
701        ]);
702        assert_that!(result)
703            .fact_keys()
704            .contains(&"though it did contain".to_string());
705        // Skip test for value because key order is not stable.
706    }
707
708    #[test]
709    fn key_set() {
710        let mut map_abc: HashMap<&str, &str> = HashMap::new();
711        map_abc.insert("a", "1");
712        map_abc.insert("b", "2");
713        map_abc.insert("c", "3");
714        assert_that!(map_abc).key_set().contains(&"a");
715        assert_that!(map_abc).key_set().contains(&"b");
716        assert_that!(map_abc).key_set().contains(&"c");
717
718        // failures
719        let result = check_that!(map_abc).key_set().contains(&"not exist");
720        assert_that!(result).facts_are_at_least(vec![
721            Fact::new("value of", "map_abc.keys()"),
722            Fact::new("expected to contain", r#""not exist""#),
723            Fact::new_simple_fact("but did not"),
724            /* TODO: fix unstable value order.
725             * Fact::new("though it did contain", r#"["c", "a", "b"]"#), */
726        ]);
727        assert_that!(result)
728            .fact_keys()
729            .contains(&"though it did contain".to_string());
730        // Skip test for value because key order is not stable.
731    }
732
733    #[test]
734    fn contains_entry() {
735        let mut map_abc: HashMap<&str, &str> = HashMap::new();
736        map_abc.insert("a", "1");
737        map_abc.insert("b", "2");
738        map_abc.insert("c", "3");
739        assert_that!(map_abc).contains_entry("a", "1");
740        assert_that!(map_abc).contains_entry("b", "2");
741        assert_that!(map_abc).contains_entry("c", "3");
742
743        // failures: missing key
744        let result = check_that!(map_abc).contains_entry("not exist", "1");
745        assert_that!(result).facts_are_at_least(vec![
746            Fact::new("expected key to be mapped to value", r#""not exist" ⟶ "1""#),
747            Fact::new("but key was not found", r#""not exist""#),
748            Fact::new_splitter(),
749        ]);
750        assert_that!(result)
751            .fact_keys()
752            .contains(&"though it did contain keys".to_string());
753        // Skip test for value because key order is not stable.
754
755        // failures: not equal value
756        let result = check_that!(map_abc).contains_entry("a", "2");
757        assert_that!(result).facts_are_at_least(vec![
758            Fact::new("expected key to be mapped to value", r#""a" ⟶ "2""#),
759            Fact::new("but key was mapped to a different value", r#""1""#),
760            Fact::new_splitter(),
761        ]);
762        assert_that!(result)
763            .fact_keys()
764            .contains(&"though it did contain keys".to_string());
765        // Skip test for value because key order is not stable.
766    }
767
768    #[test]
769    fn does_not_contain_entry() {
770        let mut map_abc: HashMap<&str, &str> = HashMap::new();
771        map_abc.insert("a", "1");
772        map_abc.insert("b", "2");
773        map_abc.insert("c", "3");
774
775        // different key
776        assert_that!(map_abc).does_not_contain_entry("x", "1");
777        // different value
778        assert_that!(map_abc).does_not_contain_entry("a", "2");
779        assert_that!(map_abc).does_not_contain_entry("b", "3");
780        assert_that!(map_abc).does_not_contain_entry("c", "4");
781
782        // failure
783        let result = check_that!(map_abc).does_not_contain_entry("a", "1");
784        assert_that!(result).facts_are_at_least(vec![
785            Fact::new("expected to not contain entry", r#""a" ⟶ "1""#),
786            Fact::new_simple_fact("but entry was found"),
787            Fact::new_splitter(),
788        ]);
789        assert_that!(result)
790            .fact_keys()
791            .contains(&"though it did contain".to_string());
792    }
793
794    #[test]
795    fn contains_at_least() {
796        let mut map_abc: HashMap<&str, &str> = HashMap::new();
797        map_abc.insert("a", "1");
798        map_abc.insert("b", "2");
799        map_abc.insert("c", "3");
800        assert_that!(map_abc).contains_at_least(HashMap::from([("a", "1")]));
801        assert_that!(map_abc).contains_at_least(HashMap::from([("a", "1"), ("b", "2")]));
802
803        // case 1: missing key
804        let result = check_that!(map_abc).contains_at_least(HashMap::from([("not exist", "1")]));
805        assert_that!(result).facts_are_at_least(vec![
806            Fact::new(
807                "expected to contain at least 1 provided entry",
808                "but 1 entry not found",
809            ),
810            Fact::new_splitter(),
811            Fact::new_multi_value_fact("entry was not found", vec![r#""not exist" ⟶ "1""#]),
812        ]);
813
814        // case 2: mismatched entries
815        let result = check_that!(map_abc).contains_at_least(HashMap::from([("c", "5")]));
816        assert_that!(result).facts_are_at_least(vec![
817            Fact::new(
818                "expected to contain the same entries",
819                "but found 1 entry that is different",
820            ),
821            Fact::new_splitter(),
822            Fact::new_multi_value_fact(
823                r#"key was mapped to unexpected value"#,
824                vec![r#"{ key: "c", expected: "3", actual: "5" }"#],
825            ),
826        ]);
827
828        // case 3: both mismatched and absent key
829        let result =
830            check_that!(map_abc).contains_at_least(HashMap::from([("not exist", "1"), ("c", "5")]));
831        assert_that!(result).facts_are_at_least(vec![
832            Fact::new(
833                "expected to contain at least 2 provided entries",
834                "but 1 entry not found",
835            ),
836            Fact::new_splitter(),
837            Fact::new_multi_value_fact("entry was not found", vec![r#""not exist" ⟶ "1""#]),
838            Fact::new_splitter(),
839            Fact::new(
840                "expected to contain the same entries",
841                "but found 1 entry that is different",
842            ),
843            Fact::new_splitter(),
844            Fact::new_multi_value_fact(
845                r#"key was mapped to unexpected value"#,
846                vec![r#"{ key: "c", expected: "3", actual: "5" }"#],
847            ),
848        ]);
849    }
850
851    #[test]
852    fn contains_exactly() {
853        let mut map_abc: HashMap<&str, &str> = HashMap::new();
854        map_abc.insert("a", "1");
855        map_abc.insert("b", "2");
856        map_abc.insert("c", "3");
857        assert_that!(map_abc).contains_exactly(HashMap::from([("a", "1"), ("c", "3"), ("b", "2")]));
858
859        // case 1: missing key
860        let result = check_that!(map_abc).contains_exactly(HashMap::from([("not exist", "1")]));
861        assert_that!(result).facts_are_at_least(vec![
862            Fact::new(
863                "expected to contain exactly 1 provided entry",
864                "but 1 entry not found",
865            ),
866            Fact::new_splitter(),
867            Fact::new_multi_value_fact("entry was not found", vec![r#""not exist" ⟶ "1""#]),
868        ]);
869
870        // case 2: extra key
871        let result = check_that!(HashMap::from([
872            ("a", "1"),
873            ("c", "3"),
874            ("b", "2"),
875            ("ex", "1")
876        ]))
877        .contains_exactly(map_abc);
878        assert_that!(result).facts_are_at_least(vec![
879            Fact::new(
880                "expected to not contain additional entries",
881                "but 1 additional entry was found",
882            ),
883            Fact::new_splitter(),
884            Fact::new_multi_value_fact("unexpected entry was found", vec![r#""ex" ⟶ "1""#]),
885        ]);
886
887        // case 3: mismatched entries
888        let result = check_that!(HashMap::from([("a", "1"), ("b", "f")]))
889            .contains_at_least(HashMap::from([("a", "2"), ("b", "g")]));
890        assert_that!(result).facts_are_at_least(vec![
891            Fact::new(
892                "expected to contain the same entries",
893                "but found 2 entries that are different",
894            ),
895            Fact::new_splitter(),
896            Fact::new_multi_value_fact(
897                r#"keys were mapped to unexpected values"#,
898                vec![
899                    r#"{ key: "a", expected: "1", actual: "2" }"#,
900                    r#"{ key: "b", expected: "f", actual: "g" }"#,
901                ],
902            ),
903        ]);
904
905        // case 4: all mismatches
906        let result = check_that!(HashMap::from([("a", "1"), ("b", "2")]))
907            .contains_exactly(HashMap::from([("a", "2"), ("c", "2")]));
908        assert_that!(result).facts_are_at_least(vec![
909            Fact::new(
910                "expected to contain exactly 2 provided entries",
911                "but 1 entry not found",
912            ),
913            Fact::new_splitter(),
914            Fact::new_multi_value_fact("entry was not found", vec![r#""c" ⟶ "2""#]),
915            Fact::new_splitter(),
916            Fact::new(
917                "expected to not contain additional entries",
918                "but 1 additional entry was found",
919            ),
920            Fact::new_splitter(),
921            Fact::new_multi_value_fact("unexpected entry was found", vec![r#""b" ⟶ "2""#]),
922            Fact::new_splitter(),
923            Fact::new(
924                "expected to contain the same entries",
925                "but found 1 entry that is different",
926            ),
927            Fact::new_splitter(),
928            Fact::new_multi_value_fact(
929                r#"key was mapped to unexpected value"#,
930                vec![r#"{ key: "a", expected: "1", actual: "2" }"#],
931            ),
932        ]);
933    }
934
935    #[test]
936    fn does_not_contain_any() {
937        let mut map_abc: HashMap<&str, &str> = HashMap::new();
938        map_abc.insert("a", "1");
939        map_abc.insert("b", "2");
940        map_abc.insert("c", "3");
941
942        assert_that!(map_abc).does_not_contain_any(HashMap::from([
943            ("a", "2"),
944            ("b", "3"),
945            ("x", "1"),
946        ]));
947
948        let result = check_that!(map_abc).does_not_contain_any(HashMap::from([
949            ("a", "1"),
950            ("c", "3"),
951            ("x", "g"),
952        ]));
953        assert_that!(result).facts_are_at_least(vec![
954            Fact::new_simple_fact("found 2 unexpected entries"),
955            Fact::new_splitter(),
956        ]);
957        assert_that!(result).facts_are_at_least(vec![Fact::new_simple_fact(r#""c" ⟶ "3""#)]);
958        assert_that!(result).facts_are_at_least(vec![Fact::new_simple_fact(r#""a" ⟶ "1""#)]);
959    }
960
961    #[test]
962    fn supports_any_map() {
963        let empty: BTreeMap<String, String> = BTreeMap::new();
964        let tree_map = BTreeMap::from([("hello", "sorted_map"), ("world", "in")]);
965        assert_that!(tree_map).has_length(2);
966        assert_that!(empty).is_empty();
967        assert_that!(tree_map).is_not_empty();
968        assert_that!(tree_map).contains_key("hello");
969        assert_that!(tree_map).does_not_contain_key(&"key");
970        assert_that!(tree_map).key_set().contains(&"hello");
971        assert_that!(tree_map).contains_entry("hello", "sorted_map");
972        assert_that!(tree_map).does_not_contain_entry("hello", "other");
973        assert_that!(tree_map).contains_at_least(BTreeMap::from([("world", "in")]));
974        assert_that!(tree_map).contains_at_least(HashMap::from([("world", "in")]));
975        assert_that!(tree_map)
976            .contains_exactly(BTreeMap::from([("hello", "sorted_map"), ("world", "in")]));
977        assert_that!(tree_map)
978            .contains_exactly(HashMap::from([("hello", "sorted_map"), ("world", "in")]));
979        assert_that!(tree_map).does_not_contain_any(BTreeMap::from([("world", "nope")]));
980        assert_that!(tree_map).does_not_contain_any(HashMap::from([("world", "nope")]));
981    }
982
983    #[test]
984    fn contains_exactly_in_order() {
985        let tree_map = BTreeMap::from([("hello", "sorted_map"), ("world", "in")]);
986        assert_that!(tree_map)
987            .contains_exactly_in_order(BTreeMap::from([("hello", "sorted_map"), ("world", "in")]));
988
989        // Wrong value
990        let result = check_that!(tree_map)
991            .contains_exactly_in_order(BTreeMap::from([("hello", "wrong"), ("world", "in")]));
992        assert_that!(result).facts_are_at_least(vec![
993            Fact::new(
994                "expected to contain the same entries",
995                "but found 1 entry that is different",
996            ),
997            Fact::new_splitter(),
998            Fact::new_multi_value_fact(
999                r#"key was mapped to unexpected value"#,
1000                vec![r#"{ key: "hello", expected: "sorted_map", actual: "wrong" }"#],
1001            ),
1002        ]);
1003
1004        // Extra key
1005        let result = check_that!(tree_map)
1006            .contains_exactly_in_order(BTreeMap::from([("hello", "sorted_map"), ("was", "at")]));
1007        assert_that!(result).facts_are(vec![
1008            Fact::new("missing (1)", r#"["was"]"#),
1009            Fact::new("unexpected (1)", r#"["world"]"#),
1010            Fact::new_splitter(),
1011            Fact::new_multi_value_fact("expected", vec![r#""hello""#, r#""was""#]),
1012            Fact::new_multi_value_fact("actual", vec![r#""hello""#, r#""world""#]),
1013        ]);
1014
1015        // Extra key and wrong value
1016        let result = check_that!(tree_map)
1017            .contains_exactly_in_order(BTreeMap::from([("hello", "wrong"), ("was", "at")]));
1018        assert_that!(result).facts_are(vec![
1019            Fact::new(
1020                "expected to contain the same entries",
1021                "but found 1 entry that is different",
1022            ),
1023            Fact::new_splitter(),
1024            Fact::new_multi_value_fact(
1025                r#"key was mapped to unexpected value"#,
1026                vec![r#"{ key: "hello", expected: "sorted_map", actual: "wrong" }"#],
1027            ),
1028            Fact::new("missing (1)", r#"["was"]"#),
1029            Fact::new("unexpected (1)", r#"["world"]"#),
1030            Fact::new_splitter(),
1031            Fact::new_multi_value_fact("expected", vec![r#""hello""#, r#""was""#]),
1032            Fact::new_multi_value_fact("actual", vec![r#""hello""#, r#""world""#]),
1033        ]);
1034    }
1035
1036    #[test]
1037    fn contains_all_of_in_order() {
1038        let tree_map = BTreeMap::from([("hello", "sorted_map"), ("lang", "en"), ("world", "in")]);
1039        assert_that!(tree_map)
1040            .contains_all_of_in_order(BTreeMap::from([("hello", "sorted_map"), ("world", "in")]));
1041
1042        // Extra key and wrong value
1043        let result = check_that!(tree_map)
1044            .contains_exactly_in_order(BTreeMap::from([("hello", "wrong"), ("ww", "w")]));
1045        assert_that!(result).facts_are(vec![
1046            Fact::new(
1047                "expected to contain the same entries",
1048                "but found 1 entry that is different",
1049            ),
1050            Fact::new_splitter(),
1051            Fact::new_multi_value_fact(
1052                r#"key was mapped to unexpected value"#,
1053                vec![r#"{ key: "hello", expected: "sorted_map", actual: "wrong" }"#],
1054            ),
1055            Fact::new("missing (1)", r#"["ww"]"#),
1056            Fact::new("unexpected (2)", r#"["lang", "world"]"#),
1057            Fact::new_splitter(),
1058            Fact::new_multi_value_fact("expected", vec![r#""hello""#, r#""ww""#]),
1059            Fact::new_multi_value_fact("actual", vec![r#""hello""#, r#""lang""#, r#""world""#]),
1060        ]);
1061    }
1062}