Exemple #1
0
 def test_features_filter_by_properties_with_several_string_field(self):
     layer = LayerFactory()
     FeatureFactory(
         layer=layer,
         geom=self.fake_geometry,
         properties={
             'text': 'foobar',
             'sentence': 'foobar is here'
         },
     )
     FeatureFactory(
         layer=layer,
         geom=self.fake_geometry,
         properties={
             'text': 'foo',
             'sentence': 'foobar is missing'
         },
     )
     FeatureFactory(
         layer=layer,
         geom=self.fake_geometry,
         properties={
             'text': 'foobar',
             'sentence': 'foobar is here'
         },
     )
     response = self.client.get(
         reverse('feature-list', kwargs={'layer': layer.pk}), {
             'properties__text': 'foobar',
             'properties__sentence': 'foobar is here'
         })
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     json_response = response.json()
     self.assertEqual(len(json_response), 2)
Exemple #2
0
    def test_relation_geojson(self, mock_relation):
        mock_relation.return_value = True
        city_cover = FeatureFactory(layer=self.layer_city,
                                    geom='POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))')
        city_cover_2 = FeatureFactory(
            layer=self.layer_city, geom='POLYGON((0 0, 0 3, 4 4, 3 0, 0 0))')
        intersect_relation = LayerRelation.objects.create(
            relation_type='intersects',
            origin=self.layer_trek,
            destination=self.layer_city,
        )
        url = reverse('feature-relation',
                      args=(self.layer_trek.pk, self.trek.identifier,
                            intersect_relation.pk))
        # city cover should be present after sync
        self.trek.sync_relations(intersect_relation.pk)
        response = self.client.get(url, data={'format': 'geojson'})

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        data = response.json()
        self.assertEqual(len(data['features']), 2, data)

        # city cover should not be present after deletion
        city_cover.delete()
        city_cover_2.delete()
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        data = response.json()
        self.assertEqual(len(data), 0, data)
Exemple #3
0
    def setUp(self) -> None:
        self.property_key = 'logo'
        self.feature_without_file_name = FeatureFactory(
            properties={
                self.property_key: 'data:image/png;base64,xxxxxxxxxxxxxxxxxxxxxxxxxx=='
            }
        )
        self.feature_without_file_data = FeatureFactory(
            properties={
                self.property_key: None
            }
        )
        self.crud_view = factories.CrudViewFactory(
            layer__schema={
                'properties': {
                    self.property_key: {
                        "type": "string",
                        "format": 'data-url',
                    }
                }
            }
        )

        self.feature_with_file_name = FeatureFactory(
            layer=self.crud_view.layer,
            properties={
                self.property_key: 'data:image/png;name=toto.png;base64,xxxxxxxxxxxxxxxxxxxxxxxxxx=='
            }
        )
Exemple #4
0
    def setUp(self) -> None:
        self.property_key = 'logo'
        self.feature_without_file_name = FeatureFactory(
            properties={
                self.property_key: 'data:image/png;base64,xxxxxxxxxxxxxxxxxxxxxxxxxx=='
            }
        )
        self.feature_without_file_data = FeatureFactory(
            properties={
                self.property_key: None
            }
        )
        self.crud_view = factories.CrudViewFactory(
            layer__schema={
                'properties': {
                    self.property_key: {
                        "type": "string",
                        "format": 'data-url',
                    }
                }
            }
        )

        self.feature_with_file_name = FeatureFactory(
            layer=self.crud_view.layer,
            properties={
                self.property_key: 'data:image/png;name=toto.png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkqAcAAIUAgUW0RjgAAAAASUVORK5CYII='
            }
        )
Exemple #5
0
 def setUp(self) -> None:
     self.layer_trek = LayerSchemaFactory(
         geom_type=GeometryTypes.LineString)
     self.layer_city = LayerSchemaFactory(geom_type=GeometryTypes.Polygon)
     self.trek = FeatureFactory(layer=self.layer_trek,
                                geom='LINESTRING(0 0, 1 1, 2 2, 3 3)')
     self.city_cover = FeatureFactory(
         layer=self.layer_city, geom='POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))')
     self.city_uncover = FeatureFactory(
         layer=self.layer_city, geom='POLYGON((4 4, 4 7, 7 7, 7 4, 4 4))')
