Merge leaflet_storage tests

This commit is contained in:
Yohan Boniface 2018-05-19 11:54:08 +02:00
parent 65e939c4ee
commit 53c738abaf
19 changed files with 1519 additions and 2 deletions

1
.gitignore vendored
View file

@ -53,6 +53,7 @@ nosetests.xml
coverage.xml
*,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo

View file

@ -1,5 +1,5 @@
test:
py.test
py.test -xv
develop:
python setup.py develop
compilemessages:

View file

@ -1 +0,0 @@
from .test_views import *

96
umap/tests/base.py Normal file
View file

@ -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

64
umap/tests/conftest.py Normal file
View file

@ -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()

View file

@ -0,0 +1,2 @@
Foo,Latitude,geo_Longitude,title,description
bar,41.34,122.86,a point somewhere,the description of this point
1 Foo Latitude geo_Longitude title description
2 bar 41.34 122.86 a point somewhere the description of this point

View file

@ -0,0 +1,17 @@
<gpx
version="1.1"
creator="GPSBabel - http://www.gpsbabel.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<wpt lat="45.44283" lon="-121.72904"><ele>1374</ele><name>Simple Point</name><desc>Simple description</desc></wpt>
<trk>
<name>Simple path</name>
<desc>Simple description</desc>
<trkseg>
<trkpt lat="45.4431641" lon="-121.7295456"></trkpt>
<trkpt lat="45.4428615" lon="-121.7290800"></trkpt>
<trkpt lat="45.4425697" lon="-121.7279085"></trkpt>
</trkseg>
</trk>
</gpx>

View file

@ -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"
}
}
]
}

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Placemark>
<name>Simple point</name>
<description>Here is a simple description.</description>
<Point>
<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Simple path</name>
<description>Simple description</description>
<LineString>
<coordinates>-112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357</coordinates>
</LineString>
</Placemark>
<Placemark>
<name>Simple polygon</name>
<description>A description.</description>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>
-77.05788457660967,38.87253259892824,100
-77.05465973756702,38.87291016281703,100
-77.05315536854791,38.87053267794386,100
-77.05788457660967,38.87253259892824,100
</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
</kml>

View file

@ -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"
}
}
]
}

View file

@ -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"
}
}
]
}

View file

@ -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"
}
}
]
}

View file

@ -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

View file

@ -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() == "{}"

43
umap/tests/test_fields.py Normal file
View file

@ -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

View file

@ -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()

109
umap/tests/test_map.py Normal file
View file

@ -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()

View file

@ -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

View file

@ -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}'
}