"""
unit tests for restframework_gis
"""

import json
import pickle
from html import escape

from django.contrib.gis.geos import GEOSGeometry, Point
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from django.urls import reverse

from rest_framework_gis import serializers as gis_serializers
from rest_framework_gis.fields import GeoJsonDict

from .models import LocatedFile, Location, Nullable, OtherSridLocation
from .serializers import LocationGeoSerializer


class TestRestFrameworkGis(TestCase):
    def setUp(self):
        self.location_list_url = reverse("api_location_list")
        self.geojson_location_list_url = reverse("api_geojson_location_list")
        self.geos_error_message = (
            "Invalid format: string or unicode input unrecognized as GeoJSON,"
            " WKT EWKT or HEXEWKB."
        )
        self.gdal_error_message = (
            "Unable to convert to python object:"
            ' Invalid geometry pointer returned from "OGR_G_CreateGeometryFromJson".'
        )
        self.value_error_message = (
            "Unable to convert to python object:"
            " String input unrecognized as WKT EWKT, and HEXEWKB."
        )
        self.type_error_message = (
            "Unable to convert to python object: Improper geometry input type:"
        )

    def _create_locations(self):
        self.l1 = Location.objects.create(
            id=1,
            name="l1",
            slug="l1",
            geometry="POINT (13.0078125000020002 42.4234565179379999)",
        )
        self.l2 = Location.objects.create(
            id=2,
            name="l2",
            slug="l2",
            geometry="POINT (12.0078125000020002 43.4234565179379999)",
        )

    def test_get_location_list(self):
        response = self.client.get(self.location_list_url)
        self.assertEqual(response.status_code, 200)

    def test_post_location_list_geojson(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "geojson input test",
            "geometry": {
                "type": "GeometryCollection",
                "geometries": [
                    {"type": "Point", "coordinates": [12.492324113849, 41.890307434153]}
                ],
            },
        }
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        data = {
            "name": "geojson input test2",
            "geometry": {
                "type": "Point",
                "coordinates": [12.492324113849, 41.890307434153],
            },
        }
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 2)

    def test_post_location_list_geojson_as_multipartformdata(self):
        """emulate sending geojson string in webform"""
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "geojson input test",
            "geometry": json.dumps(
                {
                    "type": "GeometryCollection",
                    "geometries": [
                        {
                            "type": "Point",
                            "coordinates": [12.492324113849, 41.890307434153],
                        }
                    ],
                }
            ),
        }
        response = self.client.post(self.location_list_url, data)
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)

    def test_post_HTML_browsable_api(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "geojson input test2",
            "slug": "prova",
            "geometry": json.dumps(
                {
                    "type": "GeometryCollection",
                    "geometries": [
                        {
                            "type": "Point",
                            "coordinates": [12.492324113849, 41.890307434153],
                        }
                    ],
                }
            ),
        }
        response = self.client.post(
            self.location_list_url, data, headers={"accept": "text/html"}
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        location = Location.objects.all()[0]
        self.assertEqual(location.name, "geojson input test2")
        self.assertEqual(location.slug, "prova")

    def test_post_location_list_WKT(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "WKT input test",
            "geometry": "POINT (12.492324113849 41.890307434153)",
        }
        response = self.client.post(self.location_list_url, data)
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)

    def test_post_location_list_EWKT(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "EWKT input test",
            "geometry": "SRID=28992;POINT(221160 600204)",
        }
        response = self.client.post(self.location_list_url, data)
        expected_coords = (6.381495826183805, 53.384066927384985)
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        for lat, lon in zip(
            Location.objects.get(name="EWKT input test").geometry.coords,
            expected_coords,
        ):
            self.assertAlmostEqual(lat, lon, places=5)

    def test_post_location_list_WKT_as_json(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "WKT input test",
            "geometry": "POINT (12.492324113849 41.890307434153)",
        }
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)

    def test_post_location_list_empty_geometry(self):
        data = {"name": "empty input test"}
        response = self.client.post(self.location_list_url, data)
        self.assertEqual(response.data["geometry"][0], "This field is required.")
        data = {"name": "empty input test", "geometry": ""}
        response = self.client.post(self.location_list_url, data)
        self.assertEqual(response.data["geometry"][0], "This field is required.")
        data = {"name": "empty input test"}
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.data["geometry"][0], "This field is required.")
        data = {"name": "empty input test", "geometry": ""}
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.data["geometry"][0], "This field is required.")

    def test_post_location_list_invalid_WKT(self):
        data = {"name": "WKT wrong input test", "geometry": "I AM OBVIOUSLY WRONG"}
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(Location.objects.count(), 0)
        self.assertEqual(response.data["geometry"][0], self.value_error_message)
        # repeat as multipart form data
        response = self.client.post(self.location_list_url, data)
        self.assertEqual(response.data["geometry"][0], self.value_error_message)
        data = {
            "name": "I AM MODERATELY WRONG",
            "geometry": "POINT (12.492324113849, 41.890307434153)",
        }
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.data["geometry"][0], self.geos_error_message)
        # repeat as multipart form data
        response = self.client.post(self.location_list_url, data)
        self.assertEqual(response.data["geometry"][0], self.geos_error_message)

    def test_post_location_list_invalid_geojson(self):
        data = {
            "name": "quite wrong",
            "geometry": {
                "type": "ARRRR",
                "dasdas": [{"STtype": "PTUAMAoint", "NNAare": "rgon"}],
            },
        }
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.data["geometry"][0], self.gdal_error_message)
        data = {"name": "very wrong", "geometry": ["a", "b", "c"]}
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.data["geometry"][0][0:65], self.type_error_message)
        data = {"name": "very wrong", "geometry": False}
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.data["geometry"][0][0:65], self.type_error_message)
        data = {"name": "very wrong", "geometry": {"value": {"nested": ["yo"]}}}
        response = self.client.post(
            self.location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.data["geometry"][0], self.gdal_error_message)

    def test_geojson_format(self):
        """test geojson format"""
        location = Location.objects.create(
            name="geojson test", geometry="POINT (135.0 45.0)"
        )
        url = reverse("api_geojson_location_details", args=[location.id])
        expected = {
            "id": location.id,
            "type": "Feature",
            "properties": {
                "details": "http://testserver/geojson/%s/" % location.id,
                "name": "geojson test",
                "fancy_name": "Kool geojson test",
                "timestamp": None,
                "slug": "geojson-test",
            },
            "geometry": {"type": "Point", "coordinates": [135.0, 45.0]},
        }
        response = self.client.get(url)
        self.assertCountEqual(json.dumps(response.data), json.dumps(expected))
        response = self.client.get(url, headers={"accept": "text/html"})
        self.assertContains(response, "Kool geojson test")

    def test_geojson_id_attribute(self):
        location = Location.objects.create(
            name="geojson test", geometry="POINT (10.1 10.1)"
        )
        url = reverse("api_geojson_location_details", args=[location.id])
        response = self.client.get(url)
        self.assertEqual(response.data["id"], location.id)

    def test_geojson_id_attribute_slug(self):
        location = Location.objects.create(
            name="geojson test", geometry="POINT (10.1 10.1)"
        )
        url = reverse("api_geojson_location_slug_details", args=[location.slug])
        response = self.client.get(url)
        self.assertEqual(response.data["id"], location.slug)

    def test_geojson_false_id_attribute_slug(self):
        location = Location.objects.create(
            name="falseid test", geometry="POINT (10.1 10.1)"
        )
        url = reverse("api_geojson_location_falseid_details", args=[location.id])
        response = self.client.get(url)
        self.assertEqual(response.data["properties"]["name"], "falseid test")
        with self.assertRaises(KeyError):
            response.data["id"]

    def test_geojson_srid_transforms_to_wgs84(self):
        location = OtherSridLocation.objects.create(
            name="other SRID location",
            geometry="POINT(625826.2376404074 483198.2074507246)",
        )
        url = reverse("api_other_srid_location_details", args=[location.id])
        response = self.client.get(url)
        expected_coords = (16.372500007573713, 48.20833306345481)
        expected_coords_bbox = (
            16.372500007573713,
            48.20833306345481,
            16.372500007573713,
            48.20833306345481,
        )
        self.assertEqual(response.data["properties"]["name"], "other SRID location")
        for received, expected in zip(
            response.data["geometry"]["coordinates"],
            expected_coords,
        ):
            self.assertAlmostEqual(received, expected, places=5)

        for received, expected in zip(
            response.data["geometry"]["bbox"],
            expected_coords_bbox,
        ):
            self.assertAlmostEqual(received, expected, places=5)

    def test_post_geojson_id_attribute(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "type": "Feature",
            "id": "42",
            "properties": {"name": "point"},
            "geometry": {"type": "Point", "coordinates": [10.1, 10.1]},
        }
        url = reverse("api_geojson_location_writable_id_list")
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        self.assertEqual(Location.objects.first().id, 42)

    def test_geojson_no_id_attribute_slug(self):
        location = Location.objects.create(
            name="noid test", geometry="POINT (10.1 10.1)"
        )
        url = reverse("api_geojson_location_noid_details", args=[location.id])
        response = self.client.get(url)
        self.assertEqual(response.data["properties"]["name"], "noid test")
        with self.assertRaises(KeyError):
            response.data["id"]

    def test_geojson_filefield_attribute(self):
        located_file = LocatedFile.objects.create(
            name="geojson filefield test", geometry="POINT (10.1 10.1)"
        )
        url = reverse("api_geojson_located_file_details", args=[located_file.id])
        response = self.client.get(url)
        self.assertEqual(response.data["properties"]["file"], None)

    def test_post_geojson_location_list(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "type": "Feature",
            "properties": {"name": "point?", "details": "ignore this"},
            "geometry": {"type": "Point", "coordinates": [10.1, 10.1]},
        }
        response = self.client.post(
            self.geojson_location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        url = reverse(
            "api_geojson_location_details",
            args=[Location.objects.order_by("-id")[0].id],
        )
        response = self.client.get(url)
        self.assertEqual(response.data["properties"]["name"], "point?")
        self.assertEqual(response.data["geometry"]["type"], "Point")
        self.assertEqual(
            json.dumps(response.data["geometry"]["coordinates"]), "[10.1, 10.1]"
        )
        self.assertNotEqual(response.data["properties"]["details"], "ignore this")

    def test_post_geojson_location_list_HTML(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "type": "Feature",
            "properties": {"name": "point?", "details": "ignore this"},
            "geometry": {"type": "Point", "coordinates": [10.1, 10.1]},
        }
        response = self.client.post(
            self.geojson_location_list_url,
            data=json.dumps(data),
            content_type="application/json",
            headers={"accept": "text/html"},
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        url = reverse(
            "api_geojson_location_details",
            args=[Location.objects.order_by("-id")[0].id],
        )
        response = self.client.get(url)
        self.assertEqual(response.data["properties"]["name"], "point?")
        self.assertEqual(response.data["geometry"]["type"], "Point")
        self.assertEqual(
            json.dumps(response.data["geometry"]["coordinates"]), "[10.1, 10.1]"
        )
        self.assertNotEqual(response.data["properties"]["details"], "ignore this")

    def test_post_invalid_geojson_location_list(self):
        data = {
            "type": "Feature",
            "properties": {"details": "ignore this"},
            "geometry": {"type": "Point", "coordinates": [10.1, 10.1]},
        }
        response = self.client.post(
            self.geojson_location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(Location.objects.count(), 0)
        self.assertEqual(response.data["name"][0], "This field is required.")
        data = {
            "type": "Feature",
            "properties": {"name": "point?"},
            "geometry": {"type": "Point", "WRONG": {}},
        }
        response = self.client.post(
            self.geojson_location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(Location.objects.count(), 0)
        self.assertEqual(response.data["geometry"][0], self.gdal_error_message)

    def test_post_geojson_location_list_WKT(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "type": "Feature",
            "properties": {"name": "point?"},
            "geometry": "POINT (10.1 10.1)",
        }
        response = self.client.post(
            self.geojson_location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        url = reverse(
            "api_geojson_location_details",
            args=[Location.objects.order_by("-id")[0].id],
        )
        response = self.client.get(url)
        self.assertEqual(response.data["properties"]["name"], "point?")
        self.assertEqual(response.data["geometry"]["type"], "Point")
        self.assertEqual(
            json.dumps(response.data["geometry"]["coordinates"]), "[10.1, 10.1]"
        )

    def test_geofeatured_model_serializer_compatible_with_geomodel_serializer(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "geojson input test",
            "geometry": {
                "type": "GeometryCollection",
                "geometries": [
                    {"type": "Point", "coordinates": [12.492324113849, 41.890307434153]}
                ],
            },
        }
        response = self.client.post(
            self.geojson_location_list_url,
            data=json.dumps(data),
            content_type="application/json",
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)

    def test_geofeatured_model_post_as_multipartformdata(self):
        """emulate sending geojson string in webform"""
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "geojson input test",
            "geometry": json.dumps(
                {"type": "Point", "coordinates": [12.492324113849, 41.890307434153]}
            ),
        }
        response = self.client.post(self.location_list_url, data)
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        self.assertEqual(response.data["geometry"]["type"], "Point")

    def test_HTML_browsable_geojson_location_list(self):
        response = self.client.get(
            self.geojson_location_list_url, headers={"accept": "text/html"}
        )
        self.assertEqual(response.status_code, 200)
        self._create_locations()
        response = self.client.get(
            self.geojson_location_list_url, headers={"accept": "text/html"}
        )
        self.assertContains(response, "l1")
        self.assertContains(response, "l2")

    def test_post_geojson_location_list_HTML_web_form(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {
            "name": "HTML test",
            "geometry": json.dumps({"type": "Point", "coordinates": [10.1, 10.1]}),
        }
        response = self.client.post(
            self.geojson_location_list_url, data, headers={"accept": "text/html"}
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        location = Location.objects.all()[0]
        self.assertEqual(location.name, "HTML test")
        self.assertEqual(location.geometry.geom_type, "Point")

    def test_post_geojson_location_list_HTML_web_form_WKT(self):
        self.assertEqual(Location.objects.count(), 0)
        data = {"name": "HTML test WKT", "geometry": "POINT (10.1 10.1)"}
        response = self.client.post(
            self.geojson_location_list_url, data, headers={"accept": "text/html"}
        )
        self.assertEqual(response.status_code, 201)
        self.assertEqual(Location.objects.count(), 1)
        location = Location.objects.all()[0]
        self.assertEqual(location.name, "HTML test WKT")

    def test_geojson_HTML_widget_value(self):
        self._create_locations()
        response = self.client.get(
            self.geojson_location_list_url, headers={"accept": "text/html"}
        )
        self.assertContains(response, '<textarea name="geometry"')
        self.assertContains(response, escape('"type": "Point"'))
        self.assertContains(response, escape('"coordinates": ['))

    def test_patch_geojson_location(self):
        location = Location.objects.create(
            name="geojson patch test", geometry="POINT (135.0 45.0)"
        )
        url = reverse("api_geojson_location_details", args=[location.id])
        data = {
            "properties": {"name": "geojson successful patch test"},
            "geometry": {"type": "Point", "coordinates": [10.1, 10.1]},
        }
        response = self.client.generic(
            "PATCH", url, json.dumps(data), content_type="application/json"
        )
        self.assertEqual(response.status_code, 200)
        location_reloaded = Location.objects.get(pk=location.id)
        self.assertEqual(location_reloaded.name, "geojson successful patch test")
        self.assertEqual(
            location_reloaded.geometry, Point(10.1, 10.1, srid=location.geometry.srid)
        )

    def test_patch_geojson_location_wo_changing_geometry(self):
        location = Location.objects.create(
            name="geojson patch test", geometry="POINT (135.0 45.0)"
        )
        url = reverse("api_geojson_location_details", args=[location.id])
        data = {"properties": {"name": "geojson successful patch test"}}
        response = self.client.generic(
            "PATCH", url, json.dumps(data), content_type="application/json"
        )
        self.assertEqual(response.status_code, 200)
        location_reloaded = Location.objects.get(pk=location.id)
        self.assertEqual(location_reloaded.name, "geojson successful patch test")
        self.assertEqual(
            location_reloaded.geometry, Point(135.0, 45.0, srid=location.geometry.srid)
        )

    def test_geometry_serializer_method_field(self):
        location = Location.objects.create(
            name="geometry serializer method test", geometry="POINT (135.0 45.0)"
        )
        location_loaded = Location.objects.get(pk=location.id)
        self.assertEqual(location_loaded.name, "geometry serializer method test")
        self.assertEqual(
            location_loaded.geometry, Point(135.0, 45.0, srid=location.geometry.srid)
        )
        url = reverse("api_geojson_location_details_hidden", args=[location.id])
        data = {"properties": {"name": "hidden geometry"}}
        response = self.client.generic(
            "PATCH", url, json.dumps(data), content_type="application/json"
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["properties"]["name"], "hidden geometry")
        self.assertEqual(response.data["geometry"]["type"], "Point")
        self.assertEqual(response.data["geometry"]["coordinates"], [0.0, 0.0])

    def test_geometry_serializer_method_field_none(self):
        location = Location.objects.create(
            name="None value", geometry="POINT (135.0 45.0)"
        )
        location_loaded = Location.objects.get(pk=location.id)
        self.assertEqual(
            location_loaded.geometry, Point(135.0, 45.0, srid=location.geometry.srid)
        )
        url = reverse("api_geojson_location_details_none", args=[location.id])
        response = self.client.generic("GET", url, content_type="application/json")
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["properties"]["name"], "None value")
        self.assertEqual(response.data["geometry"], None)

    def test_geometry_serializer_method_field_nogeo(self):
        nullable = Nullable.objects.create(name="No geometry value")
        nullable_loaded = Nullable.objects.get(pk=nullable.id)
        self.assertEqual(nullable_loaded.name, "No geometry value")
        url = reverse("api_geojson_nullable_details_nogeo", args=[nullable.id])
        response = self.client.generic("GET", url, content_type="application/json")
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data["properties"]["name"], "No geometry value")
        self.assertEqual(response.data["geometry"], None)

    def test_nullable_empty_geometry(self):
        empty = Nullable(name="empty", geometry="POINT EMPTY")
        empty.full_clean()
        empty.save()
        url = reverse("api_geojson_nullable_details", args=[empty.id])
        response = self.client.generic("GET", url, content_type="application/json")
        self.assertIsNotNone(response.data["geometry"])
        self.assertEqual(response.data["geometry"]["coordinates"], [])

    def test_nullable_null_geometry(self):
        empty = Nullable(name="empty", geometry=None)
        empty.full_clean()
        empty.save()
        url = reverse("api_geojson_nullable_details", args=[empty.id])
        response = self.client.generic("GET", url, content_type="application/json")
        self.assertIsNone(response.data["geometry"])

    def test_geometry_field_to_representation_none(self):
        self._create_locations()
        f = LocationGeoSerializer(instance=self.l1).fields["geometry"]
        self.assertIsNone(f.to_representation(None))

    def test_geometry_empty_representation(self):
        self._create_locations()
        f = LocationGeoSerializer(instance=self.l1).fields["geometry"]
        geom_types = (
            "POINT",
            "LINESTRING",
            "LINEARRING",
            "POLYGON",
            "MULTIPOINT",
            "MULTILINESTRING",
            "MULTIPOLYGON",
        )
        for geom_type in geom_types:
            with self.subTest(geom_type=geom_type):
                value = f.to_representation(GEOSGeometry(f"{geom_type} EMPTY"))
                self.assertIsNotNone(value)
                if geom_type == "LINEARRING":
                    geom_type = "LINESTRING"
                self.assertEqual(value["type"].upper(), geom_type)
                self.assertEqual(value["coordinates"], [])
        # GEOMETRYCOLLECTION needs different handling
        value = f.to_representation(GEOSGeometry("GEOMETRYCOLLECTION EMPTY"))
        self.assertIsNotNone(value)
        self.assertEqual(value["type"].upper(), "GEOMETRYCOLLECTION")
        self.assertEqual(value["geometries"], [])

    def test_no_geo_field_improperly_configured(self):
        class LocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer):
            class Meta:
                model = Location

        with self.assertRaises(ImproperlyConfigured):
            LocationGeoFeatureSerializer()

    def test_exclude_geo_field_improperly_configured(self):
        self._create_locations()

        class LocationGeoFeatureSerializer(gis_serializers.GeoFeatureModelSerializer):
            class Meta:
                model = Location
                geo_field = "geometry"
                exclude = ("geometry",)

        with self.assertRaises(ImproperlyConfigured):
            LocationGeoFeatureSerializer(instance=self.l1)

    def test_geojson_pagination(self):
        self._create_locations()
        response = self.client.get(self.geojson_location_list_url)
        self.assertEqual(response.data["type"], "FeatureCollection")
        self.assertEqual(len(response.data["features"]), 2)
        response = self.client.get(f"{self.geojson_location_list_url}?page_size=1")
        self.assertEqual(response.data["type"], "FeatureCollection")
        self.assertEqual(len(response.data["features"]), 1)
        self.assertIn("next", response.data)
        self.assertIn("previous", response.data)

    def test_pickle(self):
        geometry = GEOSGeometry("POINT (30 10)")
        geojsondict = GeoJsonDict(
            (
                ("type", geometry.geom_type),
                ("coordinates", geometry.coords),
            )
        )
        pickled = pickle.dumps(geojsondict)
        restored = pickle.loads(pickled)
        self.assertEqual(restored, geojsondict)

    def test_geometrycollection_geojson(self):
        """test geometry collection geojson behaviour"""
        location = Location.objects.create(
            name="geometry collection geojson test",
            geometry="GEOMETRYCOLLECTION ("
            "POINT (135.0 45.0),"
            "LINESTRING (135.0 45.0,140.0 50.0,145.0 55.0),"
            "POLYGON ((135.0 45.0,140.0 50.0,145.0 55.0,135.0 45.0)))",
        )
        url = reverse("api_geojson_location_details", args=[location.id])
        expected = {
            "id": location.id,
            "type": "Feature",
            "properties": {
                "details": "http://testserver/geojson/%s/" % location.id,
                "name": "geometry collection geojson test",
                "fancy_name": "Kool geometry collection geojson test",
                "timestamp": None,
                "slug": "geometry-collection-geojson-test",
            },
            "geometry": {
                "type": "GeometryCollection",
                "geometries": [
                    {"type": "Point", "coordinates": [135.0, 45.0]},
                    {
                        "type": "LineString",
                        "coordinates": [[135.0, 45.0], [140.0, 50.0], [145.0, 55.0]],
                    },
                    {
                        "type": "Polygon",
                        "coordinates": [
                            [
                                [135.0, 45.0],
                                [140.0, 50.0],
                                [145.0, 55.0],
                                [135.0, 45.0],
                            ]
                        ],
                    },
                ],
            },
        }
        response = self.client.get(url)
        self.assertCountEqual(json.dumps(response.data), json.dumps(expected))
        self.assertContains(response, "Kool geometry collection geojson test")
