passerelle/tests/test_arcgis.py

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