Exemple #6
0
    def test_shapefile_same_import_export(self):
        FeatureFactory(layer=self.layer,
                       properties={'key1': [{
                           'key3': 'hello world',
                       }]})

        shape_url = reverse('layer-shapefile', args=[
            self.layer.pk,
        ])
        response = self.client.get(shape_url)
        self.assertEqual(HTTP_200_OK, response.status_code)

        shapefile = SimpleUploadedFile('shapefile-WGS84.zip', response.content)
        new_layer = LayerFactory()
        response = self.client.post(reverse('layer-shapefile',
                                            args=[
                                                new_layer.pk,
                                            ]), {
                                                'shapefile': shapefile,
                                            },
                                    format="multipart")

        self.assertEqual(HTTP_200_OK, response.status_code)
        self.assertEqual(self.layer.features.first().properties,
                         new_layer.features.first().properties)
Exemple #7
0
    def test_features_intersections(self):
        layer = LayerFactory()
        FeatureFactory(layer=layer,
                       geom=GEOSGeometry(
                           json.dumps(self.intersect_ref_geometry)))
        """The layer below must intersect"""
        response = self.client.post(
            reverse('layer-intersects', args=[
                layer.pk,
            ]), {'geom': json.dumps(self.intersect_geometry)})

        self.assertEqual(HTTP_200_OK, response.status_code)
        response = response.json().get('results', {})
        self.assertEqual(1, len(response.get('features')))
        self.assertDictEqual(self.intersect_ref_geometry,
                             response.get('features')[0].get('geometry'))
        """The layer below must NOT intersect"""
        response = self.client.post(
            reverse('layer-intersects', args=[
                layer.name,
            ]), {'geom': json.dumps(self.fake_geometry)})

        self.assertEqual(HTTP_200_OK, response.status_code)

        response = response.json().get('results', {})
        self.assertEqual(0, len(response.get('features')))
        """Tests that the intersects view throw an error if geometry is
           invalid
        """
        response = self.client.post(
            reverse('layer-intersects', args=[
                layer.pk,
            ]), {'geom': '''Invalid geometry'''})
        self.assertEqual(HTTP_400_BAD_REQUEST, response.status_code)
Exemple #8
0
    def test_no_permission(self):
        FeatureFactory(layer=self.layer, properties={'a': 'b'})

        response = self.client.patch(
            reverse('layer-detail', args=[self.layer.name, ]), {})

        self.assertEqual(HTTP_403_FORBIDDEN, response.status_code)
Exemple #9
0
    def test_layer_processing_clear_output(self):
        geojson = get_files_tests('toulouse.geojson')

        call_command(
            'import_geojson',
            f'{geojson}',
            verbosity=0)

        # Retrieve the layer
        in_layer = Layer.objects.first()

        out_layer = LayerFactory(name='out')
        FeatureFactory(layer=out_layer, properties="Test")

        self.assertEqual(out_layer.features.count(), 1)
        call_command(
            'layer_processing',
            f'--layer-name-ins={in_layer.name}',
            f'--layer-pk-out={out_layer.pk}',
            f'--sql-centroid',
            f'-co',
            verbosity=0)

        out_layer = Layer.objects.get(name='out')
        self.assertTrue(out_layer.features.count() > 1)
        self.assertNotIn('Test', [feature.properties for feature in out_layer.features.all()])
Exemple #10
0
 def test_edit_extra_features_bad_geom(self):
     feature = FeatureFactory(
         layer=self.layer,
         geom=GEOSGeometry(json.dumps(self.linestring)),
         properties={
             'number': 1,
             'text': 'bar'
         },
     )
     layer_extra_geom = LayerExtraGeom.objects.create(
         layer=self.layer, geom_type=GeometryTypes.Point, title='Test')
     extra_feature = FeatureExtraGeom.objects.create(
         layer_extra_geom=layer_extra_geom,
         feature=feature,
         geom=GEOSGeometry(json.dumps(self.point)))
     feature.extra_geometries.add(extra_feature)
     response = self.client.put(reverse('feature-detail-extra-geometry',
                                        kwargs={
                                            'layer':
                                            str(self.layer.name),
                                            'identifier':
                                            str(feature.identifier),
                                            'id_extra_feature':
                                            extra_feature.pk
                                        }),
                                data={'geom': "WRONG_GEOM"})
     self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
     json_response = response.json()
     self.assertEqual(
         json_response, {
             'geom': [
                 'Unable to convert to python object: '
                 'String input unrecognized as WKT EWKT, and HEXEWKB.'
             ]
         })
