예제 #1
0
    def test_duplicates(self):
        # Three of the exact-same differences.
        differences = [Extra('xxx'), Extra('xxx'), Extra('xxx')]

        # Only allow one of them.
        with self.assertRaises(ValidationError) as cm:
            allowed = [Extra('xxx')]
            with allowed_specific(allowed):
                raise ValidationError(differences)

        actual = list(cm.exception.differences)
        expected = [Extra('xxx'), Extra('xxx')]  # Expect two remaining.
        self.assertEqual(actual, expected)

        # Only allow two of them.
        with self.assertRaises(ValidationError) as cm:
            allowed = [Extra('xxx'), Extra('xxx')]
            with allowed_specific(allowed):
                raise ValidationError(differences)

        actual = list(cm.exception.differences)
        expected = [Extra('xxx')]  # Expect one remaining.
        self.assertEqual(actual, expected)

        # Allow all three.
        allowed = [Extra('xxx'), Extra('xxx'), Extra('xxx')]
        with allowed_specific(allowed):
            raise ValidationError(differences)
예제 #2
0
    def test_percent_empty_value_handling(self):
        # Test empty deviation cases--should pass without error.
        with AcceptedPercent(0):  # <- Accepts empty deviations only.
            raise ValidationError([
                Invalid(None, 0),
                Invalid('', 0),
            ])

        # Test diffs that can not be accepted as percentages.
        with self.assertRaises(ValidationError) as cm:
            with AcceptedPercent(2.00):  # <- Accepts +/- 200%.
                raise ValidationError([
                    Invalid(None, 0),             # 0%
                    Invalid(0, None),             # 0%
                    Deviation(+2, 0),             # Can not be accepted by percent.
                    Invalid(+2, None),            # Can not be accepted by percent.
                    Deviation(float('nan'), 16),  # Not a number.
                ])
        actual = cm.exception.differences
        expected = [
            Deviation(+2, 0),             # Can not be accepted by percent.
            Invalid(2, None),             # Can not be accepted by percent.
            Deviation(float('nan'), 16),  # Not a number.
        ]
        self.assertEqual(actual, expected)
예제 #3
0
    def test_special_values(self):
        # Test empty deviation cases--should pass without error.
        with allowed_percent(0):  # <- Allows empty deviations only.
            raise ValidationError([
                Deviation(None, 0),
                Deviation('', 0),
            ])

        # Test diffs that can not be allowed as percentages.
        with self.assertRaises(ValidationError) as cm:
            with allowed_percent(2.00):  # <- Allows +/- 200%.
                raise ValidationError([
                    Deviation(None, 0),           # 0%
                    Deviation(0, None),           # 0%
                    Deviation(+2, 0),             # Can not be allowed by percent.
                    Deviation(+2, None),          # Can not be allowed by percent.
                    Deviation(float('nan'), 16),  # Not a number.
                ])
        actual = cm.exception.differences
        expected = [
            Deviation(+2, 0),             # Can not be allowed by percent.
            Deviation(+2, None),          # Can not be allowed by percent.
            Deviation(float('nan'), 16),  # Not a number.
        ]
        self.assertEqual(actual, expected)
예제 #4
0
    def test_exit_context(self):
        """The __exit__() method should re-raise exceptions that are
        not allowed and it should return True when there are no errors
        or if all differences have been allowed (see PEP 343 for
        context manager protocol).
        """
        try:
            raise ValidationError([Missing('A'), Extra('B')], 'error description')
        except ValidationError:
            type, value, traceback = sys.exc_info()  # Get exception info.

        with self.assertRaises(ValidationError) as cm:
            allowance = MinimalAllowance('allowance message')
            allowance.__exit__(type, value, traceback)

        description = cm.exception.description
        self.assertEqual(description, 'allowance message: error description')

        # Test with no error description.
        try:
            raise ValidationError([Missing('A'), Extra('B')])  # <- No description.
        except ValidationError:
            type, value, traceback = sys.exc_info()  # Get exception info.

        with self.assertRaises(ValidationError) as cm:
            allowance = MinimalAllowance('allowance message')
            allowance.__exit__(type, value, traceback)

        description = cm.exception.description
        self.assertEqual(description, 'allowance message')
예제 #5
0
    def test_failing(self):
        with self.assertRaises(ValidationError) as cm:
            with AcceptedFuzzy(cutoff=0.7):
                raise ValidationError(self.differences)
        remaining = cm.exception.differences
        self.assertEqual(remaining, [Invalid('bbyy', 'bbbb')])

        with self.assertRaises(ValidationError) as cm:
            with AcceptedFuzzy(cutoff=0.8):
                raise ValidationError(self.differences)
        remaining = cm.exception.differences
        self.assertEqual(remaining, self.differences, msg='none accepted')
