|
| 1 | +import contextlib |
| 2 | +from typing import Union, List |
| 3 | + |
1 | 4 | import math
|
2 | 5 |
|
3 | 6 | import pyproj
|
|
19 | 22 | BoundingBox,
|
20 | 23 | BoundingBoxException,
|
21 | 24 | CrsRequired,
|
| 25 | + validate_geojson_basic, |
22 | 26 | )
|
23 | 27 |
|
24 | 28 |
|
@@ -746,3 +750,169 @@ def test_best_utm(self):
|
746 | 750 |
|
747 | 751 | bbox = BoundingBox(-72, -13, -71, -12, crs="EPSG:4326")
|
748 | 752 | assert bbox.best_utm() == 32719
|
| 753 | + |
| 754 | + |
| 755 | +class TestValidateGeoJSON: |
| 756 | + @staticmethod |
| 757 | + @contextlib.contextmanager |
| 758 | + def _checker(expected_issue: Union[str, None], raise_exception: bool): |
| 759 | + """ |
| 760 | + Helper context manager to easily check a validate_geojson_basic result |
| 761 | + for both raise_exception modes: |
| 762 | +
|
| 763 | + - "exception mode": context manger __exit__ phase checks result |
| 764 | + - "return issue mode": returned `check` function should be used inside context manageer body |
| 765 | + """ |
| 766 | + checked = False |
| 767 | + |
| 768 | + def check(result: List[str]): |
| 769 | + """Check validation result in case no actual exception was thrown""" |
| 770 | + nonlocal checked |
| 771 | + checked = True |
| 772 | + if expected_issue: |
| 773 | + if raise_exception: |
| 774 | + pytest.fail("Exception should have been raised") |
| 775 | + if not result: |
| 776 | + pytest.fail("No issue was reported") |
| 777 | + assert expected_issue in "\n".join(result) |
| 778 | + else: |
| 779 | + if result: |
| 780 | + pytest.fail(f"Unexpected issue reported: {result}") |
| 781 | + |
| 782 | + try: |
| 783 | + yield check |
| 784 | + except Exception as e: |
| 785 | + # Check validation result in case of actual exception |
| 786 | + if not raise_exception: |
| 787 | + pytest.fail(f"Unexpected {e!r}: issue should be returned") |
| 788 | + if not expected_issue: |
| 789 | + pytest.fail(f"Unexpected {e!r}: no issue expected") |
| 790 | + assert expected_issue in str(e) |
| 791 | + else: |
| 792 | + # No exception was thrown: check that the `check` function has been called. |
| 793 | + if not checked: |
| 794 | + raise RuntimeError("`check` function was not used") |
| 795 | + |
| 796 | + @pytest.mark.parametrize( |
| 797 | + ["value", "expected_issue"], |
| 798 | + [ |
| 799 | + ("nope nope", "JSON object (mapping/dictionary) expected, but got str"), |
| 800 | + (123, "JSON object (mapping/dictionary) expected, but got int"), |
| 801 | + ({}, "No 'type' field"), |
| 802 | + ({"type": 123}, "Invalid 'type' type: int"), |
| 803 | + ({"type": {"Poly": "gon"}}, "Invalid 'type' type: dict"), |
| 804 | + ({"type": "meh"}, "Invalid type 'meh'"), |
| 805 | + ({"type": "Point"}, "No 'coordinates' field (type 'Point')"), |
| 806 | + ({"type": "Point", "coordinates": [1, 2]}, None), |
| 807 | + ({"type": "Polygon"}, "No 'coordinates' field (type 'Polygon')"), |
| 808 | + ({"type": "Polygon", "coordinates": [[1, 2]]}, None), |
| 809 | + ({"type": "MultiPolygon"}, "No 'coordinates' field (type 'MultiPolygon')"), |
| 810 | + ({"type": "MultiPolygon", "coordinates": [[[1, 2]]]}, None), |
| 811 | + ({"type": "GeometryCollection", "coordinates": []}, "No 'geometries' field (type 'GeometryCollection')"), |
| 812 | + ({"type": "GeometryCollection", "geometries": []}, None), |
| 813 | + ({"type": "Feature", "coordinates": []}, "No 'geometry' field (type 'Feature')"), |
| 814 | + ({"type": "Feature", "geometry": {}}, "No 'properties' field (type 'Feature')"), |
| 815 | + ({"type": "Feature", "geometry": {}, "properties": {}}, "No 'type' field"), |
| 816 | + ( |
| 817 | + {"type": "Feature", "geometry": {"type": "Polygon"}, "properties": {}}, |
| 818 | + "No 'coordinates' field (type 'Polygon')", |
| 819 | + ), |
| 820 | + ( |
| 821 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[1, 2]]}, "properties": {}}, |
| 822 | + None, |
| 823 | + ), |
| 824 | + ( |
| 825 | + {"type": "Feature", "geometry": {"type": "Polygonnnnn", "coordinates": [[1, 2]]}, "properties": {}}, |
| 826 | + "Found type 'Polygonnnnn', but expects one of ", |
| 827 | + ), |
| 828 | + ({"type": "FeatureCollection"}, "No 'features' field (type 'FeatureCollection')"), |
| 829 | + ({"type": "FeatureCollection", "features": []}, None), |
| 830 | + ({"type": "FeatureCollection", "features": [{"type": "Feature"}]}, "No 'geometry' field (type 'Feature')"), |
| 831 | + ( |
| 832 | + {"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {}}]}, |
| 833 | + "No 'properties' field (type 'Feature')", |
| 834 | + ), |
| 835 | + ( |
| 836 | + {"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {}, "properties": {}}]}, |
| 837 | + "No 'type' field", |
| 838 | + ), |
| 839 | + ( |
| 840 | + { |
| 841 | + "type": "FeatureCollection", |
| 842 | + "features": [{"type": "Feature", "geometry": {"type": "Polygon"}, "properties": {}}], |
| 843 | + }, |
| 844 | + "No 'coordinates' field (type 'Polygon')", |
| 845 | + ), |
| 846 | + ( |
| 847 | + { |
| 848 | + "type": "FeatureCollection", |
| 849 | + "features": [ |
| 850 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[1, 2]]}, "properties": {}}, |
| 851 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[3, 4]]}, "properties": {}}, |
| 852 | + ], |
| 853 | + }, |
| 854 | + None, |
| 855 | + ), |
| 856 | + ], |
| 857 | + ) |
| 858 | + @pytest.mark.parametrize("raise_exception", [False, True]) |
| 859 | + def test_validate_geojson_basic(self, value, expected_issue, raise_exception): |
| 860 | + with self._checker(expected_issue=expected_issue, raise_exception=raise_exception) as check: |
| 861 | + result = validate_geojson_basic(value, raise_exception=raise_exception) |
| 862 | + check(result) |
| 863 | + |
| 864 | + @pytest.mark.parametrize( |
| 865 | + ["value", "allowed_types", "expected_issue"], |
| 866 | + [ |
| 867 | + ( |
| 868 | + {"type": "Point", "coordinates": [1, 2]}, |
| 869 | + {"Polygon", "MultiPolygon"}, |
| 870 | + "Found type 'Point', but expects one of ['MultiPolygon', 'Polygon']", |
| 871 | + ), |
| 872 | + ({"type": "Polygon", "coordinates": [[1, 2]]}, {"Polygon", "MultiPolygon"}, None), |
| 873 | + ({"type": "MultiPolygon", "coordinates": [[[1, 2]]]}, {"Polygon", "MultiPolygon"}, None), |
| 874 | + ( |
| 875 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[1, 2]]}, "properties": {}}, |
| 876 | + {"Polygon", "MultiPolygon"}, |
| 877 | + "Found type 'Feature', but expects one of ['MultiPolygon', 'Polygon']", |
| 878 | + ), |
| 879 | + ( |
| 880 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[1, 2]]}, "properties": {}}, |
| 881 | + {"Feature"}, |
| 882 | + None, |
| 883 | + ), |
| 884 | + ( |
| 885 | + { |
| 886 | + "type": "FeatureCollection", |
| 887 | + "features": [ |
| 888 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[1, 2]]}, "properties": {}}, |
| 889 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[3, 4]]}, "properties": {}}, |
| 890 | + ], |
| 891 | + }, |
| 892 | + {"Polygon", "MultiPolygon"}, |
| 893 | + "Found type 'FeatureCollection', but expects one of ['MultiPolygon', 'Polygon']", |
| 894 | + ), |
| 895 | + ( |
| 896 | + { |
| 897 | + "type": "FeatureCollection", |
| 898 | + "features": [ |
| 899 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[1, 2]]}, "properties": {}}, |
| 900 | + {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[3, 4]]}, "properties": {}}, |
| 901 | + ], |
| 902 | + }, |
| 903 | + {"FeatureCollection"}, |
| 904 | + None, |
| 905 | + ), |
| 906 | + ], |
| 907 | + ) |
| 908 | + @pytest.mark.parametrize( |
| 909 | + "raise_exception", |
| 910 | + [ |
| 911 | + False, |
| 912 | + True, |
| 913 | + ], |
| 914 | + ) |
| 915 | + def test_validate_geojson_basic_allowed_types(self, value, allowed_types, expected_issue, raise_exception): |
| 916 | + with self._checker(expected_issue=expected_issue, raise_exception=raise_exception) as check: |
| 917 | + result = validate_geojson_basic(value, allowed_types=allowed_types, raise_exception=raise_exception) |
| 918 | + check(result) |
0 commit comments