Add query_parameters field

This adds a query_parameters field which can be used to add query
parameters to the url of the test in which it is used. It is a dict in
a form suitable for being passed to the urlencode() method.

This change is cumbersome because urlencoded in python2 and 3 have
different behaviors: In python2 a utf-8 encoded string is required.
In 3 it will figure out the right thing. So we here we just encode.

We also need to deal with the fact that a numeral in YAML will be
provided to Python as a numeric value and we need to stringify that in
a version independent fashion.
This commit is contained in:
Chris Dent
2015-08-02 21:55:09 +01:00
parent 25e86671af
commit 063f00a7a6
4 changed files with 100 additions and 6 deletions

View File

@@ -39,6 +39,9 @@ Many of these items allow substitutions (explained below).
fully qualified URL (with host and scheme). If not qualified the fully qualified URL (with host and scheme). If not qualified the
test builder will be responsible for determining host and scheme. test builder will be responsible for determining host and scheme.
**Required** **Required**
* ``query_parameters``: An optional dictionary of query parameters
that will be added to the ``url``. If there is an existing set of
query parameters they wil be extended.
* ``method``: The request method to use. Defaults to ``GET``. * ``method``: The request method to use. Defaults to ``GET``.
* ``status``: The expected response status code. The default is * ``status``: The expected response status code. The default is
``200``. If necessary you may indicate multiple response codes ``200``. If necessary you may indicate multiple response codes
@@ -110,6 +113,7 @@ character must be used at both ends.
All of these variables may be used in all of the following fields: All of these variables may be used in all of the following fields:
* ``url`` * ``url``
* ``query_parameters``
* ``data`` * ``data``
* ``request_headers`` * ``request_headers``
* ``response_strings`` * ``response_strings``

View File

@@ -59,6 +59,7 @@ BASE_TEST = {
'url': '', 'url': '',
'status': '200', 'status': '200',
'request_headers': {}, 'request_headers': {},
'query_parameters': {},
'data': '', 'data': '',
'xfail': False, 'xfail': False,
'skip': '', 'skip': '',
@@ -170,6 +171,14 @@ class HTTPTestCase(unittest.TestCase):
for handler in self.response_handlers: for handler in self.response_handlers:
handler(self) handler(self)
def _clean_query_value(self, value):
"""Clean up a single query from query_parameters."""
value = self.replace_template(value)
# stringify ints in Python version independent fashion
value = '%s' % value
value = value.encode('UTF-8')
return value
def _environ_replace(self, message): def _environ_replace(self, message):
"""Replace an indicator in a message with the environment value.""" """Replace an indicator in a message with the environment value."""
value = re.sub(self._replacer_regex('ENVIRON'), value = re.sub(self._replacer_regex('ENVIRON'),
@@ -249,27 +258,49 @@ class HTTPTestCase(unittest.TestCase):
Scheme and netloc are saved for later use in comparisons. Scheme and netloc are saved for later use in comparisons.
""" """
parsed_url = urlparse.urlsplit(url) parsed_url = urlparse.urlsplit(url)
url_scheme = parsed_url[0]
scheme = 'http' scheme = 'http'
netloc = self.host netloc = self.host
query_params = self.test_data['query_parameters']
if not url_scheme: if not parsed_url.scheme:
if (self.port if (self.port
and not (int(self.port) == 443 and ssl) and not (int(self.port) == 443 and ssl)
and not (int(self.port) == 80 and not ssl)): and not (int(self.port) == 80 and not ssl)):
netloc = '%s:%s' % (self.host, self.port) netloc = '%s:%s' % (self.host, self.port)
if ssl: if ssl:
scheme = 'https' scheme = 'https'
path = parsed_url[2] path = parsed_url[2]
if self.prefix: if self.prefix:
path = '%s%s' % (self.prefix, path) path = '%s%s' % (self.prefix, path)
if query_params:
encoded_query_params = {}
for param, value in query_params.items():
# isinstance used because we can iter a string
if isinstance(value, list):
encoded_query_params[param] = [
self._clean_query_value(subvalue)
for subvalue in value]
else:
encoded_query_params[param] = (
self._clean_query_value(value))
query_string = urlparse.urlencode(
encoded_query_params, doseq=True)
if parsed_url.query:
query_string = '&'.join([parsed_url.query, query_string])
else:
query_string = parsed_url.query
full_url = urlparse.urlunsplit((scheme, netloc, path, full_url = urlparse.urlunsplit((scheme, netloc, path,
parsed_url[3], '')) query_string, ''))
self.scheme = scheme self.scheme = scheme
self.netloc = netloc self.netloc = netloc
else: else:
full_url = url full_url = url
self.scheme = url_scheme self.scheme = parsed_url.scheme
self.netloc = parsed_url[1] self.netloc = parsed_url[1]
return full_url return full_url

View File

@@ -0,0 +1,57 @@
#
# As a convenience a URL can be augmented with structured declaration
# of query parameters.
#
tests:
- name: simple param
url: /foo
query_parameters:
bar: 1
response_headers:
x-gabbi-url: $SCHEME://$NETLOC/foo?bar=1
- name: joined params
url: /foo?cow=moo
query_parameters:
bar: 1
response_headers:
x-gabbi-url: $SCHEME://$NETLOC/foo?cow=moo&bar=1
- name: multi params
url: /foo
request_headers:
accept: application/json
query_parameters:
bar:
- 1
- 2
response_headers:
x-gabbi-url: $SCHEME://$NETLOC/foo?bar=1&bar=2
content-type: application/json
response_json_paths:
$.bar[0]: "1"
$.bar[1]: "2"
- name: replacers in params
url: /foo
query_parameters:
fromjson: $RESPONSE['$.bar[0]']
response_headers:
x-gabbi-url: $SCHEME://$NETLOC/foo?fromjson=1
- name: unicode
url: /foo
query_parameters:
snowman:
response_headers:
x-gabbi-url: $SCHEME://$NETLOC/foo?snowman=%E2%98%83
- name: url in param
url: /foo
query_parameters:
redirect: http://example.com/treehouse?secret=true&password=hello
response_headers:
x-gabbi-url: $SCHEME://$NETLOC/foo?redirect=http%3A%2F%2Fexample.com%2Ftreehouse%3Fsecret%3Dtrue%26password%3Dhello

View File

@@ -26,9 +26,11 @@ class UrlParseTest(unittest.TestCase):
@staticmethod @staticmethod
def make_test_case(host, port=8000, prefix=''): def make_test_case(host, port=8000, prefix=''):
# attributes used are port, prefix and host and they must # Attributes used are port, prefix and host and they must
# be set manually here, due to metaclass magics elsewhere # be set manually here, due to metaclass magics elsewhere.
# test_data must have a base value.
http_case = case.HTTPTestCase('test_request') http_case = case.HTTPTestCase('test_request')
http_case.test_data = case.BASE_TEST
http_case.host = host http_case.host = host
http_case.port = port http_case.port = port
http_case.prefix = prefix http_case.prefix = prefix