Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
test_exceptions.py760 linesDownload Raw Back to tests
1from unittest import TestCase2import textwrap3 4import jsonpath_ng5 6from jsonschema import exceptions7from jsonschema.validators import _LATEST_VERSION8 9 10class TestBestMatch(TestCase):11    def best_match_of(self, instance, schema):12        errors = list(_LATEST_VERSION(schema).iter_errors(instance))13        msg =  f"No errors found for {instance} under {schema!r}!"14        self.assertTrue(errors, msg=msg)15 16        best = exceptions.best_match(iter(errors))17        reversed_best = exceptions.best_match(reversed(errors))18 19        self.assertEqual(20            best._contents(),21            reversed_best._contents(),22            f"No consistent best match!\nGot: {best}\n\nThen: {reversed_best}",23        )24        return best25 26    def test_shallower_errors_are_better_matches(self):27        schema = {28            "properties": {29                "foo": {30                    "minProperties": 2,31                    "properties": {"bar": {"type": "object"}},32                },33            },34        }35        best = self.best_match_of(instance={"foo": {"bar": []}}, schema=schema)36        self.assertEqual(best.validator, "minProperties")37 38    def test_oneOf_and_anyOf_are_weak_matches(self):39        """40        A property you *must* match is probably better than one you have to41        match a part of.42        """43 44        schema = {45            "minProperties": 2,46            "anyOf": [{"type": "string"}, {"type": "number"}],47            "oneOf": [{"type": "string"}, {"type": "number"}],48        }49        best = self.best_match_of(instance={}, schema=schema)50        self.assertEqual(best.validator, "minProperties")51 52    def test_if_the_most_relevant_error_is_anyOf_it_is_traversed(self):53        """54        If the most relevant error is an anyOf, then we traverse its context55        and select the otherwise *least* relevant error, since in this case56        that means the most specific, deep, error inside the instance.57 58        I.e. since only one of the schemas must match, we look for the most59        relevant one.60        """61 62        schema = {63            "properties": {64                "foo": {65                    "anyOf": [66                        {"type": "string"},67                        {"properties": {"bar": {"type": "array"}}},68                    ],69                },70            },71        }72        best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema)73        self.assertEqual(best.validator_value, "array")74 75    def test_no_anyOf_traversal_for_equally_relevant_errors(self):76        """77        We don't traverse into an anyOf (as above) if all of its context errors78        seem to be equally "wrong" against the instance.79        """80 81        schema = {82            "anyOf": [83                {"type": "string"},84                {"type": "integer"},85                {"type": "object"},86            ],87        }88        best = self.best_match_of(instance=[], schema=schema)89        self.assertEqual(best.validator, "anyOf")90 91    def test_anyOf_traversal_for_single_equally_relevant_error(self):92        """93        We *do* traverse anyOf with a single nested error, even though it is94        vacuously equally relevant to itself.95        """96 97        schema = {98            "anyOf": [99                {"type": "string"},100            ],101        }102        best = self.best_match_of(instance=[], schema=schema)103        self.assertEqual(best.validator, "type")104 105    def test_anyOf_traversal_for_single_sibling_errors(self):106        """107        We *do* traverse anyOf with a single subschema that fails multiple108        times (e.g. on multiple items).109        """110 111        schema = {112            "anyOf": [113                {"items": {"const": 37}},114            ],115        }116        best = self.best_match_of(instance=[12, 12], schema=schema)117        self.assertEqual(best.validator, "const")118 119    def test_anyOf_traversal_for_non_type_matching_sibling_errors(self):120        """121        We *do* traverse anyOf with multiple subschemas when one does not type122        match.123        """124 125        schema = {126            "anyOf": [127                {"type": "object"},128                {"items": {"const": 37}},129            ],130        }131        best = self.best_match_of(instance=[12, 12], schema=schema)132        self.assertEqual(best.validator, "const")133 134    def test_if_the_most_relevant_error_is_oneOf_it_is_traversed(self):135        """136        If the most relevant error is an oneOf, then we traverse its context137        and select the otherwise *least* relevant error, since in this case138        that means the most specific, deep, error inside the instance.139 140        I.e. since only one of the schemas must match, we look for the most141        relevant one.142        """143 144        schema = {145            "properties": {146                "foo": {147                    "oneOf": [148                        {"type": "string"},149                        {"properties": {"bar": {"type": "array"}}},150                    ],151                },152            },153        }154        best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema)155        self.assertEqual(best.validator_value, "array")156 157    def test_no_oneOf_traversal_for_equally_relevant_errors(self):158        """159        We don't traverse into an oneOf (as above) if all of its context errors160        seem to be equally "wrong" against the instance.161        """162 163        schema = {164            "oneOf": [165                {"type": "string"},166                {"type": "integer"},167                {"type": "object"},168            ],169        }170        best = self.best_match_of(instance=[], schema=schema)171        self.assertEqual(best.validator, "oneOf")172 173    def test_oneOf_traversal_for_single_equally_relevant_error(self):174        """175        We *do* traverse oneOf with a single nested error, even though it is176        vacuously equally relevant to itself.177        """178 179        schema = {180            "oneOf": [181                {"type": "string"},182            ],183        }184        best = self.best_match_of(instance=[], schema=schema)185        self.assertEqual(best.validator, "type")186 187    def test_oneOf_traversal_for_single_sibling_errors(self):188        """189        We *do* traverse oneOf with a single subschema that fails multiple190        times (e.g. on multiple items).191        """192 193        schema = {194            "oneOf": [195                {"items": {"const": 37}},196            ],197        }198        best = self.best_match_of(instance=[12, 12], schema=schema)199        self.assertEqual(best.validator, "const")200 201    def test_oneOf_traversal_for_non_type_matching_sibling_errors(self):202        """203        We *do* traverse oneOf with multiple subschemas when one does not type204        match.205        """206 207        schema = {208            "oneOf": [209                {"type": "object"},210                {"items": {"const": 37}},211            ],212        }213        best = self.best_match_of(instance=[12, 12], schema=schema)214        self.assertEqual(best.validator, "const")215 216    def test_if_the_most_relevant_error_is_allOf_it_is_traversed(self):217        """218        Now, if the error is allOf, we traverse but select the *most* relevant219        error from the context, because all schemas here must match anyways.220        """221 222        schema = {223            "properties": {224                "foo": {225                    "allOf": [226                        {"type": "string"},227                        {"properties": {"bar": {"type": "array"}}},228                    ],229                },230            },231        }232        best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema)233        self.assertEqual(best.validator_value, "string")234 235    def test_nested_context_for_oneOf(self):236        """237        We traverse into nested contexts (a oneOf containing an error in a238        nested oneOf here).239        """240 241        schema = {242            "properties": {243                "foo": {244                    "oneOf": [245                        {"type": "string"},246                        {247                            "oneOf": [248                                {"type": "string"},249                                {250                                    "properties": {251                                        "bar": {"type": "array"},252                                    },253                                },254                            ],255                        },256                    ],257                },258            },259        }260        best = self.best_match_of(instance={"foo": {"bar": 12}}, schema=schema)261        self.assertEqual(best.validator_value, "array")262 263    def test_it_prioritizes_matching_types(self):264        schema = {265            "properties": {266                "foo": {267                    "anyOf": [268                        {"type": "array", "minItems": 2},269                        {"type": "string", "minLength": 10},270                    ],271                },272            },273        }274        best = self.best_match_of(instance={"foo": "bar"}, schema=schema)275        self.assertEqual(best.validator, "minLength")276 277        reordered = {278            "properties": {279                "foo": {280                    "anyOf": [281                        {"type": "string", "minLength": 10},282                        {"type": "array", "minItems": 2},283                    ],284                },285            },286        }287        best = self.best_match_of(instance={"foo": "bar"}, schema=reordered)288        self.assertEqual(best.validator, "minLength")289 290    def test_it_prioritizes_matching_union_types(self):291        schema = {292            "properties": {293                "foo": {294                    "anyOf": [295                        {"type": ["array", "object"], "minItems": 2},296                        {"type": ["integer", "string"], "minLength": 10},297                    ],298                },299            },300        }301        best = self.best_match_of(instance={"foo": "bar"}, schema=schema)302        self.assertEqual(best.validator, "minLength")303 304        reordered = {305            "properties": {306                "foo": {307                    "anyOf": [308                        {"type": "string", "minLength": 10},309                        {"type": "array", "minItems": 2},310                    ],311                },312            },313        }314        best = self.best_match_of(instance={"foo": "bar"}, schema=reordered)315        self.assertEqual(best.validator, "minLength")316 317    def test_boolean_schemas(self):318        schema = {"properties": {"foo": False}}319        best = self.best_match_of(instance={"foo": "bar"}, schema=schema)320        self.assertIsNone(best.validator)321 322    def test_one_error(self):323        validator = _LATEST_VERSION({"minProperties": 2})324        validator.iter_errors({})325        self.assertEqual(326            exceptions.best_match(validator.iter_errors({})).validator,327            "minProperties",328        )329 330    def test_no_errors(self):331        validator = _LATEST_VERSION({})332        self.assertIsNone(exceptions.best_match(validator.iter_errors({})))333 334 335class TestByRelevance(TestCase):336    def test_short_paths_are_better_matches(self):337        shallow = exceptions.ValidationError("Oh no!", path=["baz"])338        deep = exceptions.ValidationError("Oh yes!", path=["foo", "bar"])339        match = max([shallow, deep], key=exceptions.relevance)340        self.assertIs(match, shallow)341 342        match = max([deep, shallow], key=exceptions.relevance)343        self.assertIs(match, shallow)344 345    def test_global_errors_are_even_better_matches(self):346        shallow = exceptions.ValidationError("Oh no!", path=[])347        deep = exceptions.ValidationError("Oh yes!", path=["foo"])348 349        errors = sorted([shallow, deep], key=exceptions.relevance)350        self.assertEqual(351            [list(error.path) for error in errors],352            [["foo"], []],353        )354 355        errors = sorted([deep, shallow], key=exceptions.relevance)356        self.assertEqual(357            [list(error.path) for error in errors],358            [["foo"], []],359        )360 361    def test_weak_keywords_are_lower_priority(self):362        weak = exceptions.ValidationError("Oh no!", path=[], validator="a")363        normal = exceptions.ValidationError("Oh yes!", path=[], validator="b")364 365        best_match = exceptions.by_relevance(weak="a")366 367        match = max([weak, normal], key=best_match)368        self.assertIs(match, normal)369 370        match = max([normal, weak], key=best_match)371        self.assertIs(match, normal)372 373    def test_strong_keywords_are_higher_priority(self):374        weak = exceptions.ValidationError("Oh no!", path=[], validator="a")375        normal = exceptions.ValidationError("Oh yes!", path=[], validator="b")376        strong = exceptions.ValidationError("Oh fine!", path=[], validator="c")377 378        best_match = exceptions.by_relevance(weak="a", strong="c")379 380        match = max([weak, normal, strong], key=best_match)381        self.assertIs(match, strong)382 383        match = max([strong, normal, weak], key=best_match)384        self.assertIs(match, strong)385 386 387class TestErrorTree(TestCase):388    def test_it_knows_how_many_total_errors_it_contains(self):389        # FIXME: #442390        errors = [391            exceptions.ValidationError("Something", validator=i)392            for i in range(8)393        ]394        tree = exceptions.ErrorTree(errors)395        self.assertEqual(tree.total_errors, 8)396 397    def test_it_contains_an_item_if_the_item_had_an_error(self):398        errors = [exceptions.ValidationError("a message", path=["bar"])]399        tree = exceptions.ErrorTree(errors)400        self.assertIn("bar", tree)401 402    def test_it_does_not_contain_an_item_if_the_item_had_no_error(self):403        errors = [exceptions.ValidationError("a message", path=["bar"])]404        tree = exceptions.ErrorTree(errors)405        self.assertNotIn("foo", tree)406 407    def test_keywords_that_failed_appear_in_errors_dict(self):408        error = exceptions.ValidationError("a message", validator="foo")409        tree = exceptions.ErrorTree([error])410        self.assertEqual(tree.errors, {"foo": error})411 412    def test_it_creates_a_child_tree_for_each_nested_path(self):413        errors = [414            exceptions.ValidationError("a bar message", path=["bar"]),415            exceptions.ValidationError("a bar -> 0 message", path=["bar", 0]),416        ]417        tree = exceptions.ErrorTree(errors)418        self.assertIn(0, tree["bar"])419        self.assertNotIn(1, tree["bar"])420 421    def test_children_have_their_errors_dicts_built(self):422        e1, e2 = (423            exceptions.ValidationError("1", validator="foo", path=["bar", 0]),424            exceptions.ValidationError("2", validator="quux", path=["bar", 0]),425        )426        tree = exceptions.ErrorTree([e1, e2])427        self.assertEqual(tree["bar"][0].errors, {"foo": e1, "quux": e2})428 429    def test_multiple_errors_with_instance(self):430        e1, e2 = (431            exceptions.ValidationError(432                "1",433                validator="foo",434                path=["bar", "bar2"],435                instance="i1"),436            exceptions.ValidationError(437                "2",438                validator="quux",439                path=["foobar", 2],440                instance="i2"),441        )442        exceptions.ErrorTree([e1, e2])443 444    def test_it_does_not_contain_subtrees_that_are_not_in_the_instance(self):445        error = exceptions.ValidationError("123", validator="foo", instance=[])446        tree = exceptions.ErrorTree([error])447 448        with self.assertRaises(IndexError):449            tree[0]450 451    def test_if_its_in_the_tree_anyhow_it_does_not_raise_an_error(self):452        """453        If a keyword refers to a path that isn't in the instance, the454        tree still properly returns a subtree for that path.455        """456 457        error = exceptions.ValidationError(458            "a message", validator="foo", instance={}, path=["foo"],459        )460        tree = exceptions.ErrorTree([error])461        self.assertIsInstance(tree["foo"], exceptions.ErrorTree)462 463    def test_iter(self):464        e1, e2 = (465            exceptions.ValidationError(466                "1",467                validator="foo",468                path=["bar", "bar2"],469                instance="i1"),470            exceptions.ValidationError(471                "2",472                validator="quux",473                path=["foobar", 2],474                instance="i2"),475        )476        tree = exceptions.ErrorTree([e1, e2])477        self.assertEqual(set(tree), {"bar", "foobar"})478 479    def test_repr_single(self):480        error = exceptions.ValidationError(481            "1",482            validator="foo",483            path=["bar", "bar2"],484            instance="i1",485        )486        tree = exceptions.ErrorTree([error])487        self.assertEqual(repr(tree), "<ErrorTree (1 total error)>")488 489    def test_repr_multiple(self):490        e1, e2 = (491            exceptions.ValidationError(492                "1",493                validator="foo",494                path=["bar", "bar2"],495                instance="i1"),496            exceptions.ValidationError(497                "2",498                validator="quux",499                path=["foobar", 2],500                instance="i2"),501        )502        tree = exceptions.ErrorTree([e1, e2])503        self.assertEqual(repr(tree), "<ErrorTree (2 total errors)>")504 505    def test_repr_empty(self):506        tree = exceptions.ErrorTree([])507        self.assertEqual(repr(tree), "<ErrorTree (0 total errors)>")508 509 510class TestErrorInitReprStr(TestCase):511    def make_error(self, **kwargs):512        defaults = dict(513            message="hello",514            validator="type",515            validator_value="string",516            instance=5,517            schema={"type": "string"},518        )519        defaults.update(kwargs)520        return exceptions.ValidationError(**defaults)521 522    def assertShows(self, expected, **kwargs):523        expected = textwrap.dedent(expected).rstrip("\n")524 525        error = self.make_error(**kwargs)526        message_line, _, rest = str(error).partition("\n")527        self.assertEqual(message_line, error.message)528        self.assertEqual(rest, expected)529 530    def test_it_calls_super_and_sets_args(self):531        error = self.make_error()532        self.assertGreater(len(error.args), 1)533 534    def test_repr(self):535        self.assertEqual(536            repr(exceptions.ValidationError(message="Hello!")),537            "<ValidationError: 'Hello!'>",538        )539 540    def test_unset_error(self):541        error = exceptions.ValidationError("message")542        self.assertEqual(str(error), "message")543 544        kwargs = {545            "validator": "type",546            "validator_value": "string",547            "instance": 5,548            "schema": {"type": "string"},549        }550        # Just the message should show if any of the attributes are unset551        for attr in kwargs:552            k = dict(kwargs)553            del k[attr]554            error = exceptions.ValidationError("message", **k)555            self.assertEqual(str(error), "message")556 557    def test_empty_paths(self):558        self.assertShows(559            """560            Failed validating 'type' in schema:561                {'type': 'string'}562 563            On instance:564                5565            """,566            path=[],567            schema_path=[],568        )569 570    def test_one_item_paths(self):571        self.assertShows(572            """573            Failed validating 'type' in schema:574                {'type': 'string'}575 576            On instance[0]:577                5578            """,579            path=[0],580            schema_path=["items"],581        )582 583    def test_multiple_item_paths(self):584        self.assertShows(585            """586            Failed validating 'type' in schema['items'][0]:587                {'type': 'string'}588 589            On instance[0]['a']:590                5591            """,592            path=[0, "a"],593            schema_path=["items", 0, 1],594        )595 596    def test_uses_pprint(self):597        self.assertShows(598            """599            Failed validating 'maxLength' in schema:600                {0: 0,601                 1: 1,602                 2: 2,603                 3: 3,604                 4: 4,605                 5: 5,606                 6: 6,607                 7: 7,608                 8: 8,609                 9: 9,610                 10: 10,611                 11: 11,612                 12: 12,613                 13: 13,614                 14: 14,615                 15: 15,616                 16: 16,617                 17: 17,618                 18: 18,619                 19: 19}620 621            On instance:622                [0,623                 1,624                 2,625                 3,626                 4,627                 5,628                 6,629                 7,630                 8,631                 9,632                 10,633                 11,634                 12,635                 13,636                 14,637                 15,638                 16,639                 17,640                 18,641                 19,642                 20,643                 21,644                 22,645                 23,646                 24]647            """,648            instance=list(range(25)),649            schema=dict(zip(range(20), range(20))),650            validator="maxLength",651        )652 653    def test_does_not_reorder_dicts(self):654        self.assertShows(655            """656            Failed validating 'type' in schema:657                {'do': 3, 'not': 7, 'sort': 37, 'me': 73}658 659            On instance:660                {'here': 73, 'too': 37, 'no': 7, 'sorting': 3}661            """,662            schema={663                "do": 3,664                "not": 7,665                "sort": 37,666                "me": 73,667            },668            instance={669                "here": 73,670                "too": 37,671                "no": 7,672                "sorting": 3,673            },674        )675 676    def test_str_works_with_instances_having_overriden_eq_operator(self):677        """678        Check for #164 which rendered exceptions unusable when a679        `ValidationError` involved instances with an `__eq__` method680        that returned truthy values.681        """682 683        class DontEQMeBro:684            def __eq__(this, other):  # pragma: no cover685                self.fail("Don't!")686 687            def __ne__(this, other):  # pragma: no cover688                self.fail("Don't!")689 690        instance = DontEQMeBro()691        error = exceptions.ValidationError(692            "a message",693            validator="foo",694            instance=instance,695            validator_value="some",696            schema="schema",697        )698        self.assertIn(repr(instance), str(error))699 700 701class TestHashable(TestCase):702    def test_hashable(self):703        {exceptions.ValidationError("")}704        {exceptions.SchemaError("")}705 706 707class TestJsonPathRendering(TestCase):708    def validate_json_path_rendering(self, property_name, expected_path):709        error = exceptions.ValidationError(710            path=[property_name],711            message="1",712            validator="foo",713            instance="i1",714        )715 716        rendered_json_path = error.json_path717        self.assertEqual(rendered_json_path, expected_path)718 719        re_parsed_name = jsonpath_ng.parse(rendered_json_path).right.fields[0]720        self.assertEqual(re_parsed_name, property_name)721 722    def test_basic(self):723        self.validate_json_path_rendering("x", "$.x")724 725    def test_empty(self):726        self.validate_json_path_rendering("", "$['']")727 728    def test_number(self):729        self.validate_json_path_rendering("1", "$['1']")730 731    def test_period(self):732        self.validate_json_path_rendering(".", "$['.']")733 734    def test_single_quote(self):735        self.validate_json_path_rendering("'", r"$['\'']")736 737    def test_space(self):738        self.validate_json_path_rendering(" ", "$[' ']")739 740    def test_backslash(self):741        self.validate_json_path_rendering("\\", r"$['\\']")742 743    def test_backslash_single_quote(self):744        self.validate_json_path_rendering(r"\'", r"$['\\\'']")745 746    def test_underscore(self):747        self.validate_json_path_rendering("_", r"$['_']")748 749    def test_double_quote(self):750        self.validate_json_path_rendering('"', """$['"']""")751 752    def test_hyphen(self):753        self.validate_json_path_rendering("-", "$['-']")754 755    def test_json_path_injection(self):756        self.validate_json_path_rendering("a[0]", "$['a[0]']")757 758    def test_open_bracket(self):759        self.validate_json_path_rendering("[", "$['[']")760 
codekingpro/portable-devtools · Team Ai