Exemple #11
0
 def test_get_extra_features(self):
     feature = FeatureFactory(
         layer=self.layer,
         geom=GEOSGeometry(json.dumps(self.linestring)),
         properties={
             'number': 1,
             'text': 'bar'
         },
     )
     layer_extra_geom = LayerExtraGeom.objects.create(
         layer=self.layer, geom_type=GeometryTypes.Point, title='Test')
     extra_feature = FeatureExtraGeom.objects.create(
         layer_extra_geom=layer_extra_geom,
         feature=feature,
         geom=GEOSGeometry(json.dumps(self.point)))
     feature.extra_geometries.add(extra_feature)
     response = self.client.get(
         reverse('feature-detail-extra-geometry',
                 kwargs={
                     'layer': str(self.layer.name),
                     'identifier': str(feature.identifier),
                     'id_extra_feature': extra_feature.pk
                 }))
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     json_response = response.json()
     self.assertEqual(
         json_response, {
             'id': extra_feature.pk,
             'geom': {
                 'type': 'Point',
                 'coordinates': [1.44, 43.6]
             }
         })
Exemple #12
0
 def test_delete_extra_feature_not_editable(self):
     feature = FeatureFactory(
         layer=self.layer,
         geom=GEOSGeometry(json.dumps(self.linestring)),
         properties={
             'number': 1,
             'text': 'bar'
         },
     )
     layer_extra_geom = LayerExtraGeom.objects.create(
         layer=self.layer,
         geom_type=GeometryTypes.Point,
         title='Test',
         editable=False)
     extra_feature = FeatureExtraGeom.objects.create(
         layer_extra_geom=layer_extra_geom,
         feature=feature,
         geom=GEOSGeometry(json.dumps(self.point)))
     self.assertEqual(feature.extra_geometries.count(), 1)
     response = self.client.delete(
         reverse('feature-detail-extra-geometry',
                 kwargs={
                     'layer': str(self.layer.name),
                     'identifier': str(feature.identifier),
                     'id_extra_feature': extra_feature.pk
                 }))
     self.assertEqual(response.status_code,
                      status.HTTP_405_METHOD_NOT_ALLOWED)
     self.assertEqual(feature.extra_geometries.count(), 1)
Exemple #13
0
    def test_to_geojson_no_permission(self):
        # Create at least one feature in the layer, so it's not empty
        FeatureFactory(layer=self.layer)

        geojson_url = reverse('layer-geojson', args=[self.layer.pk, ])
        response = self.client.get(geojson_url)

        self.assertEqual(HTTP_403_FORBIDDEN, response.status_code)
Exemple #14
0
 def setUpTestData(cls):
     # create a geometry undefined layer with all kind of geometry as features
     props = {"name": "test", "label": "Test"}
     cls.layer = LayerFactory()
     cls.point = FeatureFactory(layer=cls.layer,
                                geom="POINT(0 0)",
                                properties=props)
     cls.line = FeatureFactory(layer=cls.layer,
                               geom="LINESTRING(0 0, 1 1)",
                               properties=props)
     cls.polygon = FeatureFactory(
         layer=cls.layer,
         geom=
         "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0), (0.4 0.4, 0.5 0.4, 0.5 0.5, 0.4 0.5, 0.4 0.4 ))",
         properties=props)
     cls.multipoint = FeatureFactory(layer=cls.layer,
                                     geom="MULTIPOINT((0 0), (1 0))",
                                     properties=props)
     cls.multilinestring = FeatureFactory(
         layer=cls.layer,
         geom="MULTILINESTRING((3 4,10 50,20 25),(-5 -8,-10 -8,-15 -4))",
         properties=props)
     cls.multipolygon = FeatureFactory(
         layer=cls.layer,
         geom=
         "MULTIPOLYGON(((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2)),((6 3,9 2,9 4,6 3)))",
         properties=props)
     cls.geometrycollection = FeatureFactory(
         layer=cls.layer,
         geom="GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))",
         properties=props)