예제 #6
0
    def test_percent_error(self):
        # Test "tolerance" syntax.
        with self.assertRaises(ValidationError) as cm:
            with AcceptedPercent(0.2):  # <- Accepts +/- 20%.
                raise ValidationError(self.differences)
        remaining = cm.exception.differences
        self.assertEqual(remaining, {'bbb': Deviation(+4, 16)})

        # Test "upper/lower" syntax.
        with self.assertRaises(ValidationError) as cm:
            with AcceptedPercent(0.0, 0.3):  # <- Accepts from 0 to 30%.
                raise ValidationError(self.differences)
        result_diffs = cm.exception.differences
        self.assertEqual({'aaa': Deviation(-1, 16)}, result_diffs)
예제 #7
0
    def test_over_limit(self):
        with self.assertRaises(ValidationError) as cm:
            with allowed_limit(1):  # <- Allows 1 but there are 2.
                raise ValidationError([Extra('xxx'), Missing('yyy')])

        remaining = list(cm.exception.differences)
        self.assertEqual(remaining, [Missing('yyy')])

        with self.assertRaises(ValidationError) as cm:
            with allowed_limit(1):  # <- Allows 1 and there are 2.
                raise ValidationError({'foo': Extra('xxx'), 'bar': Missing('yyy')})

        remaining = cm.exception.differences
        self.assertIsInstance(remaining, collections.Mapping)
        self.assertEqual(len(remaining), 1)
예제 #8
0
    def test_UnionedAllowance(self):
        original_diffs = [Missing('a'), Extra('a'), Missing('b'), Extra('b')]

        with self.assertRaises(ValidationError) as cm:
            with UnionedAllowance(self.allowed_missing, self.allowed_letter_a):
                raise ValidationError(original_diffs)
        differences = cm.exception.differences
        self.assertEqual(list(differences), [Extra('b')])

        # Test with allowances in reverse-order (should give same result).
        with self.assertRaises(ValidationError) as cm:
            with UnionedAllowance(self.allowed_letter_a, self.allowed_missing):
                raise ValidationError(original_diffs)
        differences = cm.exception.differences
        self.assertEqual(list(differences), [Extra('b')])
예제 #9
0
    def test_error_iter(self):
        error_list = [MinimalDifference('A'), MinimalDifference('B')]
        error_iter = iter(error_list)

        err = ValidationError(error_iter)
        self.assertEqual(err.differences, error_list,
                         'iterable should be converted to list')
예제 #10
0
    def test_iter_of_diffs(self):
        diff_list = [MinimalDifference('A'), MinimalDifference('B')]
        diff_iter = iter(diff_list)

        err = ValidationError(diff_iter)
        self.assertEqual(err.differences, diff_list,
                         'iterable should be converted to list')
예제 #11
0
    def test_dict_with_predicates(self):
        """Ellipsis wildcard key matches all, treats as a single group."""
        differences = {
            'foo': Extra('xxx'),
            'bar': [Extra('yyy'), Missing('yyy')],
            'baz': [Extra('zzz'), Missing('zzz')],
        }

        allowed = {
            lambda x: x.startswith('ba'): [
                Extra('yyy'),
                Extra('zzz'),
            ],
        }

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

        actual = cm.exception.differences
        expected = {
            'foo': Extra('xxx'),
            'bar': Missing('yyy'),
            'baz': Missing('zzz'),
        }
        self.assertEqual(actual, expected)
예제 #12
0
    def test_non_deviation_diffs(self):
        diffs = [Missing('foo'), Extra('bar'), Invalid('baz')]
        with self.assertRaises(ValidationError) as cm:
            with AcceptedPercent(0.05):
                raise ValidationError(diffs)

        uncaught_diffs = cm.exception.differences
        self.assertEqual(diffs, uncaught_diffs)
예제 #13
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
예제 #14
0
    def test_allowed_invalid(self):
        differences =  [Invalid('X'), Invalid('Y'), Extra('Z')]

        with self.assertRaises(ValidationError) as cm:
            with allowed_invalid():  # <- Apply allowance!
                raise ValidationError(differences)
        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Extra('Z')])
예제 #15
0
    def test_dict_of_lists(self):
        diff_dict = {
            'a': [MinimalDifference('A')],
            'b': [MinimalDifference('B')]
        }

        err = ValidationError(diff_dict)
        self.assertEqual(err.differences, diff_dict)
