def test_invalid_deviation_multiple_args_percent(self):
     with self.assertRaises(ValidationError) as cm:
         with AcceptedPercent(0.5):  # <- Accepts +/- 50%.
             raise ValidationError([
                 Invalid(50, 100),   # <- ACCEPTED: -50% deviation.
                 Invalid(150, 100),  # <- ACCEPTED: +50% deviation.
                 Invalid(0.5, 0),    # <- Rejected: Can not be accepted by percent.
                 Invalid(4, 2),      # <- Rejected: +100% is outside range.
             ])
     remaining = cm.exception.differences
     self.assertEqual(remaining, [Invalid(0.5, 0), Invalid(4, 2)])
    def test_accept_string(self):
        with self.assertRaises(ValidationError) as cm:

            with AcceptedKeys('aaa'):  # <- Accept by string!
                raise ValidationError({
                    'aaa': Missing(1),
                    'bbb': Missing(2),
                })

        remaining_diffs = cm.exception.differences
        self.assertEqual(dict(remaining_diffs), {'bbb': Missing(2)})
 def test_invalid_deviation_single_arg_percent(self):
     # Check error percent.
     with self.assertRaises(ValidationError) as cm:
         with AcceptedPercent(2.0):  # <- Accepts +/- 200%.
             raise ValidationError([
                 Invalid(-1),  # <- Rejected: Can not be accepted by percent.
                 Invalid(0),   # <- ACCEPTED!
                 Invalid(2),   # <- Rejected: Can not be accepted by percent.
             ])
     remaining = cm.exception.differences
     self.assertEqual(remaining, [Invalid(-1), Invalid(2)])
    def test_composite_key(self):
        with self.assertRaises(ValidationError) as cm:

            with AcceptedKeys(('a', 7)):  # <- Accept using tuple!
                raise ValidationError({
                    ('a', 7): Missing(1),
                    ('b', 7): Missing(2)
                })

        remaining_diffs = cm.exception.differences
        self.assertEqual(dict(remaining_diffs), {('b', 7): Missing(2)})
    def test_scope(self):
        with self.assertRaises(ValidationError) as cm:
            with AcceptedCount(2, scope='group'):  # <- Accepts 2 per group.
                raise ValidationError({
                    'foo': [Extra('xxx'), Extra('yyy')],
                    'bar': [Missing('xxx'), Missing('yyy')],
                    'baz': [Invalid('xxx'), Invalid('yyy'), Invalid('zzz')],
                })

        remaining = cm.exception.differences
        self.assertEqual(remaining, {'baz': Invalid('zzz')})
Esempio n. 6
0
    def assertAcceptance(self, differences, acceptance, expected):
        """Helper method to test acceptances."""
        with self.assertRaises(ValidationError) as cm:
            with acceptance:  # <- Apply acceptance!
                raise ValidationError(differences)
        remaining_diffs = cm.exception.differences

        if isinstance(differences, Mapping):
            remaining_diffs = dict(remaining_diffs)
        elif nonstringiter(remaining_diffs):
            remaining_diffs = list(remaining_diffs)
        self.assertEqual(remaining_diffs, expected)
Esempio n. 7
0
    def test_IntersectedAcceptance(self):
        original_diffs = [Extra('a'), Missing('a'), Missing('b'), Extra('b')]

        with self.assertRaises(ValidationError) as cm:
            with IntersectedAcceptance(self.accepted_missing,
                                       self.accepted_letter_a):
                raise ValidationError(original_diffs)
        differences = cm.exception.differences
        self.assertEqual(
            list(differences),
            [Extra('a'), Missing('b'), Extra('b')])

        # Test with acceptances in reverse-order (should give same result).
        with self.assertRaises(ValidationError) as cm:
            with IntersectedAcceptance(self.accepted_letter_a,
                                       self.accepted_missing):
                raise ValidationError(original_diffs)
        differences = cm.exception.differences
        self.assertEqual(
            list(differences),
            [Extra('a'), Missing('b'), Extra('b')])
 def test_nonnumeric_but_compatible(self):
     with self.assertRaises(ValidationError) as cm:
         with AcceptedTolerance(datetime.timedelta(hours=2)):  # <- Accepts +/- 2 hours.
             raise ValidationError([
                 Invalid(datetime.datetime(1989, 2, 24, hour=10, minute=30),
                         datetime.datetime(1989, 2, 24, hour=11, minute=30)),
                 Invalid(datetime.datetime(1989, 2, 24, hour=15, minute=10),
                         datetime.datetime(1989, 2, 24, hour=11, minute=30))
             ])
     remaining = cm.exception.differences
     self.assertEqual(remaining, [Invalid(datetime.datetime(1989, 2, 24, 15, 10),
                                          expected=datetime.datetime(1989, 2, 24, 11, 30))])
