diff --git a/docs/source/format.rst b/docs/source/format.rst index 827d3fd..0bb4731 100644 --- a/docs/source/format.rst +++ b/docs/source/format.rst @@ -39,6 +39,9 @@ Many of these items allow substitutions (explained below). fully qualified URL (with host and scheme). If not qualified the test builder will be responsible for determining host and scheme. **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``. * ``status``: The expected response status code. The default is ``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: * ``url`` +* ``query_parameters`` * ``data`` * ``request_headers`` * ``response_strings`` diff --git a/gabbi/case.py b/gabbi/case.py index 6632421..7b1403b 100644 --- a/gabbi/case.py +++ b/gabbi/case.py @@ -59,6 +59,7 @@ BASE_TEST = { 'url': '', 'status': '200', 'request_headers': {}, + 'query_parameters': {}, 'data': '', 'xfail': False, 'skip': '', @@ -170,6 +171,14 @@ class HTTPTestCase(unittest.TestCase): for handler in self.response_handlers: 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): """Replace an indicator in a message with the environment value.""" 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. """ parsed_url = urlparse.urlsplit(url) - url_scheme = parsed_url[0] scheme = 'http' netloc = self.host + query_params = self.test_data['query_parameters'] - if not url_scheme: + if not parsed_url.scheme: if (self.port and not (int(self.port) == 443 and ssl) and not (int(self.port) == 80 and not ssl)): netloc = '%s:%s' % (self.host, self.port) + if ssl: scheme = 'https' + path = parsed_url[2] if self.prefix: 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, - parsed_url[3], '')) + query_string, '')) self.scheme = scheme self.netloc = netloc else: full_url = url - self.scheme = url_scheme + self.scheme = parsed_url.scheme self.netloc = parsed_url[1] return full_url diff --git a/gabbi/gabbits_intercept/queryparams.yaml b/gabbi/gabbits_intercept/queryparams.yaml new file mode 100644 index 0000000..d3497e4 --- /dev/null +++ b/gabbi/gabbits_intercept/queryparams.yaml @@ -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 + diff --git a/gabbi/tests/test_parse_url.py b/gabbi/tests/test_parse_url.py index 9dd96ce..1033321 100644 --- a/gabbi/tests/test_parse_url.py +++ b/gabbi/tests/test_parse_url.py @@ -26,9 +26,11 @@ class UrlParseTest(unittest.TestCase): @staticmethod def make_test_case(host, port=8000, prefix=''): - # attributes used are port, prefix and host and they must - # be set manually here, due to metaclass magics elsewhere + # Attributes used are port, prefix and host and they must + # be set manually here, due to metaclass magics elsewhere. + # test_data must have a base value. http_case = case.HTTPTestCase('test_request') + http_case.test_data = case.BASE_TEST http_case.host = host http_case.port = port http_case.prefix = prefix