예제 #16
0
    def test_accepted_extra(self):
        differences = [Extra('X'), Extra('Y'), Missing('X')]

        with self.assertRaises(ValidationError) as cm:
            with AcceptedDifferences(Extra):  # <- Apply acceptance!
                raise ValidationError(differences)
        remaining_diffs = cm.exception.differences
        self.assertEqual(list(remaining_diffs), [Missing('X')])
예제 #17
0
    def test_iteritems_of_iters(self):
        dict_of_lists = {
            'a': [MinimalDifference('A')],
            'b': [MinimalDifference('B')]
        }
        iteritems_of_iters = ((k, iter(v)) for k, v in dict_of_lists.items())

        err = ValidationError(iteritems_of_iters)
        self.assertEqual(err.differences, dict_of_lists)
예제 #18
0
 def test_same_value_case(self):
     with self.assertRaises(ValidationError) as cm:
         with AcceptedPercent(0.25, 0.25):  # <- Accepts +25% only.
             raise ValidationError(self.differences)
     result_diffs = cm.exception.differences
     self.assertEqual({
         'aaa': Deviation(-1, 16),
         'ccc': Deviation(+2, 16)
     }, result_diffs)
예제 #19
0
 def test_same_value_case(self):
     with self.assertRaises(ValidationError) as cm:
         with AcceptedTolerance(4, 4):  # <- Accepts off-by-4 only.
             raise ValidationError(self.differences)
     result_diffs = cm.exception.differences
     self.assertEqual({
         'aaa': Deviation(-1, 16),
         'ccc': Deviation(+2, 16)
     }, result_diffs)
예제 #20
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)
예제 #21
0
 def test_extra_deviation_percent(self):
     with self.assertRaises(ValidationError) as cm:
         with AcceptedPercent(2.0):  # <- Accepts +/- 200%.
             raise ValidationError([
                 Extra(-1),  # <- Rejected: Can not be accepted by percent.
                 Extra(0),  # <- ACCEPTED!
                 Extra(2),  # <- Rejected: Can not be accepted by percent.
             ])
     remaining = cm.exception.differences
     self.assertEqual(remaining, [Extra(-1), Extra(2)])
예제 #22
0
    def test_difference_vs_list(self):
        differences = Missing('X')
        acceptance = AcceptedDifferences([Missing('Y'), Missing('Z')])
        expected = [Missing('X')]
        self.assertAcceptance(differences, acceptance, expected)

        differences = Missing('X')
        acceptance = AcceptedDifferences([Missing('X'), Missing('Y')])
        with acceptance:  # <- No error, all diffs accepted
            raise ValidationError(differences)
예제 #23
0
    def test_difference_vs_type(self):
        differences = Missing('X')
        acceptance = AcceptedDifferences(Extra)
        expected = [Missing('X')]
        self.assertAcceptance(differences, acceptance, expected)

        differences = Missing('X')
        acceptance = AcceptedDifferences(Missing)
        with acceptance:  # <- No error, all diffs accepted
            raise ValidationError(differences)
예제 #24
0
 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)])
예제 #25
0
    def test_dict_and_dict(self):
        differences = {'foo': Extra('xxx'), 'bar': [Extra('xxx'), Missing('yyy')]}
        allowed = {'bar': Extra('xxx')}

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

        actual = cm.exception.differences
        expected = {'foo': Extra('xxx'), 'bar': Missing('yyy')}
        self.assertEqual(actual, expected)
예제 #26
0
    def test_list_and_diff(self):
        differences = [Extra('xxx'), Missing('yyy')]
        allowed = Extra('xxx')  # <- Single diff, not in a container.

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

        actual = list(cm.exception.differences)
        expected = [Missing('yyy')]
        self.assertEqual(actual, expected)
예제 #27
0
    def test_list_and_list(self):
        differences = [Extra('xxx'), Missing('yyy')]
        allowed = [Extra('xxx')]
        expected = [Missing('yyy')]

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

        actual = list(cm.exception.differences)
        self.assertEqual(actual, expected)
예제 #28
0
    def test_composite_key(self):
        with self.assertRaises(ValidationError) as cm:

            with allowed_keys(('a', 7)):  # <- Allow 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)})
예제 #29
0
    def test_allow_string(self):
        with self.assertRaises(ValidationError) as cm:

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

        remaining_diffs = cm.exception.differences
        self.assertEqual(dict(remaining_diffs), {'bbb': Missing(2)})
예제 #30
0
 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)])