Esempio n. 9
0
 def test_missing_deviation_percent(self):
     with self.assertRaises(ValidationError) as cm:
         with AcceptedPercent(1.0):  # <- Accepts +/- 100%.
             raise ValidationError([
                 Missing(-1),  # <- ACCEPTED!
                 Missing(0),  # <- ACCEPTED!
                 Missing(2),  # <- ACCEPTED!
                 Missing((1, 2)),  # <- Rejected: Wrong type.
                 Missing('abc'),  # <- Rejected: Wrong type.
             ])
     remaining = cm.exception.differences
     self.assertEqual(remaining, [Missing((1, 2)), Missing('abc')])
Esempio n. 10
0
    def test_string_predicate(self):
        with self.assertRaises(ValidationError) as cm:

            with allowed_args('bbb'):  # <- Allowance!
                raise ValidationError([
                    Missing('aaa'),
                    Missing('bbb'),
                    Extra('bbb'),
                ])

        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Missing('aaa')])
Esempio n. 11
0
    def test_dict_and_list(self):
        """List of allowed differences applied to each group separately."""
        differences = {'foo': Extra('xxx'), 'bar': [Extra('xxx'), Missing('yyy')]}
        allowed = [Extra('xxx')]

        with self.assertRaises(ValidationError) as cm:
            with allowed_specific(allowed):
                raise ValidationError(differences)

        actual = cm.exception.differences
        expected = {'bar': Missing('yyy')}
        self.assertEqual(actual, expected)
Esempio n. 12
0
    def test_nonmapping_container(self):
        """When differences container is not a mapping, the keys that
        allowed_key() sees are all None.
        """
        with self.assertRaises(ValidationError) as cm:

            with allowed_keys('foo'):  # <- Allow keys that equal 'foo'.
                differences = [Missing(1), Extra(2)]  # <- List has no keys!
                raise ValidationError(differences)

        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Missing(1), Extra(2)])
Esempio n. 13
0
    def test_multiarg_predicate(self):
        with self.assertRaises(ValidationError) as cm:

            def func(diff):
                return diff < 2

            with allowed_args((func, 5)):
                raise ValidationError([
                    Deviation(+1, 5),
                    Deviation(+2, 5),
                ])

        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Deviation(+2, 5)])
Esempio n. 14
0
    def test_allow_function(self):
        with self.assertRaises(ValidationError) as cm:

            def function(key):
                return key == 'aaa'

            with allowed_keys(function):  # <- Allow by function!
                raise ValidationError({
                    'aaa': Missing(1),
                    'bbb': Missing(2),
                })

        remaining_diffs = cm.exception.differences
        self.assertEqual(dict(remaining_diffs), {'bbb': Missing(2)})
Esempio n. 15
0
    def test_function_predicate(self):
        with self.assertRaises(ValidationError) as cm:

            def function(args):
                diff, expected = args
                return diff < 2 and expected == 5

            with allowed_args(function):  # <- Allowance!
                raise ValidationError([
                    Deviation(+1, 5),
                    Deviation(+2, 5),
                ])

        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Deviation(+2, 5)])
Esempio n. 16
0
    def test_str_method(self):
        # Assert basic format and trailing comma.
        err = ValidationError([MinimalDifference('A')], 'invalid data')
        expected = """
            invalid data (1 difference): [
                MinimalDifference('A'),
            ]
        """
        expected = textwrap.dedent(expected).strip()
        self.assertEqual(str(err), expected)

        # Assert without description.
        err = ValidationError([MinimalDifference('A')])  # <- No description!
        expected = """
            1 difference: [
                MinimalDifference('A'),
            ]
        """
        expected = textwrap.dedent(expected).strip()
        self.assertEqual(str(err), expected)

        # Assert "no cacheing"--objects that inhereit from some
        # Exceptions can cache their str--but ValidationError should
        # not do this.
        err._differences = [MinimalDifference('B')]
        err._description = 'changed'
        updated = textwrap.dedent("""
            changed (1 difference): [
                MinimalDifference('B'),
            ]
        """).strip()
        self.assertEqual(str(err), updated)

        # Assert dict format and trailing comma.
        err = ValidationError(
            {
                'x': MinimalDifference('A'),
                'y': MinimalDifference('B')
            }, 'invalid data')
        regex = textwrap.dedent(r"""
            invalid data \(2 differences\): \{
                '[xy]': MinimalDifference\('[AB]'\),
                '[xy]': MinimalDifference\('[AB]'\),
            \}
        """).strip()
        self.assertRegex(str(err), regex)  # <- Using regex because dict order
