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