Files
deb-python-falcon/tests/test_query_params.py
Kurt Griffiths 36b4e7ffe6 fix: Make invalid, missing param error messages more generic
These errors may refer to form params, not just query string params. Adjust
the response message to the client to make it more generic, in order to
avoid confusion.

Also adjust docstrings for HTTPInvalidXXX and HTTPMissingXXX to be more
informative and consistent.

See issue #549
2015-05-15 12:43:30 -05:00

484 lines
18 KiB
Python

from datetime import date
import ddt
import falcon
import falcon.testing as testing
from falcon.errors import HTTPInvalidParam
@ddt.ddt
class _TestQueryParams(testing.TestBase):
def before(self):
self.resource = testing.TestResource()
self.api.add_route('/', self.resource)
def test_none(self):
query_string = ''
self.simulate_request('/', query_string=query_string)
req = self.resource.req
store = {}
self.assertIs(req.get_param('marker'), None)
self.assertIs(req.get_param('limit', store), None)
self.assertNotIn('limit', store)
self.assertIs(req.get_param_as_int('limit'), None)
self.assertIs(req.get_param_as_bool('limit'), None)
self.assertIs(req.get_param_as_list('limit'), None)
def test_blank(self):
query_string = 'marker='
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertIs(req.get_param('marker'), None)
store = {}
self.assertIs(req.get_param('marker', store=store), None)
self.assertNotIn('marker', store)
def test_simple(self):
query_string = 'marker=deadbeef&limit=25'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
store = {}
self.assertEqual(req.get_param('marker', store=store) or 'nada',
'deadbeef')
self.assertEqual(req.get_param('limit', store=store) or '0', '25')
self.assertEqual(store['marker'], 'deadbeef')
self.assertEqual(store['limit'], '25')
def test_percent_encoded(self):
query_string = 'id=23,42&q=%e8%b1%86+%e7%93%a3'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
# NOTE(kgriffs): For lists, get_param will return one of the
# elements, but which one it will choose is undefined.
self.assertIn(req.get_param('id'), [u'23', u'42'])
self.assertEqual(req.get_param_as_list('id', int), [23, 42])
self.assertEqual(req.get_param('q'), u'\u8c46 \u74e3')
def test_allowed_names(self):
query_string = ('p=0&p1=23&2p=foo&some-thing=that&blank=&'
'some_thing=x&-bogus=foo&more.things=blah&'
'_thing=42&_charset_=utf-8')
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertEqual(req.get_param('p'), '0')
self.assertEqual(req.get_param('p1'), '23')
self.assertEqual(req.get_param('2p'), 'foo')
self.assertEqual(req.get_param('some-thing'), 'that')
self.assertIs(req.get_param('blank'), None)
self.assertEqual(req.get_param('some_thing'), 'x')
self.assertEqual(req.get_param('-bogus'), 'foo')
self.assertEqual(req.get_param('more.things'), 'blah')
self.assertEqual(req.get_param('_thing'), '42')
self.assertEqual(req.get_param('_charset_'), 'utf-8')
@ddt.data('get_param', 'get_param_as_int', 'get_param_as_bool',
'get_param_as_list')
def test_required(self, method_name):
query_string = ''
self.simulate_request('/', query_string=query_string)
req = self.resource.req
try:
getattr(req, method_name)('marker', required=True)
self.fail('falcon.HTTPMissingParam not raised')
except falcon.HTTPMissingParam as ex:
self.assertIsInstance(ex, falcon.HTTPBadRequest)
self.assertEqual(ex.title, 'Missing parameter')
expected_desc = 'The "marker" parameter is required.'
self.assertEqual(ex.description, expected_desc)
def test_int(self):
query_string = 'marker=deadbeef&limit=25'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
try:
req.get_param_as_int('marker')
except Exception as ex:
self.assertIsInstance(ex, falcon.HTTPBadRequest)
self.assertIsInstance(ex, falcon.HTTPInvalidParam)
self.assertEqual(ex.title, 'Invalid parameter')
expected_desc = ('The "marker" parameter is invalid. '
'The value must be an integer.')
self.assertEqual(ex.description, expected_desc)
self.assertEqual(req.get_param_as_int('limit'), 25)
store = {}
self.assertEqual(req.get_param_as_int('limit', store=store), 25)
self.assertEqual(store['limit'], 25)
self.assertEqual(
req.get_param_as_int('limit', min=1, max=50), 25)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'limit', min=0, max=10)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'limit', min=0, max=24)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'limit', min=30, max=24)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'limit', min=30, max=50)
self.assertEqual(
req.get_param_as_int('limit', min=1), 25)
self.assertEqual(
req.get_param_as_int('limit', max=50), 25)
self.assertEqual(
req.get_param_as_int('limit', max=25), 25)
self.assertEqual(
req.get_param_as_int('limit', max=26), 25)
self.assertEqual(
req.get_param_as_int('limit', min=25), 25)
self.assertEqual(
req.get_param_as_int('limit', min=24), 25)
self.assertEqual(
req.get_param_as_int('limit', min=-24), 25)
def test_int_neg(self):
query_string = 'marker=deadbeef&pos=-7'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertEqual(req.get_param_as_int('pos'), -7)
self.assertEqual(
req.get_param_as_int('pos', min=-10, max=10), -7)
self.assertEqual(
req.get_param_as_int('pos', max=10), -7)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'pos', min=-6, max=0)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'pos', min=-6)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'pos', min=0, max=10)
self.assertRaises(
falcon.HTTPBadRequest,
req.get_param_as_int, 'pos', min=0, max=10)
def test_boolean(self):
query_string = ('echo=true&doit=false&bogus=0&bogus2=1&'
't1=True&f1=False&t2=yes&f2=no&blank')
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertRaises(falcon.HTTPBadRequest, req.get_param_as_bool,
'bogus')
try:
req.get_param_as_bool('bogus2')
except Exception as ex:
self.assertIsInstance(ex, falcon.HTTPInvalidParam)
self.assertEqual(ex.title, 'Invalid parameter')
expected_desc = ('The "bogus2" parameter is invalid. '
'The value of the parameter must be "true" '
'or "false".')
self.assertEqual(ex.description, expected_desc)
self.assertEqual(req.get_param_as_bool('echo'), True)
self.assertEqual(req.get_param_as_bool('doit'), False)
self.assertEqual(req.get_param_as_bool('t1'), True)
self.assertEqual(req.get_param_as_bool('t2'), True)
self.assertEqual(req.get_param_as_bool('f1'), False)
self.assertEqual(req.get_param_as_bool('f2'), False)
self.assertEqual(req.get_param('blank'), None)
store = {}
self.assertEqual(req.get_param_as_bool('echo', store=store), True)
self.assertEqual(store['echo'], True)
def test_boolean_blank(self):
self.api.req_options.keep_blank_qs_values = True
self.simulate_request(
'/',
query_string='blank&blank2=',
)
req = self.resource.req
self.assertEqual(req.get_param('blank'), '')
self.assertEqual(req.get_param('blank2'), '')
self.assertRaises(falcon.HTTPInvalidParam, req.get_param_as_bool,
'blank')
self.assertRaises(falcon.HTTPInvalidParam, req.get_param_as_bool,
'blank2')
self.assertEqual(req.get_param_as_bool('blank', blank_as_true=True),
True)
self.assertEqual(req.get_param_as_bool('blank3', blank_as_true=True),
None)
def test_list_type(self):
query_string = ('colors=red,green,blue&limit=1'
'&list-ish1=f,,x&list-ish2=,0&list-ish3=a,,,b'
'&empty1=&empty2=,&empty3=,,'
'&thing_one=1,,3'
'&thing_two=1&thing_two=&thing_two=3')
self.simulate_request('/', query_string=query_string)
req = self.resource.req
# NOTE(kgriffs): For lists, get_param will return one of the
# elements, but which one it will choose is undefined.
self.assertIn(req.get_param('colors'), ('red', 'green', 'blue'))
self.assertEqual(req.get_param_as_list('colors'),
['red', 'green', 'blue'])
self.assertEqual(req.get_param_as_list('limit'), ['1'])
self.assertIs(req.get_param_as_list('marker'), None)
self.assertEqual(req.get_param_as_list('empty1'), None)
self.assertEqual(req.get_param_as_list('empty2'), [])
self.assertEqual(req.get_param_as_list('empty3'), [])
self.assertEqual(req.get_param_as_list('list-ish1'),
['f', 'x'])
# Ensure that '0' doesn't get translated to None
self.assertEqual(req.get_param_as_list('list-ish2'),
['0'])
# Ensure that '0' doesn't get translated to None
self.assertEqual(req.get_param_as_list('list-ish3'),
['a', 'b'])
# Ensure consistency between list conventions
self.assertEqual(req.get_param_as_list('thing_one'),
['1', '3'])
self.assertEqual(req.get_param_as_list('thing_one'),
req.get_param_as_list('thing_two'))
store = {}
self.assertEqual(req.get_param_as_list('limit', store=store), ['1'])
self.assertEqual(store['limit'], ['1'])
def test_list_type_blank(self):
query_string = ('colors=red,green,blue&limit=1'
'&list-ish1=f,,x&list-ish2=,0&list-ish3=a,,,b'
'&empty1=&empty2=,&empty3=,,'
'&thing_one=1,,3'
'&thing_two=1&thing_two=&thing_two=3'
'&empty4=&empty4&empty4='
'&empty5&empty5&empty5')
self.api.req_options.keep_blank_qs_values = True
self.simulate_request(
'/',
query_string=query_string
)
req = self.resource.req
# NOTE(kgriffs): For lists, get_param will return one of the
# elements, but which one it will choose is undefined.
self.assertIn(req.get_param('colors'), ('red', 'green', 'blue'))
self.assertEqual(req.get_param_as_list('colors'),
['red', 'green', 'blue'])
self.assertEqual(req.get_param_as_list('limit'), ['1'])
self.assertIs(req.get_param_as_list('marker'), None)
self.assertEqual(req.get_param_as_list('empty1'), [''])
self.assertEqual(req.get_param_as_list('empty2'), ['', ''])
self.assertEqual(req.get_param_as_list('empty3'), ['', '', ''])
self.assertEqual(req.get_param_as_list('list-ish1'),
['f', '', 'x'])
# Ensure that '0' doesn't get translated to None
self.assertEqual(req.get_param_as_list('list-ish2'),
['', '0'])
# Ensure that '0' doesn't get translated to None
self.assertEqual(req.get_param_as_list('list-ish3'),
['a', '', '', 'b'])
# Ensure consistency between list conventions
self.assertEqual(req.get_param_as_list('thing_one'),
['1', '', '3'])
self.assertEqual(req.get_param_as_list('thing_one'),
req.get_param_as_list('thing_two'))
store = {}
self.assertEqual(req.get_param_as_list('limit', store=store), ['1'])
self.assertEqual(store['limit'], ['1'])
# Test empty elements
self.assertEqual(req.get_param_as_list('empty4'), ['', '', ''])
self.assertEqual(req.get_param_as_list('empty5'), ['', '', ''])
self.assertEqual(req.get_param_as_list('empty4'),
req.get_param_as_list('empty5'))
def test_list_transformer(self):
query_string = 'coord=1.4,13,15.1&limit=100&things=4,,1'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
# NOTE(kgriffs): For lists, get_param will return one of the
# elements, but which one it will choose is undefined.
self.assertIn(req.get_param('coord'), ('1.4', '13', '15.1'))
expected = [1.4, 13.0, 15.1]
actual = req.get_param_as_list('coord', transform=float)
self.assertEqual(actual, expected)
expected = ['4', '1']
actual = req.get_param_as_list('things', transform=str)
self.assertEqual(actual, expected)
expected = [4, 1]
actual = req.get_param_as_list('things', transform=int)
self.assertEqual(actual, expected)
try:
req.get_param_as_list('coord', transform=int)
except Exception as ex:
self.assertIsInstance(ex, falcon.HTTPInvalidParam)
self.assertEqual(ex.title, 'Invalid parameter')
expected_desc = ('The "coord" parameter is invalid. '
'The value is not formatted correctly.')
self.assertEqual(ex.description, expected_desc)
def test_param_property(self):
query_string = 'ant=4&bee=3&cat=2&dog=1'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertEqual(
sorted(req.params.items()),
[('ant', '4'), ('bee', '3'), ('cat', '2'), ('dog', '1')])
def test_multiple_form_keys(self):
query_string = 'ant=1&ant=2&bee=3&cat=6&cat=5&cat=4'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
# By definition, we cannot guarantee which of the multiple keys will
# be returned by .get_param().
self.assertIn(req.get_param('ant'), ('1', '2'))
# There is only one 'bee' key so it remains a scalar.
self.assertEqual(req.get_param('bee'), '3')
# There are three 'cat' keys; order is preserved.
self.assertIn(req.get_param('cat'), ('6', '5', '4'))
def test_multiple_keys_as_bool(self):
query_string = 'ant=true&ant=yes&ant=True'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertEqual(req.get_param_as_bool('ant'), True)
def test_multiple_keys_as_int(self):
query_string = 'ant=1&ant=2&ant=3'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertIn(req.get_param_as_int('ant'), (1, 2, 3))
def test_multiple_form_keys_as_list(self):
query_string = 'ant=1&ant=2&bee=3&cat=6&cat=5&cat=4'
self.simulate_request('/', query_string=query_string)
req = self.resource.req
# There are two 'ant' keys.
self.assertEqual(req.get_param_as_list('ant'), ['1', '2'])
# There is only one 'bee' key..
self.assertEqual(req.get_param_as_list('bee'), ['3'])
# There are three 'cat' keys; order is preserved.
self.assertEqual(req.get_param_as_list('cat'), ['6', '5', '4'])
def test_get_date_valid(self):
date_value = "2015-04-20"
query_string = "thedate={0}".format(date_value)
self.simulate_request("/", query_string=query_string)
req = self.resource.req
self.assertEqual(req.get_param_as_date("thedate"),
date(2015, 4, 20))
def test_get_date_missing_param(self):
query_string = "notthedate=2015-04-20"
self.simulate_request("/", query_string=query_string)
req = self.resource.req
self.assertEqual(req.get_param_as_date("thedate"),
None)
def test_get_date_valid_with_format(self):
date_value = "20150420"
query_string = "thedate={0}".format(date_value)
format_string = "%Y%m%d"
self.simulate_request("/", query_string=query_string)
req = self.resource.req
self.assertEqual(req.get_param_as_date("thedate",
format_string=format_string),
date(2015, 4, 20))
def test_get_date_store(self):
date_value = "2015-04-20"
query_string = "thedate={0}".format(date_value)
self.simulate_request("/", query_string=query_string)
req = self.resource.req
store = {}
req.get_param_as_date("thedate", store=store)
self.assertNotEqual(len(store), 0)
def test_get_date_invalid(self):
date_value = "notarealvalue"
query_string = "thedate={0}".format(date_value)
format_string = "%Y%m%d"
self.simulate_request("/", query_string=query_string)
req = self.resource.req
self.assertRaises(HTTPInvalidParam, req.get_param_as_date,
"thedate", format_string=format_string)
class PostQueryParams(_TestQueryParams):
def simulate_request(self, path, query_string, **kwargs):
headers = {"Content-Type": "application/x-www-form-urlencoded"}
super(PostQueryParams, self).simulate_request(
path, body=query_string, headers=headers, **kwargs)
def test_non_ascii(self):
value = u'\u8c46\u74e3'
query_string = b'q=' + value.encode('utf-8')
self.simulate_request('/', query_string=query_string)
req = self.resource.req
self.assertEqual(req.get_param('q'), None)
class GetQueryParams(_TestQueryParams):
def simulate_request(self, path, query_string, **kwargs):
super(GetQueryParams, self).simulate_request(
path, query_string=query_string, **kwargs)