wcs/tests/api/test_all.py

260 lines
10 KiB
Python

import io
import json
import os
from unittest import mock
import pytest
from quixote import get_publisher
from wcs.api_utils import sign_url
from wcs.formdef import FormDef
from wcs.qommon.afterjobs import AfterJob
from wcs.qommon.http_request import HTTPRequest
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app
@pytest.fixture
def pub(emails):
pub = create_temporary_pub()
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
pub.set_app_dir(req)
pub.cfg['identification'] = {'methods': ['password']}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
fd.write(
'''\
[api-secrets]
coucou = 1234
'''
)
return pub
def teardown_module(module):
clean_temporary_pub()
def test_tracking_code(pub):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = []
formdef.enable_tracking_codes = True
formdef.store()
data_class = formdef.data_class()
formdata = data_class()
formdata.store()
code = get_publisher().tracking_code_class()
code.formdata = formdata
code.store()
# missing signature
get_app(pub).get('/api/code/foobar', status=403)
resp = get_app(pub).get(sign_url('/api/code/foobar?orig=coucou', '1234'), status=404)
assert resp.json['err'] == 1
resp = get_app(pub).get(sign_url('/api/code/%s?orig=coucou' % code.id, '1234'), status=200)
assert resp.json['err'] == 0
assert resp.json['url'] == 'http://example.net/test/%s/' % formdata.id
assert resp.json['load_url'] == 'http://example.net/code/%s/load' % code.id
formdef.enable_tracking_codes = False
formdef.store()
resp = get_app(pub).get(sign_url('/api/code/%s?orig=coucou' % code.id, '1234'), status=404)
formdef.enable_tracking_codes = True
formdef.store()
formdata.remove_self()
resp = get_app(pub).get(sign_url('/api/code/%s?orig=coucou' % code.id, '1234'), status=404)
def test_validate_expression(pub):
resp = get_app(pub).get('/api/validate-expression?expression=hello')
assert resp.json == {'klass': None, 'msg': ''}
resp = get_app(pub).get('/api/validate-expression?expression=[hello]')
assert resp.json == {'klass': None, 'msg': ''}
resp = get_app(pub).get('/api/validate-expression?expression==[hello')
assert resp.json['klass'] == 'error'
assert resp.json['msg'].startswith('syntax error')
resp = get_app(pub).get('/api/validate-expression?expression==[hello]')
assert resp.json['klass'] == 'warning'
assert resp.json['msg'].startswith('Make sure you want a Python expression,')
resp = get_app(pub).get('/api/validate-expression?expression==hello[0]')
assert resp.json == {'klass': None, 'msg': ''}
resp = get_app(pub).get('/api/validate-expression?expression==hello[\'plop\']')
assert resp.json == {'klass': None, 'msg': ''}
# django with unicode
resp = get_app(pub).get('/api/validate-expression?expression={{hello+%C3%A9l%C3%A9phant}}')
assert resp.json['klass'] == 'error'
assert resp.json['msg'].startswith('syntax error in Django template: Could not parse the remainder')
# broken ezt
resp = get_app(pub).get('/api/validate-expression?expression=[for]')
assert resp.json == {
'klass': 'error',
'msg': 'syntax error in ezt template: wrong number of arguments at line 1 and column 1',
}
def test_validate_condition(pub):
resp = get_app(pub).get('/api/validate-condition?type=python&value_python=hello')
assert resp.json == {'klass': None, 'msg': ''}
resp = get_app(pub).get('/api/validate-condition?type=python&value_python=~2')
assert resp.json == {'klass': None, 'msg': ''}
resp = get_app(pub).get('/api/validate-condition?type=python&value_python=hello -')
assert resp.json['klass'] == 'error'
assert resp.json['msg'].startswith('syntax error')
resp = get_app(pub).get('/api/validate-condition?type=python&value_python={{form_number}}==3')
assert resp.json['klass'] == 'error'
assert 'Python condition cannot contain {{' in resp.json['msg']
resp = get_app(pub).get('/api/validate-condition?type=django&value_django=un+%C3%A9l%C3%A9phant')
assert resp.json['klass'] == 'error'
assert resp.json['msg'].startswith("syntax error: Unused 'éléphant'")
resp = get_app(pub).get('/api/validate-condition?type=django&value_django=~2')
assert resp.json['klass'] == 'error'
assert resp.json['msg'].startswith('syntax error')
resp = get_app(pub).get('/api/validate-condition?type=django&value_django=%22...%22+inf') # "..." + inf
assert resp.json['klass'] == 'error'
assert resp.json['msg'].startswith('syntax error')
resp = get_app(pub).get('/api/validate-condition?type=unknown&value_unknown=2')
assert resp.json['klass'] == 'error'
assert resp.json['msg'] == 'unknown condition type'
def test_reverse_geocoding(pub):
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
urlopen.side_effect = lambda *args: io.StringIO(json.dumps({'address': 'xxx'}))
get_app(pub).get('/api/reverse-geocoding', status=400)
resp = get_app(pub).get('/api/reverse-geocoding?lat=0&lon=0')
assert resp.content_type == 'application/json'
assert resp.text == json.dumps({'address': 'xxx'})
assert (
urlopen.call_args[0][0]
== 'https://nominatim.entrouvert.org/reverse?zoom=18&format=json&addressdetails=1&lat=0&lon=0&accept-language=en'
)
pub.site_options.add_section('options')
pub.site_options.set('options', 'nominatim_reverse_zoom_level', '16')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/api/reverse-geocoding?lat=0&lon=0')
assert (
urlopen.call_args[0][0]
== 'https://nominatim.entrouvert.org/reverse?zoom=16&format=json&addressdetails=1&lat=0&lon=0&accept-language=en'
)
pub.site_options.set('options', 'nominatim_key', 'KEY')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/api/reverse-geocoding?lat=0&lon=0')
assert (
urlopen.call_args[0][0]
== 'https://nominatim.entrouvert.org/reverse?zoom=16&key=KEY&format=json&addressdetails=1&lat=0&lon=0&accept-language=en'
)
pub.site_options.set(
'options', 'reverse_geocoding_service_url', 'http://reverse.example.net/?param=value'
)
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/api/reverse-geocoding?lat=0&lon=0')
assert (
urlopen.call_args[0][0]
== 'http://reverse.example.net/?param=value&format=json&addressdetails=1&lat=0&lon=0&accept-language=en'
)
def test_geocoding(pub):
with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
urlopen.side_effect = lambda *args: io.StringIO(json.dumps([{'lat': 0, 'lon': 0}]))
get_app(pub).get('/api/geocoding', status=400)
resp = get_app(pub).get('/api/geocoding?q=test')
assert resp.content_type == 'application/json'
assert resp.text == json.dumps([{'lat': 0, 'lon': 0}])
assert (
urlopen.call_args[0][0]
== 'https://nominatim.entrouvert.org/search?format=json&q=test&accept-language=en'
)
pub.site_options.add_section('options')
pub.site_options.set('options', 'map-bounds-top-left', '1.23;2.34')
pub.site_options.set('options', 'map-bounds-bottom-right', '2.34;3.45')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/api/geocoding?q=test')
assert urlopen.call_args[0][0] == (
'https://nominatim.entrouvert.org/search?viewbox=2.34,1.23,3.45,2.34&bounded=1&'
'format=json&q=test&accept-language=en'
)
pub.site_options.set('options', 'nominatim_key', 'KEY')
pub.site_options.set('options', 'map-bounds-top-left', '')
pub.site_options.set('options', 'map-bounds-bottom-right', '')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/api/geocoding?q=test')
assert (
urlopen.call_args[0][0]
== 'https://nominatim.entrouvert.org/search?key=KEY&format=json&q=test&accept-language=en'
)
pub.site_options.set('options', 'map-bounds-top-left', '1.23;2.34')
pub.site_options.set('options', 'map-bounds-bottom-right', '2.34;3.45')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/api/geocoding?q=test')
assert urlopen.call_args[0][0] == (
'https://nominatim.entrouvert.org/search?key=KEY&viewbox=2.34,1.23,3.45,2.34&bounded=1&'
'format=json&q=test&accept-language=en'
)
pub.site_options.set('options', 'geocoding_service_url', 'http://reverse.example.net/?param=value')
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
pub.site_options.write(fd)
resp = get_app(pub).get('/api/geocoding?q=test')
assert (
urlopen.call_args[0][0]
== 'http://reverse.example.net/?param=value&format=json&q=test&accept-language=en'
)
def test_afterjobs_status(pub):
job = AfterJob('test')
job.store()
# missing signature
get_app(pub).get('/api/jobs/%s/' % job.id, status=403)
# unknown id
resp = get_app(pub).get(sign_url('/api/jobs/not-a-job-id/?orig=coucou', '1234'), status=404)
assert resp.json['err'] == 1
# without trailing /
resp = get_app(pub).get(sign_url('/api/jobs/%s?orig=coucou' % job.id, '1234'), status=404)
resp = get_app(pub).get(sign_url('/api/jobs/%s/?orig=coucou' % job.id, '1234'), status=200)
assert resp.json == {
'err': 0,
'data': {
'label': 'test',
'status': 'registered',
'creation_time': job.creation_time,
'completion_time': None,
'completion_status': '',
},
}
def test_afterjobs_base_directory(pub):
# missing signature
get_app(pub).get('/api/jobs/', status=403)
# base directory is 404
get_app(pub).get(sign_url('/api/jobs/?orig=coucou', '1234'), status=404)