diff --git a/.gitignore b/.gitignore
index 60d85dc4..a9a3b7a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,6 +53,7 @@ nosetests.xml
coverage.xml
*,cover
.hypothesis/
+.pytest_cache/
# Translations
*.mo
diff --git a/Makefile b/Makefile
index 6d0d58cb..c02b8c8e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
test:
- py.test
+ py.test -xv
develop:
python setup.py develop
compilemessages:
diff --git a/umap/tests/__init__.py b/umap/tests/__init__.py
index 92018579..e69de29b 100644
--- a/umap/tests/__init__.py
+++ b/umap/tests/__init__.py
@@ -1 +0,0 @@
-from .test_views import *
diff --git a/umap/tests/base.py b/umap/tests/base.py
new file mode 100644
index 00000000..820425e6
--- /dev/null
+++ b/umap/tests/base.py
@@ -0,0 +1,96 @@
+import json
+
+import factory
+from django.contrib.auth import get_user_model
+from django.urls import reverse
+
+from umap.forms import DEFAULT_CENTER
+from umap.models import DataLayer, Licence, Map, TileLayer
+
+User = get_user_model()
+
+
+class LicenceFactory(factory.DjangoModelFactory):
+ name = "WTFPL"
+
+ class Meta:
+ model = Licence
+
+
+class TileLayerFactory(factory.DjangoModelFactory):
+ name = "Test zoom layer"
+ url_template = "http://{s}.test.org/{z}/{x}/{y}.png"
+ attribution = "Test layer attribution"
+
+ class Meta:
+ model = TileLayer
+
+
+class UserFactory(factory.DjangoModelFactory):
+ username = 'Joe'
+ email = factory.LazyAttribute(
+ lambda a: '{0}@example.com'.format(a.username).lower())
+ password = factory.PostGenerationMethodCall('set_password', '123123')
+
+ class Meta:
+ model = User
+
+
+class MapFactory(factory.DjangoModelFactory):
+ name = "test map"
+ slug = "test-map"
+ center = DEFAULT_CENTER
+ settings = {
+ 'geometry': {
+ 'coordinates': [13.447265624999998, 48.94415123418794],
+ 'type': 'Point'
+ },
+ 'properties': {
+ 'datalayersControl': True,
+ 'description': 'Which is just the Danube, at the end',
+ 'displayCaptionOnLoad': False,
+ 'displayDataBrowserOnLoad': False,
+ 'displayPopupFooter': False,
+ 'licence': '',
+ 'miniMap': False,
+ 'moreControl': True,
+ 'name': 'Cruising on the Donau',
+ 'scaleControl': True,
+ 'tilelayer': {
+ 'attribution': u'\xa9 OSM Contributors',
+ 'maxZoom': 18,
+ 'minZoom': 0,
+ 'url_template': 'http://{s}.osm.fr/{z}/{x}/{y}.png'
+ },
+ 'tilelayersControl': True,
+ 'zoom': 7,
+ 'zoomControl': True
+ },
+ 'type': 'Feature'
+ }
+
+ licence = factory.SubFactory(LicenceFactory)
+ owner = factory.SubFactory(UserFactory)
+
+ class Meta:
+ model = Map
+
+
+class DataLayerFactory(factory.DjangoModelFactory):
+ map = factory.SubFactory(MapFactory)
+ name = "test datalayer"
+ description = "test description"
+ display_on_load = True
+ geojson = factory.django.FileField(data="""{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[13.68896484375,48.55297816440071]},"properties":{"_storage_options":{"color":"DarkCyan","iconClass":"Ball"},"name":"Here","description":"Da place anonymous again 755"}}],"_storage":{"displayOnLoad":true,"name":"Donau","id":926}}""") # noqa
+
+ class Meta:
+ model = DataLayer
+
+
+def login_required(response):
+ assert response.status_code == 200
+ j = json.loads(response.content.decode())
+ assert 'login_required' in j
+ redirect_url = reverse('login')
+ assert j['login_required'] == redirect_url
+ return True
diff --git a/umap/tests/conftest.py b/umap/tests/conftest.py
new file mode 100644
index 00000000..441513f7
--- /dev/null
+++ b/umap/tests/conftest.py
@@ -0,0 +1,64 @@
+import shutil
+import tempfile
+
+import pytest
+from django.core.signing import get_cookie_signer
+
+from .base import (DataLayerFactory, LicenceFactory, MapFactory,
+ TileLayerFactory, UserFactory)
+
+TMP_ROOT = tempfile.mkdtemp()
+
+
+def pytest_configure(config):
+ from django.conf import settings
+ settings.MEDIA_ROOT = TMP_ROOT
+
+
+def pytest_unconfigure(config):
+ shutil.rmtree(TMP_ROOT, ignore_errors=True)
+
+
+@pytest.fixture
+def user():
+ return UserFactory(password="123123")
+
+
+@pytest.fixture
+def licence():
+ return LicenceFactory()
+
+
+@pytest.fixture
+def map(licence, tilelayer):
+ user = UserFactory(username="Gabriel", password="123123")
+ return MapFactory(owner=user, licence=licence)
+
+
+@pytest.fixture
+def anonymap(map):
+ map.owner = None
+ map.save()
+ return map
+
+
+@pytest.fixture
+def cookieclient(client, anonymap):
+ key, value = anonymap.signed_cookie_elements
+ client.cookies[key] = get_cookie_signer(salt=key).sign(value)
+ return client
+
+
+@pytest.fixture
+def allow_anonymous(settings):
+ settings.LEAFLET_STORAGE_ALLOW_ANONYMOUS = True
+
+
+@pytest.fixture
+def datalayer(map):
+ return DataLayerFactory(map=map, name="Default Datalayer")
+
+
+@pytest.fixture
+def tilelayer():
+ return TileLayerFactory()
diff --git a/umap/tests/fixtures/test_upload_data.csv b/umap/tests/fixtures/test_upload_data.csv
new file mode 100644
index 00000000..107f2510
--- /dev/null
+++ b/umap/tests/fixtures/test_upload_data.csv
@@ -0,0 +1,2 @@
+Foo,Latitude,geo_Longitude,title,description
+bar,41.34,122.86,a point somewhere,the description of this point
\ No newline at end of file
diff --git a/umap/tests/fixtures/test_upload_data.gpx b/umap/tests/fixtures/test_upload_data.gpx
new file mode 100644
index 00000000..ea7e1080
--- /dev/null
+++ b/umap/tests/fixtures/test_upload_data.gpx
@@ -0,0 +1,17 @@
+
+ 1374Simple PointSimple description
+
+ Simple path
+ Simple description
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/umap/tests/fixtures/test_upload_data.json b/umap/tests/fixtures/test_upload_data.json
new file mode 100644
index 00000000..8d7fc002
--- /dev/null
+++ b/umap/tests/fixtures/test_upload_data.json
@@ -0,0 +1,188 @@
+{
+ "crs": null,
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -0.1318359375,
+ 51.474540439419755
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "London",
+ "description": "London description",
+ "color": "Pink"
+ }
+ },
+ {
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 4.350585937499999,
+ 51.26878915771344
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "Antwerpen",
+ "description": ""
+ }
+ },
+ {
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ 2.4005126953125,
+ 48.81228985866255
+ ],
+ [
+ 2.78228759765625,
+ 48.89903236496008
+ ],
+ [
+ 2.845458984375,
+ 48.89903236496008
+ ],
+ [
+ 2.86468505859375,
+ 48.96218736991556
+ ],
+ [
+ 2.9278564453125,
+ 48.93693495409401
+ ],
+ [
+ 2.93060302734375,
+ 48.99283383694349
+ ],
+ [
+ 3.04046630859375,
+ 49.01085236926211
+ ],
+ [
+ 3.0157470703125,
+ 48.96038404976431
+ ],
+ [
+ 3.12286376953125,
+ 48.94415123418794
+ ],
+ [
+ 3.1805419921874996,
+ 48.99824008113872
+ ],
+ [
+ 3.2684326171875,
+ 48.95497369808868
+ ],
+ [
+ 3.53759765625,
+ 49.0900564769189
+ ],
+ [
+ 3.57330322265625,
+ 49.057670047140604
+ ],
+ [
+ 3.72161865234375,
+ 49.095452162534826
+ ],
+ [
+ 3.9578247070312496,
+ 49.06486885623368
+ ]
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "2011"
+ }
+ },
+ {
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 10.04150390625,
+ 52.70967533219883
+ ],
+ [
+ 8.800048828125,
+ 51.80182150078305
+ ],
+ [
+ 11.271972656249998,
+ 51.12421275782688
+ ],
+ [
+ 12.689208984375,
+ 52.214338608258196
+ ],
+ [
+ 10.04150390625,
+ 52.70967533219883
+ ]
+ ]
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "test"
+ }
+ },
+ {
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 7.327880859374999,
+ 50.52041218671901
+ ],
+ [
+ 6.064453125,
+ 49.6462914122132
+ ],
+ [
+ 7.503662109375,
+ 49.54659778073743
+ ],
+ [
+ 6.8115234375,
+ 49.167338606291075
+ ],
+ [
+ 9.635009765625,
+ 48.99463598353408
+ ],
+ [
+ 10.557861328125,
+ 49.937079756975294
+ ],
+ [
+ 8.4814453125,
+ 49.688954878870305
+ ],
+ [
+ 9.173583984375,
+ 51.04830113331224
+ ],
+ [
+ 7.327880859374999,
+ 50.52041218671901
+ ]
+ ]
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "test polygon 2"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/umap/tests/fixtures/test_upload_data.kml b/umap/tests/fixtures/test_upload_data.kml
new file mode 100644
index 00000000..af857078
--- /dev/null
+++ b/umap/tests/fixtures/test_upload_data.kml
@@ -0,0 +1,33 @@
+
+
+
+ Simple point
+ Here is a simple description.
+
+ -122.0822035425683,37.42228990140251,0
+
+
+
+ Simple path
+ Simple description
+
+ -112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357
+
+
+
+ Simple polygon
+ A description.
+
+
+
+
+ -77.05788457660967,38.87253259892824,100
+ -77.05465973756702,38.87291016281703,100
+ -77.05315536854791,38.87053267794386,100
+ -77.05788457660967,38.87253259892824,100
+
+
+
+
+
+
\ No newline at end of file
diff --git a/umap/tests/fixtures/test_upload_empty_coordinates.json b/umap/tests/fixtures/test_upload_empty_coordinates.json
new file mode 100644
index 00000000..65f8dd16
--- /dev/null
+++ b/umap/tests/fixtures/test_upload_empty_coordinates.json
@@ -0,0 +1,36 @@
+{
+ "crs": null,
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "geometry": {
+ "type": "Point",
+ "coordinates": []
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "London"
+ }
+ },
+ {
+ "geometry": {
+ "type": "LineString",
+ "coordinates": []
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "2011"
+ }
+ },
+ {
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [[]]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "test"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/umap/tests/fixtures/test_upload_missing_name.json b/umap/tests/fixtures/test_upload_missing_name.json
new file mode 100644
index 00000000..e4e4acbf
--- /dev/null
+++ b/umap/tests/fixtures/test_upload_missing_name.json
@@ -0,0 +1,153 @@
+{
+ "crs": null,
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -0.1318359375,
+ 51.474540439419755
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "London"
+ }
+ },
+ {
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 4.350585937499999,
+ 51.26878915771344
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "noname": "this feature is missing a name",
+ "name": null
+ }
+ },
+ {
+ "geometry": {
+ "type": "LineString",
+ "coordinates": [
+ [
+ 2.4005126953125,
+ 48.81228985866255
+ ],
+ [
+ 2.78228759765625,
+ 48.89903236496008
+ ],
+ [
+ 2.845458984375,
+ 48.89903236496008
+ ],
+ [
+ 2.86468505859375,
+ 48.96218736991556
+ ],
+ [
+ 2.9278564453125,
+ 48.93693495409401
+ ],
+ [
+ 2.93060302734375,
+ 48.99283383694349
+ ],
+ [
+ 3.04046630859375,
+ 49.01085236926211
+ ],
+ [
+ 3.0157470703125,
+ 48.96038404976431
+ ],
+ [
+ 3.12286376953125,
+ 48.94415123418794
+ ],
+ [
+ 3.1805419921874996,
+ 48.99824008113872
+ ],
+ [
+ 3.2684326171875,
+ 48.95497369808868
+ ],
+ [
+ 3.53759765625,
+ 49.0900564769189
+ ],
+ [
+ 3.57330322265625,
+ 49.057670047140604
+ ],
+ [
+ 3.72161865234375,
+ 49.095452162534826
+ ],
+ [
+ 3.9578247070312496,
+ 49.06486885623368
+ ]
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "2011"
+ }
+ },
+ {
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 7.327880859374999,
+ 50.52041218671901
+ ],
+ [
+ 6.064453125,
+ 49.6462914122132
+ ],
+ [
+ 7.503662109375,
+ 49.54659778073743
+ ],
+ [
+ 6.8115234375,
+ 49.167338606291075
+ ],
+ [
+ 9.635009765625,
+ 48.99463598353408
+ ],
+ [
+ 10.557861328125,
+ 49.937079756975294
+ ],
+ [
+ 8.4814453125,
+ 49.688954878870305
+ ],
+ [
+ 9.173583984375,
+ 51.04830113331224
+ ],
+ [
+ 7.327880859374999,
+ 50.52041218671901
+ ]
+ ]
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "test polygon 2"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/umap/tests/fixtures/test_upload_non_linear_ring.json b/umap/tests/fixtures/test_upload_non_linear_ring.json
new file mode 100644
index 00000000..db22bb0b
--- /dev/null
+++ b/umap/tests/fixtures/test_upload_non_linear_ring.json
@@ -0,0 +1,51 @@
+{
+ "crs": null,
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 7.327880859374999,
+ 50.52041218671901
+ ],
+ [
+ 6.064453125,
+ 49.6462914122132
+ ],
+ [
+ 7.503662109375,
+ 49.54659778073743
+ ],
+ [
+ 6.8115234375,
+ 49.167338606291075
+ ],
+ [
+ 9.635009765625,
+ 48.99463598353408
+ ],
+ [
+ 10.557861328125,
+ 49.937079756975294
+ ],
+ [
+ 8.4814453125,
+ 49.688954878870305
+ ],
+ [
+ 9.173583984375,
+ 51.04830113331224
+ ]
+ ]
+ ]
+ },
+ "type": "Feature",
+ "properties": {
+ "name": "non linear ring"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/umap/tests/test_datalayer.py b/umap/tests/test_datalayer.py
new file mode 100644
index 00000000..65f8e2f7
--- /dev/null
+++ b/umap/tests/test_datalayer.py
@@ -0,0 +1,81 @@
+import os
+
+import pytest
+from django.core.files.base import ContentFile
+
+from .base import DataLayerFactory, MapFactory
+
+pytestmark = pytest.mark.django_db
+
+
+def test_datalayers_should_be_ordered_by_rank(map, datalayer):
+ datalayer.rank = 5
+ datalayer.save()
+ c4 = DataLayerFactory(map=map, rank=4)
+ c1 = DataLayerFactory(map=map, rank=1)
+ c3 = DataLayerFactory(map=map, rank=3)
+ c2 = DataLayerFactory(map=map, rank=2)
+ assert list(map.datalayer_set.all()) == [c1, c2, c3, c4, datalayer]
+
+
+def test_upload_to(map, datalayer):
+ map.pk = 302
+ datalayer.pk = 17
+ assert datalayer.upload_to().startswith('datalayer/2/0/302/17_')
+
+
+def test_save_should_use_pk_as_name(map, datalayer):
+ assert "/{}_".format(datalayer.pk) in datalayer.geojson.name
+
+
+def test_same_geojson_file_name_will_be_suffixed(map, datalayer):
+ before = datalayer.geojson.name
+ datalayer.geojson.save(before, ContentFile("{}"))
+ assert datalayer.geojson.name != before
+ assert "/{}_".format(datalayer.pk) in datalayer.geojson.name
+
+
+def test_clone_should_return_new_instance(map, datalayer):
+ clone = datalayer.clone()
+ assert datalayer.pk != clone.pk
+ assert datalayer.name == clone.name
+ assert datalayer.map == clone.map
+
+
+def test_clone_should_update_map_if_passed(datalayer, user, licence):
+ map = MapFactory(owner=user, licence=licence)
+ clone = datalayer.clone(map_inst=map)
+ assert datalayer.pk != clone.pk
+ assert datalayer.name == clone.name
+ assert datalayer.map != clone.map
+ assert map == clone.map
+
+
+def test_clone_should_clone_geojson_too(datalayer):
+ clone = datalayer.clone()
+ assert datalayer.pk != clone.pk
+ assert clone.geojson is not None
+ assert clone.geojson.path != datalayer.geojson.path
+
+
+def test_should_remove_old_versions_on_save(datalayer, map, settings):
+ settings.LEAFLET_STORAGE_KEEP_VERSIONS = 3
+ root = datalayer.storage_root()
+ before = len(datalayer.geojson.storage.listdir(root)[1])
+ newer = '%s/%s_1440924889.geojson' % (root, datalayer.pk)
+ medium = '%s/%s_1440923687.geojson' % (root, datalayer.pk)
+ older = '%s/%s_1440918637.geojson' % (root, datalayer.pk)
+ for path in [medium, newer, older]:
+ datalayer.geojson.storage.save(path, ContentFile("{}"))
+ datalayer.geojson.storage.save(path + '.gz', ContentFile("{}"))
+ assert len(datalayer.geojson.storage.listdir(root)[1]) == 6 + before
+ datalayer.save()
+ files = datalayer.geojson.storage.listdir(root)[1]
+ assert len(files) == 5
+ assert os.path.basename(newer) in files
+ assert os.path.basename(newer + '.gz') in files
+ assert os.path.basename(medium) in files
+ assert os.path.basename(medium + '.gz') in files
+ assert os.path.basename(datalayer.geojson.path) in files
+ assert os.path.basename(older) not in files
+ assert os.path.basename(older + '.gz') not in files
diff --git a/umap/tests/test_datalayer_views.py b/umap/tests/test_datalayer_views.py
new file mode 100644
index 00000000..b4f90f73
--- /dev/null
+++ b/umap/tests/test_datalayer_views.py
@@ -0,0 +1,162 @@
+import json
+
+import pytest
+from django.core.files.base import ContentFile
+from django.urls import reverse
+
+from umap.models import DataLayer, Map
+
+from .base import MapFactory
+
+pytestmark = pytest.mark.django_db
+
+
+@pytest.fixture
+def post_data():
+ return {
+ "name": 'name',
+ "display_on_load": True,
+ "rank": 0,
+ "geojson": '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-3.1640625,53.014783245859235],[-3.1640625,51.86292391360244],[-0.50537109375,51.385495069223204],[1.16455078125,52.38901106223456],[-0.41748046875,53.91728101547621],[-2.109375,53.85252660044951],[-3.1640625,53.014783245859235]]]},"properties":{"_storage_options":{},"name":"Ho god, sounds like a polygouine"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[1.8017578124999998,51.16556659836182],[-0.48339843749999994,49.710272582105695],[-3.1640625,50.0923932109388],[-5.60302734375,51.998410382390325]]},"properties":{"_storage_options":{},"name":"Light line"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[0.63720703125,51.15178610143037]},"properties":{"_storage_options":{},"name":"marker he"}}],"_storage":{"displayOnLoad":true,"name":"new name","id":1668,"remoteData":{},"color":"LightSeaGreen","description":"test"}}' # noqa
+ }
+
+
+def test_get(client, settings, datalayer):
+ url = reverse('datalayer_view', args=(datalayer.pk, ))
+ response = client.get(url)
+ if getattr(settings, 'LEAFLET_STORAGE_XSENDFILE_HEADER', None):
+ assert response['ETag'] is not None
+ assert response['Last-Modified'] is not None
+ assert response['Cache-Control'] is not None
+ assert 'Content-Encoding' not in response
+ j = json.loads(response.content.decode())
+ assert '_storage' in j
+ assert 'features' in j
+ assert j['type'] == 'FeatureCollection'
+
+
+def test_update(client, datalayer, map, post_data):
+ url = reverse('datalayer_update', args=(map.pk, datalayer.pk))
+ client.login(username=map.owner.username, password="123123")
+ name = 'new name'
+ rank = 2
+ post_data['name'] = name
+ post_data['rank'] = rank
+ response = client.post(url, post_data, follow=True)
+ assert response.status_code == 200
+ modified_datalayer = DataLayer.objects.get(pk=datalayer.pk)
+ assert modified_datalayer.name == name
+ assert modified_datalayer.rank == rank
+ # Test response is a json
+ j = json.loads(response.content.decode())
+ assert "id" in j
+ assert datalayer.pk == j['id']
+
+
+def test_should_not_be_possible_to_update_with_wrong_map_id_in_url(client, datalayer, map, post_data): # noqa
+ other_map = MapFactory(owner=map.owner)
+ url = reverse('datalayer_update', args=(other_map.pk, datalayer.pk))
+ client.login(username=map.owner.username, password="123123")
+ name = 'new name'
+ post_data['name'] = name
+ response = client.post(url, post_data, follow=True)
+ assert response.status_code == 403
+ modified_datalayer = DataLayer.objects.get(pk=datalayer.pk)
+ assert modified_datalayer.name == datalayer.name
+
+
+def test_delete(client, datalayer, map):
+ url = reverse('datalayer_delete', args=(map.pk, datalayer.pk))
+ client.login(username=map.owner.username, password='123123')
+ response = client.post(url, {}, follow=True)
+ assert response.status_code == 200
+ assert not DataLayer.objects.filter(pk=datalayer.pk).count()
+ # Check that map has not been impacted
+ assert Map.objects.filter(pk=map.pk).exists()
+ # Test response is a json
+ j = json.loads(response.content.decode())
+ assert 'info' in j
+
+
+def test_should_not_be_possible_to_delete_with_wrong_map_id_in_url(client, datalayer, map): # noqa
+ other_map = MapFactory(owner=map.owner)
+ url = reverse('datalayer_delete', args=(other_map.pk, datalayer.pk))
+ client.login(username=map.owner.username, password='123123')
+ response = client.post(url, {}, follow=True)
+ assert response.status_code == 403
+ assert DataLayer.objects.filter(pk=datalayer.pk).exists()
+
+
+def test_get_gzipped(client, datalayer, settings):
+ url = reverse('datalayer_view', args=(datalayer.pk, ))
+ response = client.get(url, HTTP_ACCEPT_ENCODING='gzip')
+ if getattr(settings, 'LEAFLET_STORAGE_XSENDFILE_HEADER', None):
+ assert response['ETag'] is not None
+ assert response['Last-Modified'] is not None
+ assert response['Cache-Control'] is not None
+ assert response['Content-Encoding'] == 'gzip'
+
+
+def test_optimistic_concurrency_control_with_good_etag(client, datalayer, map, post_data): # noqa
+ # Get Etag
+ url = reverse('datalayer_view', args=(datalayer.pk, ))
+ response = client.get(url)
+ etag = response['ETag']
+ url = reverse('datalayer_update',
+ args=(map.pk, datalayer.pk))
+ client.login(username=map.owner.username, password="123123")
+ name = 'new name'
+ post_data['name'] = 'new name'
+ response = client.post(url, post_data, follow=True, HTTP_IF_MATCH=etag)
+ assert response.status_code == 200
+ modified_datalayer = DataLayer.objects.get(pk=datalayer.pk)
+ assert modified_datalayer.name == name
+
+
+def test_optimistic_concurrency_control_with_bad_etag(client, datalayer, map, post_data): # noqa
+ url = reverse('datalayer_update', args=(map.pk, datalayer.pk))
+ client.login(username=map.owner.username, password='123123')
+ name = 'new name'
+ post_data['name'] = name
+ response = client.post(url, post_data, follow=True, HTTP_IF_MATCH='xxx')
+ assert response.status_code == 412
+ modified_datalayer = DataLayer.objects.get(pk=datalayer.pk)
+ assert modified_datalayer.name != name
+
+
+def test_optimistic_concurrency_control_with_empty_etag(client, datalayer, map, post_data): # noqa
+ url = reverse('datalayer_update', args=(map.pk, datalayer.pk))
+ client.login(username=map.owner.username, password='123123')
+ name = 'new name'
+ post_data['name'] = name
+ response = client.post(url, post_data, follow=True, HTTP_IF_MATCH=None)
+ assert response.status_code == 200
+ modified_datalayer = DataLayer.objects.get(pk=datalayer.pk)
+ assert modified_datalayer.name == name
+
+
+def test_versions_should_return_versions(client, datalayer, map, settings):
+ root = datalayer.storage_root()
+ datalayer.geojson.storage.save(
+ '%s/%s_1440924889.geojson' % (root, datalayer.pk),
+ ContentFile("{}"))
+ datalayer.geojson.storage.save(
+ '%s/%s_1440923687.geojson' % (root, datalayer.pk),
+ ContentFile("{}"))
+ datalayer.geojson.storage.save(
+ '%s/%s_1440918637.geojson' % (root, datalayer.pk),
+ ContentFile("{}"))
+ url = reverse('datalayer_versions', args=(datalayer.pk, ))
+ versions = json.loads(client.get(url).content.decode())
+ assert len(versions['versions']) == 4
+ version = {'name': '%s_1440918637.geojson' % datalayer.pk, 'size': 2,
+ 'at': '1440918637'}
+ assert version in versions['versions']
+
+
+def test_version_should_return_one_version_geojson(client, datalayer, map):
+ root = datalayer.storage_root()
+ name = '%s_1440924889.geojson' % datalayer.pk
+ datalayer.geojson.storage.save('%s/%s' % (root, name), ContentFile("{}"))
+ url = reverse('datalayer_version', args=(datalayer.pk, name))
+ assert client.get(url).content.decode() == "{}"
diff --git a/umap/tests/test_fields.py b/umap/tests/test_fields.py
new file mode 100644
index 00000000..4d744cd3
--- /dev/null
+++ b/umap/tests/test_fields.py
@@ -0,0 +1,43 @@
+import json
+
+import pytest
+
+from umap.models import Map
+
+pytestmark = pytest.mark.django_db
+
+
+def test_can_use_dict(map):
+ d = {'locateControl': True}
+ map.settings = d
+ map.save()
+ assert Map.objects.get(pk=map.pk).settings == d
+
+
+def test_can_set_item(map):
+ d = {'locateControl': True}
+ map.settings = d
+ map.save()
+ map_inst = Map.objects.get(pk=map.pk)
+ map_inst.settings['color'] = 'DarkGreen'
+ assert map_inst.settings['locateControl'] is True
+
+
+def test_should_return_a_dict_if_none(map):
+ map.settings = None
+ map.save()
+ assert Map.objects.get(pk=map.pk).settings == {}
+
+
+def test_should_not_double_dumps(map):
+ map.settings = '{"locate": true}'
+ map.save()
+ assert Map.objects.get(pk=map.pk).settings == {'locate': True}
+
+
+def test_value_to_string(map):
+ d = {'locateControl': True}
+ map.settings = d
+ map.save()
+ field = Map._meta.get_field('settings')
+ assert json.loads(field.value_to_string(map)) == d
diff --git a/umap/tests/test_licence.py b/umap/tests/test_licence.py
new file mode 100644
index 00000000..fd5e606b
--- /dev/null
+++ b/umap/tests/test_licence.py
@@ -0,0 +1,12 @@
+import pytest
+
+from umap.models import DataLayer, Map
+
+pytestmark = pytest.mark.django_db
+
+
+def test_licence_delete_should_not_remove_linked_maps(map, licence, datalayer):
+ assert map.licence == licence
+ licence.delete()
+ assert Map.objects.filter(pk=map.pk).exists()
+ assert DataLayer.objects.filter(pk=datalayer.pk).exists()
diff --git a/umap/tests/test_map.py b/umap/tests/test_map.py
new file mode 100644
index 00000000..c72d3d1f
--- /dev/null
+++ b/umap/tests/test_map.py
@@ -0,0 +1,109 @@
+import pytest
+from django.contrib.auth.models import AnonymousUser
+from django.urls import reverse
+
+from umap.models import Map
+
+from .base import MapFactory
+
+pytestmark = pytest.mark.django_db
+
+
+def test_anonymous_can_edit_if_status_anonymous(map):
+ anonymous = AnonymousUser()
+ map.edit_status = map.ANONYMOUS
+ map.save()
+ assert map.can_edit(anonymous)
+
+
+def test_anonymous_cannot_edit_if_not_status_anonymous(map):
+ anonymous = AnonymousUser()
+ map.edit_status = map.OWNER
+ map.save()
+ assert not map.can_edit(anonymous)
+
+
+def test_non_editors_can_edit_if_status_anonymous(map, user):
+ assert map.owner != user
+ map.edit_status = map.ANONYMOUS
+ map.save()
+ assert map.can_edit(user)
+
+
+def test_non_editors_cannot_edit_if_not_status_anonymous(map, user):
+ map.edit_status = map.OWNER
+ map.save()
+ assert not map.can_edit(user)
+
+
+def test_editors_cannot_edit_if_status_owner(map, user):
+ map.edit_status = map.OWNER
+ map.editors.add(user)
+ map.save()
+ assert not map.can_edit(user)
+
+
+def test_editors_can_edit_if_status_editors(map, user):
+ map.edit_status = map.EDITORS
+ map.editors.add(user)
+ map.save()
+ assert map.can_edit(user)
+
+
+def test_logged_in_user_should_be_allowed_for_anonymous_map_with_anonymous_edit_status(map, user, rf): # noqa
+ map.owner = None
+ map.edit_status = map.ANONYMOUS
+ map.save()
+ url = reverse('map_update', kwargs={'map_id': map.pk})
+ request = rf.get(url)
+ request.user = user
+ assert map.can_edit(user, request)
+
+
+def test_clone_should_return_new_instance(map, user):
+ clone = map.clone()
+ assert map.pk != clone.pk
+ assert u"Clone of " + map.name == clone.name
+ assert map.settings == clone.settings
+ assert map.center == clone.center
+ assert map.zoom == clone.zoom
+ assert map.licence == clone.licence
+ assert map.tilelayer == clone.tilelayer
+
+
+def test_clone_should_keep_editors(map, user):
+ map.editors.add(user)
+ clone = map.clone()
+ assert map.pk != clone.pk
+ assert user in map.editors.all()
+ assert user in clone.editors.all()
+
+
+def test_clone_should_update_owner_if_passed(map, user):
+ clone = map.clone(owner=user)
+ assert map.pk != clone.pk
+ assert map.owner != clone.owner
+ assert user == clone.owner
+
+
+def test_clone_should_clone_datalayers_and_features_too(map, user, datalayer):
+ clone = map.clone()
+ assert map.pk != clone.pk
+ assert map.datalayer_set.count() == 1
+ assert clone.datalayer_set.count() == 1
+ other = clone.datalayer_set.all()[0]
+ assert datalayer in map.datalayer_set.all()
+ assert other.pk != datalayer.pk
+ assert other.name == datalayer.name
+ assert other.geojson is not None
+ assert other.geojson.path != datalayer.geojson.path
+
+
+def test_publicmanager_should_get_only_public_maps(map, user, licence):
+ map.share_status = map.PUBLIC
+ open_map = MapFactory(owner=user, licence=licence, share_status=Map.OPEN)
+ private_map = MapFactory(owner=user, licence=licence,
+ share_status=Map.PRIVATE)
+ assert map in Map.public.all()
+ assert open_map not in Map.public.all()
+ assert private_map not in Map.public.all()
diff --git a/umap/tests/test_map_views.py b/umap/tests/test_map_views.py
new file mode 100644
index 00000000..37241776
--- /dev/null
+++ b/umap/tests/test_map_views.py
@@ -0,0 +1,449 @@
+import json
+
+import pytest
+from django.contrib.auth import get_user_model
+from django.urls import reverse
+
+from umap.models import DataLayer, Map
+
+from .base import login_required
+
+pytestmark = pytest.mark.django_db
+User = get_user_model()
+
+
+@pytest.fixture
+def post_data():
+ return {
+ 'name': 'name',
+ 'center': '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
+ 'settings': '{"type":"Feature","geometry":{"type":"Point","coordinates":[5.0592041015625,52.05924589011585]},"properties":{"tilelayer":{"maxZoom":20,"url_template":"http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png","minZoom":0,"attribution":"HOT and friends"},"licence":"","description":"","name":"test enrhûmé","tilelayersControl":true,"displayDataBrowserOnLoad":false,"displayPopupFooter":true,"displayCaptionOnLoad":false,"miniMap":true,"moreControl":true,"scaleControl":true,"zoomControl":true,"datalayersControl":true,"zoom":8}}' # noqa
+ }
+
+
+def test_create(client, user, post_data):
+ url = reverse('map_create')
+ # POST only mendatory fields
+ name = 'test-map-with-new-name'
+ post_data['name'] = name
+ client.login(username=user.username, password="123123")
+ response = client.post(url, post_data)
+ assert response.status_code == 200
+ j = json.loads(response.content.decode())
+ created_map = Map.objects.latest('pk')
+ assert j['id'] == created_map.pk
+ assert created_map.name == name
+
+
+def test_map_create_permissions(client, settings):
+ settings.LEAFLET_STORAGE_ALLOW_ANONYMOUS = False
+ url = reverse('map_create')
+ # POST anonymous
+ response = client.post(url, {})
+ assert login_required(response)
+
+
+def test_map_update_access(client, map, user):
+ url = reverse('map_update', kwargs={'map_id': map.pk})
+ # GET anonymous
+ response = client.get(url)
+ assert login_required(response)
+ # POST anonymous
+ response = client.post(url, {})
+ assert login_required(response)
+ # GET with wrong permissions
+ client.login(username=user.username, password="123123")
+ response = client.get(url)
+ assert response.status_code == 403
+ # POST with wrong permissions
+ client.login(username=user.username, password="123123")
+ response = client.post(url, {})
+ assert response.status_code == 403
+
+
+def test_map_update_permissions_access(client, map, user):
+ url = reverse('map_update_permissions', kwargs={'map_id': map.pk})
+ # GET anonymous
+ response = client.get(url)
+ assert login_required(response)
+ # POST anonymous
+ response = client.post(url, {})
+ assert login_required(response)
+ # GET with wrong permissions
+ client.login(username=user.username, password="123123")
+ response = client.get(url)
+ assert response.status_code == 403
+ # POST with wrong permissions
+ client.login(username=user.username, password="123123")
+ response = client.post(url, {})
+ assert response.status_code == 403
+
+
+def test_update(client, map, post_data):
+ url = reverse('map_update', kwargs={'map_id': map.pk})
+ # POST only mendatory fields
+ name = 'new map name'
+ post_data['name'] = name
+ client.login(username=map.owner.username, password="123123")
+ response = client.post(url, post_data)
+ assert response.status_code == 200
+ j = json.loads(response.content.decode())
+ assert 'html' not in j
+ updated_map = Map.objects.get(pk=map.pk)
+ assert j['id'] == updated_map.pk
+ assert updated_map.name == name
+
+
+def test_delete(client, map, datalayer):
+ url = reverse('map_delete', args=(map.pk, ))
+ client.login(username=map.owner.username, password="123123")
+ response = client.post(url, {}, follow=True)
+ assert response.status_code == 200
+ assert not Map.objects.filter(pk=map.pk).exists()
+ assert not DataLayer.objects.filter(pk=datalayer.pk).exists()
+ # Check that user has not been impacted
+ assert User.objects.filter(pk=map.owner.pk).exists()
+ # Test response is a json
+ j = json.loads(response.content.decode())
+ assert 'redirect' in j
+
+
+def test_wrong_slug_should_redirect_to_canonical(client, map):
+ url = reverse('map', kwargs={'pk': map.pk, 'slug': 'wrong-slug'})
+ canonical = reverse('map', kwargs={'pk': map.pk,
+ 'slug': map.slug})
+ response = client.get(url)
+ assert response.status_code == 301
+ assert response['Location'] == canonical
+
+
+def test_wrong_slug_should_redirect_with_query_string(client, map):
+ url = reverse('map', kwargs={'pk': map.pk, 'slug': 'wrong-slug'})
+ url = "{}?allowEdit=0".format(url)
+ canonical = reverse('map', kwargs={'pk': map.pk,
+ 'slug': map.slug})
+ canonical = "{}?allowEdit=0".format(canonical)
+ response = client.get(url)
+ assert response.status_code == 301
+ assert response['Location'] == canonical
+
+
+def test_should_not_consider_the_query_string_for_canonical_check(client, map):
+ url = reverse('map', kwargs={'pk': map.pk, 'slug': map.slug})
+ url = "{}?allowEdit=0".format(url)
+ response = client.get(url)
+ assert response.status_code == 200
+
+
+def test_short_url_should_redirect_to_canonical(client, map):
+ url = reverse('map_short_url', kwargs={'pk': map.pk})
+ canonical = reverse('map', kwargs={'pk': map.pk,
+ 'slug': map.slug})
+ response = client.get(url)
+ assert response.status_code == 301
+ assert response['Location'] == canonical
+
+
+def test_old_url_should_redirect_to_canonical(client, map):
+ url = reverse(
+ 'map_old_url',
+ kwargs={'username': map.owner.username, 'slug': map.slug}
+ )
+ canonical = reverse('map', kwargs={'pk': map.pk,
+ 'slug': map.slug})
+ response = client.get(url)
+ assert response.status_code == 301
+ assert response['Location'] == canonical
+
+
+def test_clone_map_should_create_a_new_instance(client, map):
+ assert Map.objects.count() == 1
+ url = reverse('map_clone', kwargs={'map_id': map.pk})
+ client.login(username=map.owner.username, password="123123")
+ response = client.post(url)
+ assert response.status_code == 200
+ assert Map.objects.count() == 2
+ clone = Map.objects.latest('pk')
+ assert clone.pk != map.pk
+ assert clone.name == u"Clone of " + map.name
+
+
+def test_user_not_allowed_should_not_clone_map(client, map, user, settings):
+ settings.LEAFLET_STORAGE_ALLOW_ANONYMOUS = False
+ assert Map.objects.count() == 1
+ url = reverse('map_clone', kwargs={'map_id': map.pk})
+ map.edit_status = map.OWNER
+ map.save()
+ response = client.post(url)
+ assert login_required(response)
+ client.login(username=user.username, password="123123")
+ response = client.post(url)
+ assert response.status_code == 403
+ map.edit_status = map.ANONYMOUS
+ map.save()
+ client.logout()
+ response = client.post(url)
+ assert response.status_code == 403
+ assert Map.objects.count() == 1
+
+
+def test_clone_should_set_cloner_as_owner(client, map, user):
+ url = reverse('map_clone', kwargs={'map_id': map.pk})
+ map.edit_status = map.EDITORS
+ map.editors.add(user)
+ map.save()
+ client.login(username=user.username, password="123123")
+ response = client.post(url)
+ assert response.status_code == 200
+ assert Map.objects.count() == 2
+ clone = Map.objects.latest('pk')
+ assert clone.pk != map.pk
+ assert clone.name == u"Clone of " + map.name
+ assert clone.owner == user
+
+
+def test_map_creation_should_allow_unicode_names(client, map, post_data):
+ url = reverse('map_create')
+ # POST only mendatory fields
+ name = u'Академический'
+ post_data['name'] = name
+ client.login(username=map.owner.username, password="123123")
+ response = client.post(url, post_data)
+ assert response.status_code == 200
+ j = json.loads(response.content.decode())
+ created_map = Map.objects.latest('pk')
+ assert j['id'] == created_map.pk
+ assert created_map.name == name
+ # Lower case of the russian original name
+ # self.assertEqual(created_map.slug, u"академический")
+ # for now we fallback to "map", see unicode_name branch
+ assert created_map.slug == 'map'
+
+
+def test_anonymous_can_access_map_with_share_status_public(client, map):
+ url = reverse('map', args=(map.slug, map.pk))
+ map.share_status = map.PUBLIC
+ map.save()
+ response = client.get(url)
+ assert response.status_code == 200
+
+
+def test_anonymous_can_access_map_with_share_status_open(client, map):
+ url = reverse('map', args=(map.slug, map.pk))
+ map.share_status = map.OPEN
+ map.save()
+ response = client.get(url)
+ assert response.status_code == 200
+
+
+def test_anonymous_cannot_access_map_with_share_status_private(client, map):
+ url = reverse('map', args=(map.slug, map.pk))
+ map.share_status = map.PRIVATE
+ map.save()
+ response = client.get(url)
+ assert response.status_code == 403
+
+
+def test_owner_can_access_map_with_share_status_private(client, map):
+ url = reverse('map', args=(map.slug, map.pk))
+ map.share_status = map.PRIVATE
+ map.save()
+ client.login(username=map.owner.username, password="123123")
+ response = client.get(url)
+ assert response.status_code == 200
+
+
+def test_editors_can_access_map_with_share_status_private(client, map, user):
+ url = reverse('map', args=(map.slug, map.pk))
+ map.share_status = map.PRIVATE
+ map.editors.add(user)
+ map.save()
+ client.login(username=user.username, password="123123")
+ response = client.get(url)
+ assert response.status_code == 200
+
+
+def test_non_editor_cannot_access_map_if_share_status_private(client, map, user): # noqa
+ url = reverse('map', args=(map.slug, map.pk))
+ map.share_status = map.PRIVATE
+ map.save()
+ client.login(username=user.username, password="123123")
+ response = client.get(url)
+ assert response.status_code == 403
+
+
+def test_map_geojson_view(client, map):
+ url = reverse('map_geojson', args=(map.pk, ))
+ response = client.get(url)
+ j = json.loads(response.content.decode())
+ assert 'type' in j
+
+
+def test_only_owner_can_delete(client, map, user):
+ map.editors.add(user)
+ url = reverse('map_delete', kwargs={'map_id': map.pk})
+ client.login(username=user.username, password="123123")
+ response = client.post(url, {}, follow=True)
+ assert response.status_code == 403
+
+
+def test_map_editors_do_not_see_owner_change_input(client, map, user):
+ map.editors.add(user)
+ map.edit_status = map.EDITORS
+ map.save()
+ url = reverse('map_update_permissions', kwargs={'map_id': map.pk})
+ client.login(username=user.username, password="123123")
+ response = client.get(url)
+ assert 'id_owner' not in response
+
+
+def test_logged_in_user_can_edit_map_editable_by_anonymous(client, map, user):
+ map.owner = None
+ map.edit_status = map.ANONYMOUS
+ map.save()
+ client.login(username=user.username, password="123123")
+ url = reverse('map_update', kwargs={'map_id': map.pk})
+ new_name = 'this is my new name'
+ data = {
+ 'center': '{"type":"Point","coordinates":[13.447265624999998,48.94415123418794]}', # noqa
+ 'name': new_name
+ }
+ response = client.post(url, data)
+ assert response.status_code == 200
+ assert Map.objects.get(pk=map.pk).name == new_name
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_anonymous_create(cookieclient, post_data):
+ url = reverse('map_create')
+ # POST only mendatory fields
+ name = 'test-map-with-new-name'
+ post_data['name'] = name
+ response = cookieclient.post(url, post_data)
+ assert response.status_code == 200
+ j = json.loads(response.content.decode())
+ created_map = Map.objects.latest('pk')
+ assert j['id'] == created_map.pk
+ assert created_map.name == name
+ key, value = created_map.signed_cookie_elements
+ assert key in cookieclient.cookies
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_anonymous_update_without_cookie_fails(client, anonymap, post_data): # noqa
+ url = reverse('map_update', kwargs={'map_id': anonymap.pk})
+ response = client.post(url, post_data)
+ assert response.status_code == 403
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_anonymous_update_with_cookie_should_work(cookieclient, anonymap, post_data): # noqa
+ url = reverse('map_update', kwargs={'map_id': anonymap.pk})
+ # POST only mendatory fields
+ name = 'new map name'
+ post_data['name'] = name
+ response = cookieclient.post(url, post_data)
+ assert response.status_code == 200
+ j = json.loads(response.content.decode())
+ updated_map = Map.objects.get(pk=anonymap.pk)
+ assert j['id'] == updated_map.pk
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_anonymous_delete(cookieclient, anonymap):
+ url = reverse('map_delete', args=(anonymap.pk, ))
+ response = cookieclient.post(url, {}, follow=True)
+ assert response.status_code == 200
+ assert not Map.objects.filter(pk=anonymap.pk).count()
+ # Test response is a json
+ j = json.loads(response.content.decode())
+ assert 'redirect' in j
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_no_cookie_cant_delete(client, anonymap):
+ url = reverse('map_delete', args=(anonymap.pk, ))
+ response = client.post(url, {}, follow=True)
+ assert response.status_code == 403
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_anonymous_edit_url(cookieclient, anonymap):
+ url = anonymap.get_anonymous_edit_url()
+ canonical = reverse('map', kwargs={'pk': anonymap.pk,
+ 'slug': anonymap.slug})
+ response = cookieclient.get(url)
+ assert response.status_code == 302
+ assert response['Location'] == canonical
+ key, value = anonymap.signed_cookie_elements
+ assert key in cookieclient.cookies
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_bad_anonymous_edit_url_should_return_403(cookieclient, anonymap):
+ url = anonymap.get_anonymous_edit_url()
+ url = reverse(
+ 'map_anonymous_edit_url',
+ kwargs={'signature': "%s:badsignature" % anonymap.pk}
+ )
+ response = cookieclient.get(url)
+ assert response.status_code == 403
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_authenticated_user_with_cookie_is_attached_as_owner(cookieclient, anonymap, post_data, user): # noqa
+ url = reverse('map_update', kwargs={'map_id': anonymap.pk})
+ cookieclient.login(username=user.username, password="123123")
+ assert anonymap.owner is None
+ # POST only mendatory filds
+ name = 'new map name for authenticat_anonymoused user'
+ post_data['name'] = name
+ response = cookieclient.post(url, post_data)
+ assert response.status_code == 200
+ j = json.loads(response.content.decode())
+ updated_map = Map.objects.get(pk=anonymap.pk)
+ assert j['id'] == updated_map.pk
+ assert updated_map.owner.pk, user.pk
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_clone_anonymous_map_should_not_be_possible_if_user_is_not_allowed(client, anonymap, user): # noqa
+ assert Map.objects.count() == 1
+ url = reverse('map_clone', kwargs={'map_id': anonymap.pk})
+ anonymap.edit_status = anonymap.OWNER
+ anonymap.save()
+ response = client.post(url)
+ assert response.status_code == 403
+ client.login(username=user.username, password="123123")
+ response = client.post(url)
+ assert response.status_code == 403
+ assert Map.objects.count() == 1
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_clone_map_should_be_possible_if_edit_status_is_anonymous(client, anonymap): # noqa
+ assert Map.objects.count() == 1
+ url = reverse('map_clone', kwargs={'map_id': anonymap.pk})
+ anonymap.edit_status = anonymap.ANONYMOUS
+ anonymap.save()
+ response = client.post(url)
+ assert response.status_code == 200
+ assert Map.objects.count() == 2
+ clone = Map.objects.latest('pk')
+ assert clone.pk != anonymap.pk
+ assert clone.name == 'Clone of ' + anonymap.name
+ assert clone.owner is None
+
+
+@pytest.mark.usefixtures('allow_anonymous')
+def test_anyone_can_access_anonymous_map(cookieclient, anonymap):
+ url = reverse('map', args=(anonymap.slug, anonymap.pk))
+ anonymap.share_status = anonymap.PUBLIC
+ response = cookieclient.get(url)
+ assert response.status_code == 200
+ anonymap.share_status = anonymap.OPEN
+ response = cookieclient.get(url)
+ assert response.status_code == 200
+ anonymap.share_status = anonymap.PRIVATE
+ response = cookieclient.get(url)
+ assert response.status_code == 200
diff --git a/umap/tests/test_tilelayer.py b/umap/tests/test_tilelayer.py
new file mode 100644
index 00000000..11e81709
--- /dev/null
+++ b/umap/tests/test_tilelayer.py
@@ -0,0 +1,21 @@
+import pytest
+
+from .base import TileLayerFactory
+
+pytestmark = pytest.mark.django_db
+
+
+def test_tilelayer_json():
+ tilelayer = TileLayerFactory(attribution='Attribution', maxZoom=19,
+ minZoom=0, name='Name', rank=1, tms=True,
+ url_template='http://{s}.x.fr/{z}/{x}/{y}')
+ assert tilelayer.json == {
+ 'attribution': 'Attribution',
+ 'id': tilelayer.id,
+ 'maxZoom': 19,
+ 'minZoom': 0,
+ 'name': 'Name',
+ 'rank': 1,
+ 'tms': True,
+ 'url_template': 'http://{s}.x.fr/{z}/{x}/{y}'
+ }