Merge pull request #214 from cdent/cd/simple-coerce

Manage numerals more effectively in replacers
This commit is contained in:
Chris Dent 2017-04-26 21:16:42 +01:00 committed by GitHub
commit e18a889245
7 changed files with 283 additions and 12 deletions

View File

@ -154,6 +154,9 @@ class HTTPTestCase(testtools.TestCase):
message[k] = self.replace_template(message[k])
return message
if isinstance(message, list):
return [self.replace_template(line) for line in message]
for replacer in REPLACERS:
template = '$%s' % replacer
method = '_%s_replace' % replacer.lower()
@ -187,13 +190,27 @@ class HTTPTestCase(testtools.TestCase):
return value
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.
If value can be a number, cast it as such. If value is a form of
"null", "true", or "false" cast it to None, True, False.
"""
value = re.sub(self._replacer_regex('ENVIRON'),
self._environ_replacer, message)
if value == "False":
try:
if '.' in value:
value = float(value)
else:
value = int(value)
return value
except ValueError:
pass
if value.lower() == "false":
return False
if value == "True":
if value.lower() == "true":
return True
if value.lower() == "null":
return None
return value
@staticmethod
@ -351,11 +368,18 @@ class HTTPTestCase(testtools.TestCase):
return r"%s\$%s" % (case, key)
def _response_replace(self, message):
"""Replace a content path with the value from a previous response."""
return re.sub(self._replacer_regex('RESPONSE'),
self._response_replacer, message)
"""Replace a content path with the value from a previous response.
def _response_replacer(self, match):
If the match would replace the entire message, then don't cast it
to a string.
"""
regex = self._replacer_regex('RESPONSE')
match = re.match('^%s$' % regex, message)
if match:
return self._response_replacer(match, preserve=True)
return re.sub(regex, self._response_replacer, message)
def _response_replacer(self, match, preserve=False):
"""Replace a regex match with the value from a previous response."""
response_path = match.group('arg')
case = match.group('case')
@ -368,8 +392,12 @@ class HTTPTestCase(testtools.TestCase):
# If no handler can be found use the null replacer,
# which returns "foo" when "$RESPONSE['foo']".
replacer_class = replacer_class or base.ContentHandler
return replacer_class.replacer(
result = replacer_class.replacer(
referred_case.response_data, response_path)
if preserve:
return result
else:
return six.text_type(result)
def _run_request(self, url, method, headers, body, redirect=False):
"""Run the http request and decode output.
@ -485,6 +513,7 @@ class HTTPTestCase(testtools.TestCase):
else:
dumper_class = self.get_content_handler(content_type)
if dumper_class:
data = self.replace_template(data)
data = dumper_class.dumps(data, test=self)
else:
raise ValueError(

View File

@ -41,7 +41,7 @@ class JSONHandler(base.ContentHandler):
@classmethod
def replacer(cls, response_data, match):
return str(cls.extract_json_path_value(response_data, match))
return cls.extract_json_path_value(response_data, match)
@staticmethod
def dumps(data, pretty=False, test=None):

View File

@ -0,0 +1,201 @@
defaults:
request_headers:
content-type: application/json
verbose: True
tests:
- name: post data
POST: /
data:
one_string: "1"
one_int: 1
one_float: 1.1
response_json_paths:
$.one_string: "1"
$.one_int: 1
$.one_float: 1.1
response_strings:
- '"one_string": "1"'
- '"one_int": 1'
- '"one_float": 1.1'
- name: use data
desc: data will be coerced because templates in use
POST: /
data:
one_string: !!str "$RESPONSE['$.one_string']"
one_int: $RESPONSE['$.one_int']
one_float: $RESPONSE['$.one_float']
response_json_paths:
$.one_string: "1"
$.one_int: 1
$.one_float: 1.1
response_strings:
- '"one_string": "1"'
- '"one_int": 1'
- '"one_float": 1.1'
- name: from environ
POST: /
data:
one_environ: $ENVIRON['ONE']
response_json_paths:
$.one_environ: 1
response_strings:
- '"one_environ": 1'
- name: with list
POST: /
data:
- $ENVIRON['ONE']
- 2
- "3"
response_json_paths:
$[0]: 1
$[1]: 2
$[2]: "3"
response_strings:
- '[1, 2, "3"]'
- name: object with list
desc: without recursive handling no coersion
POST: /
data:
collection:
- alpha: $ENVIRON['ONE']
beta: max
- alpha: 2
beta: climb
response_json_paths:
$.collection[0].alpha: 1
$.collection[0].beta: max
$.collection[1].alpha: 2
$.collection[1].beta: climb
response_strings:
- '"alpha": 1'
- '"beta": "max"'
- name: post extra data
POST: /
data:
a: 1
b: 1.0
c: '[1,2,3]'
d: true
e: false
f:
key: val
g: null
h:
key:
less_key: [1, true, null]
more_key: 1
response_json_paths:
a: 1
b: 1.0
c: '[1,2,3]'
d: true
e: false
f:
key: val
g: null
h:
key:
less_key: [1, true, null]
more_key: 1
- name: check posted data
POST: /
data:
a: $RESPONSE['$.a']
b: $RESPONSE['$.b']
c: $RESPONSE['$.c']
d: $RESPONSE['$.d']
e: $RESPONSE['$.e']
f: $RESPONSE['$.f']
g: $RESPONSE['$.g']
h: $RESPONSE['$.h']
response_json_paths:
a: 1
b: 1.0
c: '[1,2,3]'
d: true
e: false
f:
key: val
g: null
h:
key:
less_key: [1, true, null]
more_key: 1
- name: Post again and check the results
POST: /
data:
a: $HISTORY['post extra data'].$RESPONSE['$.a']
b: $HISTORY['post extra data'].$RESPONSE['$.b']
c: $HISTORY['post extra data'].$RESPONSE['$.c']
d: $HISTORY['post extra data'].$RESPONSE['$.d']
e: $HISTORY['post extra data'].$RESPONSE['$.e']
f: $HISTORY['post extra data'].$RESPONSE['$.f']
g: $HISTORY['post extra data'].$RESPONSE['$.g']
h: $HISTORY['post extra data'].$RESPONSE['$.h']
response_json_paths:
a: $ENVIRON['ONE']
b: $ENVIRON['DECIMAL']
c: $ENVIRON['ARRAY_STRING']
d: $ENVIRON['TRUE']
e: $ENVIRON['FALSE']
f:
key: $ENVIRON['STRING']
g: $ENVIRON['NULL']
h:
key:
less_key:
- $ENVIRON['ONE']
- $ENVIRON['TRUE']
- $ENVIRON['NULL']
more_key: $ENVIRON['ONE']
- name: Post again and check the results (reversed)
POST: /
data:
a: $ENVIRON['ONE']
b: $ENVIRON['DECIMAL']
c: $ENVIRON['ARRAY_STRING']
d: $ENVIRON['TRUE']
e: $ENVIRON['FALSE']
f:
key: $ENVIRON['STRING']
g: $ENVIRON['NULL']
h:
key:
less_key:
- $ENVIRON['ONE']
- $ENVIRON['TRUE']
- $ENVIRON['NULL']
more_key: $ENVIRON['ONE']
response_json_paths:
a: $HISTORY['check posted data'].$RESPONSE['$.a']
b: $HISTORY['check posted data'].$RESPONSE['$.b']
c: $HISTORY['check posted data'].$RESPONSE['$.c']
d: $HISTORY['check posted data'].$RESPONSE['$.d']
e: $HISTORY['check posted data'].$RESPONSE['$.e']
f: $HISTORY['check posted data'].$RESPONSE['$.f']
g: $HISTORY['check posted data'].$RESPONSE['$.g']
h:
key:
less_key:
- $HISTORY['check posted data'].$RESPONSE['$.h.key.less_key[0]']
- $HISTORY['check posted data'].$RESPONSE['$.h.key.less_key[1]']
- $HISTORY['check posted data'].$RESPONSE['$.h.key.less_key[2]']
more_key: $HISTORY['check posted data'].$RESPONSE['$.h.key.more_key']
- name: string internal replace
POST: /
data:
endpoint_resp: /api/0.1/item/$HISTORY['check posted data'].$RESPONSE['$.a']
endpoint_var: /api/0.1/item/$ENVIRON['ONE']
response_json_paths:
endpoint_resp: /api/0.1/item/1
endpoint_var: /api/0.1/item/1

View File

@ -24,12 +24,13 @@ from gabbi import driver
from gabbi.driver import test_pytest # noqa
from gabbi.tests import simple_wsgi
from gabbi.tests import test_intercept
from gabbi.tests import util
TESTS_DIR = 'gabbits_intercept'
def pytest_generate_tests(metafunc):
os.environ['GABBI_TEST_URL'] = 'takingnames'
util.set_test_environ()
test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR)
driver.py_test_generator(
test_dir, intercept=simple_wsgi.SimpleWsgi,

View File

@ -23,6 +23,7 @@ from gabbi import driver
from gabbi import fixture
from gabbi.handlers import base
from gabbi.tests import simple_wsgi
from gabbi.tests import util
TESTS_DIR = 'gabbits_intercept'
@ -64,7 +65,8 @@ SkipAllFixture = fixture.SkipAllFixture
def load_tests(loader, tests, pattern):
"""Provide a TestSuite to the discovery process."""
# Set and environment variable for one of the tests.
os.environ['GABBI_TEST_URL'] = 'takingnames'
util.set_test_environ()
prefix = os.environ.get('GABBI_PREFIX')
test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR)
return driver.build_tests(test_dir, loader, host=None,

View File

@ -37,8 +37,17 @@ class EnvironReplaceTest(unittest.TestCase):
os.environ['moo'] = "False"
self.assertEqual(False, http_case._environ_replace(message))
os.environ['moo'] = "true"
self.assertEqual(True, http_case._environ_replace(message))
os.environ['moo'] = "faLse"
self.assertEqual(False, http_case._environ_replace(message))
os.environ['moo'] = "null"
self.assertEqual(None, http_case._environ_replace(message))
os.environ['moo'] = "1"
self.assertEqual("1", http_case._environ_replace(message))
self.assertEqual(1, http_case._environ_replace(message))
os.environ['moo'] = "cow"
self.assertEqual("cow", http_case._environ_replace(message))

29
gabbi/tests/util.py Normal file
View File

@ -0,0 +1,29 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Utility methods shared by some tests."""
import os
def set_test_environ():
"""Set some environment variables used in tests."""
os.environ['GABBI_TEST_URL'] = 'takingnames'
# Setup environment variables for `coerce.yaml`
os.environ['ONE'] = '1'
os.environ['DECIMAL'] = '1.0'
os.environ['ARRAY_STRING'] = '[1,2,3]'
os.environ['TRUE'] = 'true'
os.environ['FALSE'] = 'false'
os.environ['STRING'] = 'val'
os.environ['NULL'] = 'null'