Esempio n. 17
0
    def test_combination_of_cases(self):
        """This is a bit of an integration test."""
        differences = {
            'foo': [Extra('xxx'), Missing('yyy')],
            'bar': [Extra('xxx')],
            'baz': [Extra('xxx'), Missing('yyy'), Extra('zzz')],
        }
        #allowed = {Ellipsis: [Extra('xxx'), Missing('yyy')]}
        allowed = [Extra('xxx'), Missing('yyy')]
        with self.assertRaises(ValidationError) as cm:
            with allowed_specific(allowed):
                raise ValidationError(differences)

        actual = cm.exception.differences
        self.assertEqual(actual, {'baz': Extra('zzz')})
Esempio n. 18
0
    def test_incompatible_diffs(self):
        """Test differences that cannot be fuzzy matched."""
        incompatible_diffs = [
            Missing('foo'),
            Extra('bar'),
            Invalid('baz'),  # <- Cannot accept if there's no expected value.
            Deviation(1, 10),
        ]
        differences = incompatible_diffs + self.differences

        with self.assertRaises(ValidationError) as cm:
            with AcceptedFuzzy(cutoff=0.5):
                raise ValidationError(differences)

        remaining = cm.exception.differences
        self.assertEqual(remaining, incompatible_diffs)
Esempio n. 19
0
    def test_dict_global_wildcard_predicate(self):
        """Ellipsis wildcard key matches all, treats as a single group."""
        differences = {'foo': Extra('xxx'), 'bar': [Extra('xxx'), Missing('yyy')]}
        allowed = {Ellipsis: Extra('xxx')}

        with self.assertRaises(ValidationError) as cm:
            with allowed_specific(allowed):
                raise ValidationError(differences)

        actual = cm.exception.differences
        # Actual result can vary with unordered dictionaries.
        if len(actual) == 1:
            expected = {'bar': [Extra('xxx'), Missing('yyy')]}
        else:
            expected = {'foo': Extra('xxx'), 'bar': Missing('yyy')}
        self.assertEqual(actual, expected)
Esempio n. 20
0
    def test_predicate_collision(self):
        """Ellipsis wildcard key matches all, treats as a single group."""
        differences = {
            'foo': Extra('xxx'),
            'bar': [Extra('yyy'), Missing('yyy')],
        }

        def allow1(x):
            return x.startswith('ba')

        def allow2(x):
            return x == 'bar'

        allowed = {
            allow1: Extra('yyy'),
            allow2: Missing('yyy'),
        }

        regex = ("the key 'bar' matches multiple predicates: "
                 "allow[12], allow[12]")
        with self.assertRaisesRegex(KeyError, regex):
            with allowed_specific(allowed):
                raise ValidationError(differences)
Esempio n. 21
0
    def test_repr(self):
        err = ValidationError([MinimalDifference('A')])  # <- No description.
        expected = "ValidationError([MinimalDifference('A')])"
        self.assertEqual(repr(err), expected)

        err = ValidationError([MinimalDifference('A')], 'description string')
        expected = "ValidationError([MinimalDifference('A')], 'description string')"
        self.assertEqual(repr(err), expected)

        # Objects that inhereit from some Exceptions can cache their
        # repr--but ValidationError should not do this.
        err._differences = [MinimalDifference('B')]
        err._description = 'changed'
        self.assertNotEqual(repr(err), expected,
                            'exception should not cache repr')

        updated = "ValidationError([MinimalDifference('B')], 'changed')"
        self.assertEqual(repr(err), updated)
Esempio n. 22
0
    def test_str_truncation(self):
        # Assert optional truncation behavior.
        err = ValidationError([
            MinimalDifference('A'),
            MinimalDifference('B'),
            MinimalDifference('C'),
        ], 'invalid data')
        self.assertIsNone(err._should_truncate)
        self.assertIsNone(err._truncation_notice)
        no_truncation = """
            invalid data (3 differences): [
                MinimalDifference('A'),
                MinimalDifference('B'),
                MinimalDifference('C'),
            ]
        """
        no_truncation = textwrap.dedent(no_truncation).strip()
        self.assertEqual(str(err), no_truncation)

        # Truncate without notice.
        err._should_truncate = lambda line_count, char_count: char_count > 35
        err._truncation_notice = None
        truncation_witout_notice = """
            invalid data (3 differences): [
                MinimalDifference('A'),
                ...
        """
        truncation_witout_notice = textwrap.dedent(
            truncation_witout_notice).strip()
        self.assertEqual(str(err), truncation_witout_notice)

        # Truncate and use truncation notice.
        err._should_truncate = lambda line_count, char_count: char_count > 35
        err._truncation_notice = 'Message truncated.'
        truncation_plus_notice = """
            invalid data (3 differences): [
                MinimalDifference('A'),
                ...

            Message truncated.
        """
        truncation_plus_notice = textwrap.dedent(
            truncation_plus_notice).strip()
        self.assertEqual(str(err), truncation_plus_notice)
