934 lines
35 KiB
Python
934 lines
35 KiB
Python
import json
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.core.exceptions import ValidationError
|
|
|
|
import tests.utils
|
|
from passerelle.apps.arcgis.models import ArcGIS, Query, SqlFormatter, validate_where
|
|
from passerelle.base.models import AccessRight, ApiUser
|
|
from passerelle.utils import import_site
|
|
from tests.test_manager import login
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
# from http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/fold/serv/MapServer/1
|
|
STATES = '''{
|
|
"fieldAliases" : {
|
|
"OBJECTID" : "OBJECTID",
|
|
"STATE_NAME" : "STATE_NAME",
|
|
"STATE_ABBR" : "STATE_ABBR"
|
|
},
|
|
"features" : [
|
|
{
|
|
"attributes" : {
|
|
"STATE_NAME" : "Texas",
|
|
"STATE_ABBR" : "TX",
|
|
"OBJECTID" : 40
|
|
},
|
|
"geometry" : {
|
|
"rings" : [
|
|
[
|
|
[-105.998886788462, 31.3939400524361],
|
|
[-106.21328556164, 31.4782464373727]
|
|
]
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"geometry" : {
|
|
"rings" : [
|
|
[
|
|
[-111.475425113078, 44.7021622250113],
|
|
[-111.480804007084, 44.6914159859524]
|
|
]
|
|
]
|
|
},
|
|
"attributes" : {
|
|
"STATE_NAME" : "Montana",
|
|
"STATE_ABBR" : "MT",
|
|
"OBJECTID" : 2
|
|
}
|
|
}
|
|
],
|
|
"spatialReference" : {
|
|
"wkid" : 4326
|
|
},
|
|
"fields" : [
|
|
{
|
|
"alias" : "OBJECTID",
|
|
"type" : "esriFieldTypeOID",
|
|
"name" : "OBJECTID"
|
|
},
|
|
{
|
|
"type" : "esriFieldTypeString",
|
|
"alias" : "STATE_NAME",
|
|
"length" : 25,
|
|
"name" : "STATE_NAME"
|
|
},
|
|
{
|
|
"length" : 2,
|
|
"alias" : "STATE_ABBR",
|
|
"type" : "esriFieldTypeString",
|
|
"name" : "STATE_ABBR"
|
|
}
|
|
],
|
|
"geometryType" : "esriGeometryPolygon",
|
|
"displayFieldName" : "STATE_NAME"
|
|
}'''
|
|
|
|
FEATURES = '''{
|
|
"features": [
|
|
{
|
|
"attributes": {
|
|
"nom": "Le Nom",
|
|
"id": "onze",
|
|
"objectid": 11
|
|
},
|
|
"geometry" : {
|
|
"rings" : [
|
|
[
|
|
[-111.475425113078, 44.7021622250113],
|
|
[-111.480804007084, 44.6914159859524]
|
|
]
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"attributes": {
|
|
"nom": "Autre Nom",
|
|
"id": "dix",
|
|
"objectid": 10
|
|
},
|
|
"geometry" : {
|
|
"rings" : [
|
|
[
|
|
[-111.475425113078, 44.7021622250113],
|
|
[-111.480804007084, 44.6914159859524]
|
|
]
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"fields": [
|
|
{
|
|
"alias": "OBJECTID",
|
|
"name": "objectid",
|
|
"type": "esriFieldTypeOID"
|
|
},
|
|
{
|
|
"alias": "quartier",
|
|
"name": "nom",
|
|
"type": "esriFieldTypeString"
|
|
},
|
|
{
|
|
"alias": "code",
|
|
"length": 5,
|
|
"name": "id",
|
|
"type": "esriFieldTypeString"
|
|
}
|
|
],
|
|
"geometryType": "esriGeometryPolygon",
|
|
"globalIdFieldName": "",
|
|
"objectIdFieldName": "objectid",
|
|
"spatialReference": {
|
|
"latestWkid": 3947,
|
|
"wkid": 3947
|
|
}
|
|
}'''
|
|
|
|
INFO = '''{
|
|
"authInfo": {
|
|
"isTokenBasedSecurity": true,
|
|
"tokenServicesUrl": "https://arcgis/portal/sharing/rest/generateToken"
|
|
},
|
|
"currentVersion": 10.81,
|
|
"fullVersion": "10.8.1",
|
|
"owningSystemUrl": "https://arcgis/portal",
|
|
"secureSoapUrl": null,
|
|
"soapUrl": "https://arcgis/arcgis/services"
|
|
}'''
|
|
|
|
TOKEN = '''{
|
|
"expires": 1649761676673,
|
|
"ssl": true,
|
|
"token": "tok42"
|
|
}'''
|
|
|
|
ERRORS = [
|
|
({"error": {"message": "crash message"}}, 'crash message'),
|
|
({"error": {"foo": "bar"}}, 'unknown ArcGIS/token error'),
|
|
({"error": "string crash"}, 'unknown ArcGIS/token error'),
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def arcgis():
|
|
return ArcGIS.objects.create(slug='test', base_url='https://arcgis.example.net/')
|
|
|
|
|
|
def test_arcgis_mapservice_query(app, arcgis):
|
|
endpoint = tests.utils.generic_endpoint_url('arcgis', 'mapservice-query', slug=arcgis.slug)
|
|
assert endpoint == '/arcgis/test/mapservice-query'
|
|
params = {'folder': 'fold', 'service': 'serv', 'layer': '1'}
|
|
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content=STATES, status_code=200)
|
|
|
|
resp = app.get(endpoint, params=params, status=403)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'django.core.exceptions.PermissionDenied'
|
|
|
|
# open access
|
|
api = ApiUser.objects.create(username='all', keytype='', key='')
|
|
obj_type = ContentType.objects.get_for_model(arcgis)
|
|
AccessRight.objects.create(
|
|
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=arcgis.pk
|
|
)
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['f'] == 'json'
|
|
assert args['outFields'] == '*'
|
|
assert args['where'] == '1=1'
|
|
assert 'data' in resp.json
|
|
assert resp.json['err'] == 0
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == '40'
|
|
assert resp.json['data'][0]['text'] == 'Texas'
|
|
assert 'geometry' not in resp.json['data'][0]
|
|
assert 'metadata' not in resp.json
|
|
|
|
params['full'] = 'on'
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 2
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['f'] == 'json'
|
|
assert args['outFields'] == '*'
|
|
assert args['where'] == '1=1'
|
|
assert 'data' in resp.json
|
|
assert resp.json['err'] == 0
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == '40'
|
|
assert resp.json['data'][0]['text'] == 'Texas'
|
|
assert resp.json['data'][0]['geometry']
|
|
assert resp.json['metadata']
|
|
|
|
params['q'] = 'Texas'
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 3
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['text'] == 'Texas'
|
|
assert 'where' not in args
|
|
|
|
params['lat'] = '9.87654'
|
|
params['lon'] = '1.12345'
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 4
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['geometry'] == '1.12345,9.87654'
|
|
assert args['geometryType'] == 'esriGeometryPoint'
|
|
|
|
del params['lat'] # missing lat, do not search by geometry
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 5
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert 'geometry' not in args
|
|
assert 'geometryType' not in args
|
|
|
|
params.update({'latmin': '1', 'lonmin': '2', 'latmax': '3', 'lonmax': '4'})
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 6
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['geometry'] == '2.0,1.0,4.0,3.0'
|
|
assert args['geometryType'] == 'esriGeometryEnvelope'
|
|
|
|
del params['latmin'] # incomplete box, do not search by geometry
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 7
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert 'geometry' not in args
|
|
assert 'geometryType' not in args
|
|
|
|
# others params are directly sent to ArcGIS
|
|
params['spatialRel'] = 'esriSpatialRelContains'
|
|
params.update({'latmin': '1', 'lonmin': '2', 'latmax': '3', 'lonmax': '4'})
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 8
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['geometry'] == '2.0,1.0,4.0,3.0'
|
|
assert args['geometryType'] == 'esriGeometryEnvelope'
|
|
assert args['spatialRel'] == 'esriSpatialRelContains'
|
|
|
|
# folder
|
|
params['folder'] = 'foo/bar'
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/foo/bar/serv/MapServer/1/query'
|
|
)
|
|
del params['folder']
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_args[0][0] == 'https://arcgis.example.net/services/serv/MapServer/1/query'
|
|
|
|
# minimal call
|
|
resp = app.get(endpoint, params={'service': 'srv'}, status=200)
|
|
assert requests_get.call_args[0][0] == 'https://arcgis.example.net/services/srv/MapServer/0/query'
|
|
args = requests_get.call_args[1]['params']
|
|
assert args == {'f': 'json', 'inSR': '4326', 'outSR': '4326', 'outFields': '*', 'where': '1=1'}
|
|
|
|
# distance
|
|
resp = app.get(endpoint, params={'service': 'srv', 'distance': '100'}, status=200)
|
|
assert requests_get.call_args[0][0] == 'https://arcgis.example.net/services/srv/MapServer/0/query'
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['distance'] == '100'
|
|
assert args['units'] == 'esriSRUnit_Meter' # default unit
|
|
resp = app.get(
|
|
endpoint,
|
|
params={'service': 'srv', 'distance': '5', 'units': 'esriSRUnit_NauticalMile'},
|
|
status=200,
|
|
)
|
|
assert requests_get.call_args[0][0] == 'https://arcgis.example.net/services/srv/MapServer/0/query'
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['distance'] == '5'
|
|
assert args['units'] == 'esriSRUnit_NauticalMile'
|
|
|
|
# call errors
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content=STATES, status_code=200)
|
|
resp = app.get(endpoint, params={}, status=400)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'passerelle.views.WrongParameter'
|
|
assert resp.json['err_desc'] == "missing parameters: 'service'."
|
|
|
|
resp = app.get(endpoint, params={'service': 'src', 'lat': '0', 'lon': 'y'}, status=400)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
|
assert resp.json['err_desc'] == '<lon> and <lat> must be floats'
|
|
|
|
resp = app.get(
|
|
endpoint,
|
|
params={'service': 'src', 'latmin': '0', 'lonmin': 'y', 'latmax': '0', 'lonmax': '1'},
|
|
status=400,
|
|
)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
|
assert resp.json['err_desc'] == '<lonmin> <latmin> <lonmax> and <latmax> must be floats'
|
|
|
|
|
|
def test_arcgis_featureservice_query(app, arcgis):
|
|
endpoint = tests.utils.generic_endpoint_url('arcgis', 'featureservice-query', slug=arcgis.slug)
|
|
assert endpoint == '/arcgis/test/featureservice-query'
|
|
params = {'folder': 'fold', 'service': 'serv', 'layer': '42'}
|
|
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content=FEATURES, status_code=200)
|
|
|
|
resp = app.get(endpoint, params=params, status=403)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'django.core.exceptions.PermissionDenied'
|
|
|
|
# open access
|
|
api = ApiUser.objects.create(username='all', keytype='', key='')
|
|
obj_type = ContentType.objects.get_for_model(arcgis)
|
|
AccessRight.objects.create(
|
|
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=arcgis.pk
|
|
)
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['f'] == 'json'
|
|
assert args['outFields'] == '*'
|
|
assert 'data' in resp.json
|
|
assert resp.json['err'] == 0
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == '11'
|
|
assert resp.json['data'][0]['text'] == '11'
|
|
assert 'geometry' not in resp.json['data'][0]
|
|
assert 'metadata' not in resp.json
|
|
|
|
params['full'] = 'on'
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 2
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['f'] == 'json'
|
|
assert args['outFields'] == '*'
|
|
assert 'data' in resp.json
|
|
assert resp.json['err'] == 0
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == '11'
|
|
assert resp.json['data'][0]['text'] == '11'
|
|
assert resp.json['data'][0]['geometry']
|
|
assert resp.json['metadata']
|
|
|
|
params['lat'] = '9.87654'
|
|
params['lon'] = '1.12345'
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 3
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['geometry'] == '1.12345,9.87654'
|
|
assert args['geometryType'] == 'esriGeometryPoint'
|
|
|
|
del params['lat'] # missing lat, do not search by geometry
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 4
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert 'geometry' not in args
|
|
assert 'geometryType' not in args
|
|
|
|
params.update({'latmin': '1', 'lonmin': '2', 'latmax': '3', 'lonmax': '4'})
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 5
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['geometry'] == '2.0,1.0,4.0,3.0'
|
|
assert args['geometryType'] == 'esriGeometryEnvelope'
|
|
|
|
del params['latmin'] # incomplete box, do not search by geometry
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 6
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert 'geometry' not in args
|
|
assert 'geometryType' not in args
|
|
|
|
# others params are directly sent to ArcGIS
|
|
params['spatialRel'] = 'esriSpatialRelContains'
|
|
params.update({'latmin': '1', 'lonmin': '2', 'latmax': '3', 'lonmax': '4'})
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 7
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['geometry'] == '2.0,1.0,4.0,3.0'
|
|
assert args['geometryType'] == 'esriGeometryEnvelope'
|
|
assert args['spatialRel'] == 'esriSpatialRelContains'
|
|
|
|
# folder
|
|
params['folder'] = 'foo/bar'
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/foo/bar/serv/FeatureServer/42/query'
|
|
)
|
|
del params['folder']
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/serv/FeatureServer/42/query'
|
|
)
|
|
|
|
# minimal call
|
|
resp = app.get(endpoint, params={'service': 'srv'}, status=200)
|
|
assert requests_get.call_args[0][0] == 'https://arcgis.example.net/services/srv/FeatureServer/0/query'
|
|
args = requests_get.call_args[1]['params']
|
|
assert args == {'f': 'json', 'inSR': '4326', 'outSR': '4326', 'outFields': '*'}
|
|
|
|
# distance
|
|
resp = app.get(endpoint, params={'service': 'srv', 'distance': '100'}, status=200)
|
|
assert requests_get.call_args[0][0] == 'https://arcgis.example.net/services/srv/FeatureServer/0/query'
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['distance'] == '100'
|
|
assert args['units'] == 'esriSRUnit_Meter' # default unit
|
|
resp = app.get(
|
|
endpoint,
|
|
params={'service': 'srv', 'distance': '5', 'units': 'esriSRUnit_NauticalMile'},
|
|
status=200,
|
|
)
|
|
assert requests_get.call_args[0][0] == 'https://arcgis.example.net/services/srv/FeatureServer/0/query'
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['distance'] == '5'
|
|
assert args['units'] == 'esriSRUnit_NauticalMile'
|
|
|
|
# text_fieldname
|
|
for text_fieldname in ('nom', 'quartier'): # 'nom': real name, 'quartier': its alias
|
|
resp = app.get(
|
|
endpoint,
|
|
params={
|
|
'service': 'srv',
|
|
'text_fieldname': text_fieldname,
|
|
},
|
|
status=200,
|
|
)
|
|
assert 'data' in resp.json
|
|
assert resp.json['err'] == 0
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == '11'
|
|
assert resp.json['data'][0]['text'] == 'Le Nom'
|
|
assert resp.json['data'][1]['id'] == '10'
|
|
assert resp.json['data'][1]['text'] == 'Autre Nom'
|
|
|
|
# templates
|
|
resp = app.get(
|
|
endpoint,
|
|
params={
|
|
'service': 'srv',
|
|
'template': '{{ attributes.nom }}',
|
|
'id_template': '{{ attributes.id }}',
|
|
},
|
|
status=200,
|
|
)
|
|
assert 'data' in resp.json
|
|
assert resp.json['err'] == 0
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == 'onze'
|
|
assert resp.json['data'][0]['text'] == 'Le Nom'
|
|
assert resp.json['data'][1]['id'] == 'dix'
|
|
assert resp.json['data'][1]['text'] == 'Autre Nom'
|
|
|
|
# call errors
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content=FEATURES, status_code=200)
|
|
resp = app.get(endpoint, params={}, status=400)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'passerelle.views.WrongParameter'
|
|
assert resp.json['err_desc'] == "missing parameters: 'service'."
|
|
|
|
resp = app.get(endpoint, params={'service': 'src', 'lat': '0', 'lon': 'y'}, status=400)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
|
assert resp.json['err_desc'] == '<lon> and <lat> must be floats'
|
|
|
|
resp = app.get(
|
|
endpoint,
|
|
params={'service': 'src', 'latmin': '0', 'lonmin': 'y', 'latmax': '0', 'lonmax': '1'},
|
|
status=400,
|
|
)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
|
|
assert resp.json['err_desc'] == '<lonmin> <latmin> <lonmax> and <latmax> must be floats'
|
|
|
|
|
|
def test_arcgis_with_token_query(app, arcgis):
|
|
endpoint = tests.utils.generic_endpoint_url('arcgis', 'featureservice-query', slug=arcgis.slug)
|
|
assert endpoint == '/arcgis/test/featureservice-query'
|
|
# open access
|
|
api = ApiUser.objects.create(username='all', keytype='', key='')
|
|
obj_type = ContentType.objects.get_for_model(arcgis)
|
|
AccessRight.objects.create(
|
|
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=arcgis.pk
|
|
)
|
|
|
|
arcgis.token_username = 'tokenuser'
|
|
arcgis.token_password = 'tokenpass'
|
|
arcgis.save()
|
|
params = {'folder': 'fold', 'service': 'serv', 'layer': '42'}
|
|
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
with mock.patch('passerelle.utils.Request.post') as requests_post:
|
|
requests_get.side_effect = [
|
|
tests.utils.FakedResponse(content=INFO, status_code=200),
|
|
tests.utils.FakedResponse(content=FEATURES, status_code=200),
|
|
]
|
|
requests_post.side_effect = [
|
|
tests.utils.FakedResponse(content=TOKEN, status_code=200),
|
|
]
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 2 # info + featureservice-query
|
|
assert requests_post.call_count == 1 # token
|
|
assert (
|
|
requests_get.call_args[0][0]
|
|
== 'https://arcgis.example.net/services/fold/serv/FeatureServer/42/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args['f'] == 'json'
|
|
assert args['outFields'] == '*'
|
|
assert args['token'] == 'tok42' # token added in query
|
|
assert 'data' in resp.json
|
|
assert resp.json['err'] == 0
|
|
assert len(resp.json['data']) == 2
|
|
assert resp.json['data'][0]['id'] == '11'
|
|
assert resp.json['data'][0]['text'] == '11'
|
|
|
|
# bad info/token responses
|
|
|
|
requests_get.reset_mock()
|
|
requests_post.reset_mock()
|
|
requests_get.side_effect = [
|
|
tests.utils.FakedResponse(content='{}', status_code=200),
|
|
]
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert requests_post.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'].startswith(
|
|
'ArcGIS/token responded no authInfo/tokenServicesUrl in info'
|
|
)
|
|
|
|
requests_get.reset_mock()
|
|
requests_get.side_effect = [
|
|
tests.utils.FakedResponse(content='CRASH', status_code=500),
|
|
]
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert requests_post.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'ArcGIS/token returned status code 500'
|
|
|
|
requests_get.reset_mock()
|
|
requests_get.side_effect = [
|
|
tests.utils.FakedResponse(content='not json', status_code=200),
|
|
]
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert requests_post.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'].startswith('ArcGIS/token returned invalid JSON content:')
|
|
|
|
requests_get.reset_mock()
|
|
requests_get.side_effect = [
|
|
tests.utils.FakedResponse(content='"not a dict"', status_code=200),
|
|
]
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert requests_post.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'].startswith('ArcGIS/token not returned a dict:')
|
|
|
|
for content, err_desc in ERRORS:
|
|
requests_get.reset_mock()
|
|
requests_get.side_effect = [
|
|
tests.utils.FakedResponse(content=json.dumps(content), status_code=200),
|
|
]
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert requests_post.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == err_desc
|
|
|
|
requests_get.reset_mock()
|
|
requests_post.reset_mock()
|
|
requests_get.side_effect = [
|
|
tests.utils.FakedResponse(content=INFO, status_code=200),
|
|
]
|
|
requests_post.side_effect = [
|
|
tests.utils.FakedResponse(content="{}", status_code=200), # no token
|
|
]
|
|
resp = app.get(endpoint, params=params, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert requests_post.call_count == 1
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'].startswith('ArcGIS/token returned no token:')
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'format_string,fail',
|
|
[
|
|
('x {é}', True),
|
|
('x {aa.bb}', True),
|
|
('x {a:s} {b:d}', False),
|
|
],
|
|
)
|
|
def test_validate_where(format_string, fail):
|
|
if fail:
|
|
with pytest.raises(ValidationError):
|
|
validate_where(format_string)
|
|
else:
|
|
validate_where(format_string)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'format_string,kwargs,expected',
|
|
[
|
|
('adresse LIKE {adresse:s}', {'adresse': "AVENUE D'ANNAM"}, "adresse LIKE 'AVENUE D''ANNAM'"),
|
|
(
|
|
'adresse LIKE {adresse:s} AND population < {pop:d}',
|
|
{
|
|
'adresse': "AVENUE D'ANNAM",
|
|
'pop': '34',
|
|
},
|
|
"adresse LIKE 'AVENUE D''ANNAM' AND population < 34",
|
|
),
|
|
(
|
|
'adresse LIKE {adresse:s} AND population < {pop:d}',
|
|
{
|
|
'adresse': "AVENUE D'ANNAM",
|
|
'pop': 'x',
|
|
},
|
|
ValueError,
|
|
),
|
|
(
|
|
'ID IN {ids:l}',
|
|
{
|
|
'ids': '1,3,5',
|
|
},
|
|
"ID IN ('1', '3', '5')",
|
|
),
|
|
],
|
|
)
|
|
def test_sql_formatter(format_string, kwargs, expected):
|
|
formatter = SqlFormatter()
|
|
if not isinstance(expected, type) or not issubclass(expected, Exception):
|
|
assert formatter.format(format_string, **kwargs) == expected
|
|
else:
|
|
with pytest.raises(expected):
|
|
formatter.format(format_string, **kwargs)
|
|
|
|
|
|
@pytest.fixture
|
|
def query(arcgis):
|
|
return Query.objects.create(
|
|
resource=arcgis,
|
|
name='Adresses',
|
|
slug='adresses',
|
|
description='Rechercher une adresse',
|
|
id_template='{{ attributes.ident }}',
|
|
text_template='{{ attributes.address }} - {{ attributes.codepost }}',
|
|
folder='fold',
|
|
layer='1',
|
|
service='serv',
|
|
where='adress LIKE {adress:s}',
|
|
)
|
|
|
|
|
|
def test_query_q_method(arcgis, query, rf):
|
|
arcgis_response = {
|
|
"features": [
|
|
{
|
|
"attributes": {
|
|
"ident": "1234",
|
|
"address": "rue du calvaire",
|
|
"codepost": 13200,
|
|
},
|
|
'geo': {},
|
|
},
|
|
],
|
|
'meta': {},
|
|
}
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(
|
|
content=json.dumps(arcgis_response), status_code=200
|
|
)
|
|
assert query.q(rf.get('/', data={'adress': "AVENUE D'ANNAM"}), full=True) == {
|
|
"data": [
|
|
{
|
|
"attributes": {"ident": "1234", "address": "rue du calvaire", "codepost": 13200},
|
|
"geo": {},
|
|
"id": "1234",
|
|
"text": "rue du calvaire - 13200",
|
|
}
|
|
],
|
|
"metadata": {"meta": {}},
|
|
}
|
|
assert requests_get.call_count == 1
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args == {
|
|
'f': 'json',
|
|
'inSR': '4326',
|
|
'outSR': '4326',
|
|
'outFields': '*',
|
|
'where': "adress LIKE 'AVENUE D''ANNAM'",
|
|
}
|
|
|
|
|
|
def test_q_endpoint(arcgis, query, app):
|
|
endpoint = tests.utils.generic_endpoint_url('arcgis', 'q/adresses/', slug=arcgis.slug)
|
|
assert endpoint == '/arcgis/test/q/adresses/'
|
|
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content=STATES, status_code=200)
|
|
|
|
resp = app.get(endpoint, params={}, status=403)
|
|
assert requests_get.call_count == 0
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'django.core.exceptions.PermissionDenied'
|
|
|
|
# open access
|
|
api = ApiUser.objects.create(username='all', keytype='', key='')
|
|
obj_type = ContentType.objects.get_for_model(arcgis)
|
|
AccessRight.objects.create(
|
|
codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=arcgis.pk
|
|
)
|
|
resp = app.get(endpoint, params={'adress': "AVENUE D'ANNAM"}, status=200)
|
|
assert requests_get.call_count == 1
|
|
assert (
|
|
requests_get.call_args[0][0] == 'https://arcgis.example.net/services/fold/serv/MapServer/1/query'
|
|
)
|
|
args = requests_get.call_args[1]['params']
|
|
assert args == {
|
|
'f': 'json',
|
|
'inSR': '4326',
|
|
'outSR': '4326',
|
|
'outFields': '*',
|
|
'where': "adress LIKE 'AVENUE D''ANNAM'",
|
|
}
|
|
|
|
|
|
def test_tile_endpoint(arcgis, app):
|
|
assert arcgis.base_url == 'https://arcgis.example.net/'
|
|
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content='', status_code=200)
|
|
|
|
resp = app.get('/arcgis/test/tile/layer1/13/4258/2991.png')
|
|
assert requests_get.call_count == 1
|
|
assert requests_get.call_args[0][0] == (
|
|
'https://arcgis.example.net/layer1/MapServer/export'
|
|
'?dpi=96&format=png24&bboxSR=4326&imageSR=3857&transparent=true&size=256,256&f=image'
|
|
'&bbox=7.119141,43.612217,7.163086,43.580391'
|
|
)
|
|
assert resp.content_type == 'image/png'
|
|
|
|
# test layer and folders
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content='', status_code=200)
|
|
|
|
resp = app.get('/arcgis/test/tile/layer1/foo/bar/13/4258/2991.png')
|
|
assert requests_get.call_count == 1
|
|
assert requests_get.call_args[0][0] == (
|
|
'https://arcgis.example.net/layer1/foo/bar/MapServer/export'
|
|
'?dpi=96&format=png24&bboxSR=4326&imageSR=3857&transparent=true&size=256,256&f=image'
|
|
'&bbox=7.119141,43.612217,7.163086,43.580391'
|
|
)
|
|
assert resp.content_type == 'image/png'
|
|
|
|
# test missing trailing slash
|
|
arcgis.base_url = 'https://arcgis.example.net'
|
|
arcgis.save()
|
|
with mock.patch('passerelle.utils.Request.get') as requests_get:
|
|
requests_get.return_value = tests.utils.FakedResponse(content='', status_code=200)
|
|
|
|
resp = app.get('/arcgis/test/tile/layer1/13/4258/2991.png')
|
|
assert requests_get.call_count == 1
|
|
assert requests_get.call_args[0][0] == (
|
|
'https://arcgis.example.net/layer1/MapServer/export'
|
|
'?dpi=96&format=png24&bboxSR=4326&imageSR=3857&transparent=true&size=256,256&f=image'
|
|
'&bbox=7.119141,43.612217,7.163086,43.580391'
|
|
)
|
|
assert resp.content_type == 'image/png'
|
|
|
|
|
|
def test_query_documentation(arcgis, query, app):
|
|
resp = app.get(arcgis.get_absolute_url())
|
|
assert query.name in resp.text
|
|
assert query.description in resp.text
|
|
# additional parameter appears in endpoint documentation
|
|
assert '<span class="param-name">adress</span>' in resp.text
|
|
|
|
|
|
def test_arcgis_query_unicity(admin_user, app, arcgis):
|
|
query = Query.objects.create(
|
|
resource=arcgis,
|
|
name='Test Query',
|
|
slug='test-query',
|
|
)
|
|
|
|
arcgis2 = ArcGIS.objects.create(slug='test2', base_url='https://arcgis.example.net/')
|
|
Query.objects.create(
|
|
resource=arcgis2,
|
|
name='Foo Bar',
|
|
slug='foo-bar',
|
|
)
|
|
|
|
app = login(app)
|
|
resp = app.get('/manage/arcgis/%s/query/new/' % arcgis.slug)
|
|
resp.form['slug'] = query.slug
|
|
resp.form['name'] = 'Foo Bar'
|
|
resp.form['service'] = 'Foo'
|
|
resp = resp.form.submit()
|
|
assert resp.status_code == 200
|
|
assert Query.objects.filter(resource=arcgis).count() == 1
|
|
assert 'A query with this slug already exists' in resp.text
|
|
resp.form['slug'] = 'foo-bar'
|
|
resp.form['name'] = query.name
|
|
resp.form['service'] = 'Foo'
|
|
resp = resp.form.submit()
|
|
assert Query.objects.filter(resource=arcgis).count() == 1
|
|
assert resp.status_code == 200
|
|
assert 'A query with this name already exists' in resp.text
|
|
resp.form['slug'] = 'foo-bar'
|
|
resp.form['name'] = 'Foo Bar'
|
|
resp.form['service'] = 'Foo'
|
|
resp = resp.form.submit()
|
|
assert resp.status_code == 302
|
|
assert Query.objects.filter(resource=arcgis).count() == 2
|
|
new_query = Query.objects.latest('pk')
|
|
assert new_query.resource == arcgis
|
|
|
|
resp = app.get('/manage/arcgis/%s/query/%s/' % (arcgis.slug, new_query.pk))
|
|
resp.form['slug'] = query.slug
|
|
resp.form['name'] = 'Foo Bar'
|
|
resp = resp.form.submit()
|
|
assert resp.status_code == 200
|
|
assert 'A query with this slug already exists' in resp.text
|
|
resp.form['slug'] = 'foo-bar'
|
|
resp.form['name'] = query.name
|
|
resp = resp.form.submit()
|
|
assert resp.status_code == 200
|
|
assert 'A query with this name already exists' in resp.text
|
|
resp.form['slug'] = 'foo-bar'
|
|
resp.form['name'] = 'Foo Bar'
|
|
resp = resp.form.submit()
|
|
assert resp.status_code == 302
|
|
|
|
|
|
def test_export_import(query):
|
|
assert ArcGIS.objects.count() == 1
|
|
assert Query.objects.count() == 1
|
|
serialization = {'resources': [query.resource.export_json()]}
|
|
ArcGIS.objects.all().delete()
|
|
assert ArcGIS.objects.count() == 0
|
|
assert Query.objects.count() == 0
|
|
import_site(serialization)
|
|
assert ArcGIS.objects.count() == 1
|
|
assert Query.objects.count() == 1
|