Exemple #15
0
    def test_features_filter_by_properties_with_wrong_field(self):
        layer = LayerFactory()
        FeatureFactory(
            layer=layer,
            geom=self.fake_geometry,
            properties={'number': 1},
        )
        FeatureFactory(
            layer=layer,
            geom=self.fake_geometry,
            properties={'number': 2},
        )
        response = self.client.get(
            reverse('feature-list', kwargs={'layer': layer.pk}),
            {'properties__wrongfield': 'wrong value'},
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        json_response = response.json()
        self.assertEqual(len(json_response), 0)
Exemple #16
0
 def setUp(self) -> None:
     self.layer_trek = LayerSchemaFactory(
         geom_type=GeometryTypes.LineString)
     self.layer_city = LayerSchemaFactory(geom_type=GeometryTypes.Polygon)
     self.trek = FeatureFactory(layer=self.layer_trek,
                                geom='LINESTRING(0 0, 1 1, 2 2, 3 3)')
     self.city_uncover = FeatureFactory(
         layer=self.layer_city,
         geom='POLYGON((4 4, 4 7, 7 7, 7 4, 4 4))',
         properties={
             "name": "Cahors",
             "age": 50000
         })
     self.detail_url = reverse('feature-detail',
                               args=(
                                   self.layer_city.pk,
                                   self.city_uncover.identifier,
                               ))
     self.super_user = UserFactory(is_superuser=True)
     self.client.force_authenticate(self.super_user)
Exemple #17
0
 def test_to_geojson(self):
     # Create at least one feature in the layer, so it's not empty
     FeatureFactory(layer=self.layer)
     FeatureFactory(
         layer=self.layer,
         geom=GEOSGeometry(json.dumps(self.fake_geometry)),
         properties={
             'number': 1,
             'digit': 34
         },
     )
     geojson = json.loads(self.layer.to_geojson())
     self.assertEqual(
         str(geojson['features'][0]['geometry']),
         "{'type': 'Point', 'coordinates': [2.4609375, 45.583289756006316]}"
     )
     self.assertEqual(str(geojson['features'][1]['geometry']),
                      "{'type': 'Point', 'coordinates': [2.0, 45.0]}")
     self.assertEqual(str(geojson['features'][1]['properties']),
                      "{'digit': 34, 'number': 1}")
Exemple #18
0
    def test_shapefile_export(self):
        # Create at least one feature in the layer, so it's not empty
        FeatureFactory(layer=self.layer)

        shape_url = reverse('layer-shapefile', args=[
            self.layer.pk,
        ])
        response = self.client.get(shape_url)
        self.assertEqual(HTTP_200_OK, response.status_code)

        zip_file = ZipFile(BytesIO(response.content), 'r')
        self.assertListEqual(
            sorted(['prj', 'cpg', 'shx', 'shp', 'dbf']),
            sorted(set([f.split('.')[1] for f in zip_file.namelist()])))
Exemple #19
0
    def test_to_geojson(self):
        # Create at least one feature in the layer, so it's not empty
        self.user.user_permissions.add(Permission.objects.get(codename='can_export_layers'))
        FeatureFactory(layer=self.layer)

        geojson_url = reverse('layer-geojson', args=[self.layer.pk, ])
        response = self.client.get(geojson_url)

        self.assertEqual(HTTP_200_OK, response.status_code)

        response = response.json()
        self.assertEqual('FeatureCollection', response.get('type'))
        self.assertEqual(self.layer.features.all().count(),
                         len(response.get('features')))
Exemple #20
0
 def test_features_filter_by_properties(self):
     layer = LayerFactory()
     FeatureFactory(
         layer=layer,
         geom=self.fake_geometry,
         properties={
             'number': 1,
             'text': 'bar'
         },
     )
     FeatureFactory(
         layer=layer,
         geom=self.fake_geometry,
         properties={
             'number': 1,
             'text': 'foo'
         },
     )
     FeatureFactory(
         layer=layer,
         geom=self.fake_geometry,
         properties={
             'number': 2,
             'text': 'foo'
         },
     )
     response = self.client.get(
         reverse('feature-list', kwargs={'layer': layer.pk}),
         {
             'properties__number': 1,
             'properties__text': 'foo'
         },
     )
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     json_response = response.json()
     self.assertEqual(len(json_response), 1)
Exemple #21
0
 def test_features_filter_by_properties_with_several_int_field(self):
     layer = LayerFactory()
     FeatureFactory(
         layer=layer,
         geom=GEOSGeometry(json.dumps(self.fake_geometry)),
         properties={'number': 2, 'digit': 42},
     )
     FeatureFactory(
         layer=layer,
         geom=GEOSGeometry(json.dumps(self.fake_geometry)),
         properties={'number': 1, 'digit': 42},
     )
     FeatureFactory(
         layer=layer,
         geom=GEOSGeometry(json.dumps(self.fake_geometry)),
         properties={'number': 1, 'digit': 34},
     )
     response = self.client.get(
         reverse('feature-list', kwargs={'layer': layer.pk}),
         {'properties__number': 1, 'properties__digit': 42},
     )
     self.assertEqual(response.status_code, HTTP_200_OK)
     json_response = response.json()
     self.assertEqual(len(json_response), 1)
Exemple #22
0
    def test_feature_from_layer_name(self):
        layer = LayerFactory()
        feature = FeatureFactory(
            layer=layer,
            geom=GEOSGeometry(json.dumps(self.fake_geometry)),
            properties={'text': 'foobar', 'sentence': 'foobar is here'},
        )

        response = self.client.get(
            reverse(
                'feature-detail',
                kwargs={'layer': str(layer.name),
                        'identifier': str(feature.identifier)}
            ),
        )
        self.assertEqual(response.status_code, HTTP_200_OK)
Exemple #23
0
 def test_post_extra_layer_do_not_exists(self):
     feature = FeatureFactory(
         layer=self.layer,
         geom=GEOSGeometry(json.dumps(self.linestring)),
         properties={
             'number': 1,
             'text': 'bar'
         },
     )
     response = self.client.post(
         reverse('feature-create-extra-geometry',
                 kwargs={
                     'layer': str(self.layer.name),
                     'identifier': str(feature.identifier),
                     'id_extra_layer': 999
                 }), {'geom': json.dumps(self.point)})
     self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
Exemple #24
0
    def test_routing_view_with_polygon(self):
        """test that a layer with another kind of geometry raise the right exception"""
        feature = FeatureFactory()

        points = [Point(
            *p['coordinates'],
            srid=app_settings.INTERNAL_GEOMETRY_SRID) for p in
            [self.points[0], self.points[0]]]

        geometry = LineString(*points)

        response = self.client.post(reverse('layer-route',
                                            args=[feature.layer.pk]),
                                    {'geom': geometry.geojson, })
        self.assertEqual(HTTP_400_BAD_REQUEST, response.status_code)
        error = response.json()['errors'][0]
        self.assertEqual("Layer is not routable", error)
Exemple #25
0
 def setUpTestData(cls):
     cls.feature = FeatureFactory()
     cls.user = TerraUserFactory()
     # Create viewpoint with draft picture attached to it
     cls.viewpoint = ViewpointFactory(label="Basic viewpoint")
     # Create viewpoint with accepted picture attached to it
     cls.viewpoint_with_accepted_picture = ViewpointFactory(
         label="Viewpoint with accepted picture",
         pictures__state="accepted",
         properties={"test_update": "ko"},
         active=True,
     )
     # Create viewpoints with no picture attached to it
     cls.viewpoint_without_picture = ViewpointFactory(
         label="Viewpoint without picture",
         pictures=None,
         properties={"test_update": "ko"},
     )
Exemple #26
0
 def test_get_extra_layer(self):
     feature = FeatureFactory(
         layer=self.layer,
         geom=GEOSGeometry(json.dumps(self.linestring)),
         properties={
             'number': 1,
             'text': 'bar'
         },
     )
     layer_extra_geom = LayerExtraGeom.objects.create(
         layer=self.layer, geom_type=GeometryTypes.Point, title='Test')
     response = self.client.get(
         reverse('feature-create-extra-geometry',
                 kwargs={
                     'layer': str(self.layer.name),
                     'identifier': str(feature.identifier),
                     'id_extra_layer': layer_extra_geom.pk
                 }), {'geom': json.dumps(self.point)})
     self.assertEqual(response.status_code,
                      status.HTTP_405_METHOD_NOT_ALLOWED)
Exemple #27
0
    def test_layer_processing_by_name(self):
        geojson = get_files_tests('toulouse.geojson')

        call_command('import_geojson', geojson, verbosity=0)

        # Retrieve the layer
        in_layer = Layer.objects.first()

        out_layer = Layer.objects.create(name='out')
        FeatureFactory(layer=out_layer, properties="Test")

        call_command(
            'layer_processing',
            f'--layer-name-ins={in_layer.name}',
            f'--layer-name-out={out_layer.name}',
            f'--sql-centroid',
            verbosity=0)

        out_layer = Layer.objects.get(name='out')
        self.assertIn('Test', [feature.properties for feature in out_layer.features.all()])
        self.assertTrue(len(out_layer.features.all()) > 0)
Exemple #28
0
 def test_edit_extra_features(self):
     feature = FeatureFactory(
         layer=self.layer,
         geom=GEOSGeometry(json.dumps(self.linestring)),
         properties={
             'number': 1,
             'text': 'bar'
         },
     )
     layer_extra_geom = LayerExtraGeom.objects.create(
         layer=self.layer, geom_type=GeometryTypes.Point, title='Test')
     extra_feature = FeatureExtraGeom.objects.create(
         layer_extra_geom=layer_extra_geom,
         feature=feature,
         geom=GEOSGeometry(json.dumps(self.point)))
     feature.extra_geometries.add(extra_feature)
     response = self.client.put(reverse('feature-detail-extra-geometry',
                                        kwargs={
                                            'layer':
                                            str(self.layer.name),
                                            'identifier':
                                            str(feature.identifier),
                                            'id_extra_feature':
                                            extra_feature.pk
                                        }),
                                data={'geom': json.dumps(self.linestring)})
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     json_response = response.json()
     self.assertEqual(
         json_response, {
             'id': extra_feature.pk,
             'geom': {
                 'type':
                 'LineString',
                 'coordinates': [[1.440925598144531, 43.64750394449096],
                                 [1.440582275390625, 43.574421623084234]]
             }
         })
Exemple #29
0
    def test_relation_with_pagination(self, mock_view):
        class MyPagination(PageNumberPagination):
            page_size = 1
            page_size_query_param = 'page_size'

        mock_view.return_value = MyPagination
        FeatureFactory(layer=self.layer_city,
                       geom='POLYGON((0 0, 0 3, 3 3, 3 0, 0 0))')
        intersect_relation = LayerRelation.objects.create(
            relation_type='intersects',
            origin=self.layer_trek,
            destination=self.layer_city,
        )
        url = reverse('feature-relation',
                      args=(self.layer_trek.pk, self.trek.identifier,
                            intersect_relation.pk))
        # city cover should be present after sync
        self.trek.sync_relations(intersect_relation.pk)
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        data = response.json()
        self.assertEqual(len(data['results']), 1, data)
        self.assertTrue(mock_view.called)
Exemple #30
0
    def test_update(self):
        self.user.user_permissions.add(
            Permission.objects.get(codename='can_manage_layers'))
        geom = GEOSGeometry(json.dumps(self.geometry))
        feature = FeatureFactory(layer=self.layer,
                                 geom=geom,
                                 properties={'a': 'b'})

        updated_properties = {
            'c': 'd',
            'a': 'd',
        }

        response = self.client.patch(reverse(
            'layer-detail', args=[
                self.layer.name,
            ]), {
                "type":
                "FeatureCollection",
                "features": [
                    {
                        "type": "Feature",
                        'geometry': self.geometry,
                        'properties': updated_properties,
                    },
                ]
            },
                                     format='json')

        self.assertEqual(HTTP_200_OK, response.status_code)
        response = response.json()
        self.assertEqual(response['features'][0]['properties'],
                         updated_properties)

        feature.refresh_from_db()
        self.assertDictEqual(feature.properties, updated_properties)