Esempio n. 23
0
 def test_tolerance_syntax(self):
     with self.assertRaises(ValidationError) as cm:
         with allowed_percent(0.2):  # <- Allows +/- 20%.
             raise ValidationError(self.differences)
     remaining = cm.exception.differences
     self.assertEqual(remaining, {'bbb': Deviation(+4, 16)})
Esempio n. 24
0
    def test_all_allowed(self):
        differences = {'foo': Extra('xxx'), 'bar': Missing('yyy')}
        allowed = {'foo': Extra('xxx'), 'bar': Missing('yyy')}

        with allowed_specific(allowed):  # <- Allows all differences, no error!
            raise ValidationError(differences)
Esempio n. 25
0
 def test_NaN_values(self):
     with self.assertRaises(ValidationError):  # <- NaN values should not be caught!
         with allowed_deviation(0):
             raise ValidationError(Deviation(float('nan'), 0))
Esempio n. 26
0
    def test_at_limit(self):
        with allowed_limit(2):  # <- Allows 2 and there are 2.
            raise ValidationError([Extra('xxx'), Missing('yyy')])

        with allowed_limit(3):  # <- Allows 2 and there are 2.
            raise ValidationError({'foo': Extra('xxx'), 'bar': Missing('yyy')})
Esempio n. 27
0
 def test_excess_allowed(self):
     diffs = [Extra('xxx')]
     allowed = [Extra('xxx'), Missing('yyy')]  # <- More allowed than
     with allowed_specific(allowed):           #    are actually found.
         raise ValidationError(diffs)
Esempio n. 28
0
 def test_same_value_case(self):
     with self.assertRaises(ValidationError) as cm:
         with allowed_percent(0.25, 0.25):  # <- Allows +25% only.
             raise ValidationError(self.differences)
     result_diffs = cm.exception.differences
     self.assertEqual({'aaa': Deviation(-1, 16), 'ccc': Deviation(+2, 16)}, result_diffs)
Esempio n. 29
0
    def test_integration_examples(self):
        # Test allowance of +/- 2 OR +/- 6%.
        with self.assertRaises(ValidationError) as cm:
            differences = [
                Deviation(+2, 1),   # 200%
                Deviation(+4, 8),   #  50%
                Deviation(+8, 32),  #  25%
            ]
            with allowed_deviation(2) | allowed_percent(0.25):
                raise ValidationError(differences)

        remaining = cm.exception.differences
        self.assertEqual(remaining, [Deviation(+4, 8)])

        # Test missing-type AND matching-value.
        with self.assertRaises(ValidationError) as cm:
            differences = [
                Missing('A'),
                Missing('B'),
                Extra('C'),
            ]
            with allowed_missing() & allowed_args(lambda x: x == 'A'):
                raise ValidationError(differences)

        remaining = cm.exception.differences
        self.assertEqual(remaining, [Missing('B'), Extra('C')])

        # Test missing-type OR allowed-limit.
        with self.assertRaises(ValidationError) as cm:
            differences = [
                Extra('A'),
                Missing('B'),
                Extra('C'),
                Missing('D'),
            ]
            with allowed_limit(1) | allowed_missing():
                raise ValidationError(differences)

        remaining = cm.exception.differences
        self.assertEqual(remaining, [Extra('C')])

        # Test missing-type AND allowed-limit.
        with self.assertRaises(ValidationError) as cm:
            differences = [
                Extra('A'),
                Missing('B'),
                Missing('C'),
            ]
            with allowed_limit(1) & allowed_missing():  # Allows only 1 missing.
                raise ValidationError(differences)

        remaining = cm.exception.differences
        self.assertEqual(remaining, [Extra('A'), Missing('C')])

        # Test missing-type OR allowed-limit.
        with self.assertRaises(ValidationError) as cm:
            differences = [
                Extra('A'),
                Missing('B'),
                Extra('C'),
                Missing('D'),
            ]
            with allowed_limit(1) | allowed_specific(Extra('A')):
                raise ValidationError(differences)

        remaining = cm.exception.differences
        self.assertEqual(remaining, [Extra('C'), Missing('D')])
Esempio n. 30
0
 def test_lower_upper_syntax(self):
     with self.assertRaises(ValidationError) as cm:
         with allowed_percent(0.0, 0.3):  # <- Allows from 0 to 30%.
             raise ValidationError(self.differences)
     result_diffs = cm.exception.differences
     self.assertEqual({'aaa': Deviation(-1, 16)}, result_diffs)