From 08cb8ac1bde3d8cd21047e3084bf028a6a010c6a Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Thu, 2 Jun 2016 09:15:57 +0100 Subject: [PATCH] Allowing forcing SSL in build_tests and gabbi-run build_tests gains a require_ssl argument which, if set to True, makes all the loaded test suites default to 'ssl: True'. gabbi-run will interpret a target containing 'https' as meaning that the tests in the provided yaml should default to 'ssl: True'. Fixes: #50 Fixes: #105 Fixes: #138 The changes here are the naive basics to get the desired behavior. There's an existing cleanup branch on which we can clean this up later. --- docs/source/example.py | 2 ++ docs/source/pytest-example.py | 2 ++ docs/source/runner.rst | 3 +++ gabbi/driver.py | 15 ++++++++++++--- gabbi/runner.py | 10 ++++++++++ gabbi/tests/test_driver.py | 15 +++++++++++++++ gabbi/tests/test_runner.py | 15 ++++++++++++--- 7 files changed, 56 insertions(+), 6 deletions(-) diff --git a/docs/source/example.py b/docs/source/example.py index ce089d6..f83ea9c 100644 --- a/docs/source/example.py +++ b/docs/source/example.py @@ -22,6 +22,8 @@ TESTS_DIR = 'gabbits' def load_tests(loader, tests, pattern): """Provide a TestSuite to the discovery process.""" test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR) + # Pass "require_ssl=True" as an argument to force all tests + # to use SSL in requests. return driver.build_tests(test_dir, loader, intercept=wsgiapp.app, fixture_module=fixtures) diff --git a/docs/source/pytest-example.py b/docs/source/pytest-example.py index c1b18bb..6d21a55 100644 --- a/docs/source/pytest-example.py +++ b/docs/source/pytest-example.py @@ -20,6 +20,8 @@ TESTS_DIR = 'gabbits' def test_gabbits(): test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR) + # Pass "require_ssl=True" as an argument to force all tests + # to use SSL in requests. test_generator = driver.py_test_generator( test_dir, intercept=wsgiapp.app, fixture_module=fixtures) diff --git a/docs/source/runner.rst b/docs/source/runner.rst index aaa5815..c943ec7 100644 --- a/docs/source/runner.rst +++ b/docs/source/runner.rst @@ -35,5 +35,8 @@ are not fully qualified. Anywhere host is used, if it is a raw IPV6 address it should be wrapped in ``[`` and ``]``. +If ``https`` is used in the target, then the tests in the provided +YAML will default to ``ssl: True``. + If a ``-x`` or ``--failfast`` argument is provided then ``gabbi-run`` will exit after the first test failure. diff --git a/gabbi/driver.py b/gabbi/driver.py index bbe2713..d70653c 100644 --- a/gabbi/driver.py +++ b/gabbi/driver.py @@ -188,7 +188,7 @@ class TestBuilder(type): def build_tests(path, loader, host=None, port=8001, intercept=None, test_loader_name=None, fixture_module=None, - response_handlers=None, prefix=''): + response_handlers=None, prefix='', require_ssl=False): """Read YAML files from a directory to create tests. Each YAML file represents an ordered sequence of HTTP requests. @@ -203,6 +203,7 @@ def build_tests(path, loader, host=None, port=8001, intercept=None, :param response_handers: ResponseHandler classes. :type response_handlers: List of ResponseHandler classes. :param prefix: A URL prefix for all URLs that are not fully qualified. + :param require_ssl: If ``True``, make all tests default to using SSL. :rtype: TestSuite containing multiple TestSuites (one for each YAML file). """ @@ -228,6 +229,13 @@ def build_tests(path, loader, host=None, port=8001, intercept=None, suite_dict = load_yaml(test_file) test_base_name = '%s_%s' % ( test_loader_name, os.path.splitext(os.path.basename(test_file))[0]) + + if require_ssl: + if 'defaults' in suite_dict: + suite_dict['defaults']['ssl'] = True + else: + suite_dict['defaults'] = {'ssl': True} + file_suite = test_suite_from_dict(loader, test_base_name, suite_dict, path, host, port, fixture_module, intercept, prefix) @@ -237,7 +245,8 @@ def build_tests(path, loader, host=None, port=8001, intercept=None, def py_test_generator(test_dir, host=None, port=8001, intercept=None, prefix=None, test_loader_name=None, - fixture_module=None, response_handlers=None): + fixture_module=None, response_handlers=None, + require_ssl=False): """Generate tests cases for py.test This uses build_tests to create TestCases and then yields them in @@ -250,7 +259,7 @@ def py_test_generator(test_dir, host=None, port=8001, intercept=None, test_loader_name=test_loader_name, fixture_module=fixture_module, response_handlers=response_handlers, - prefix=prefix) + prefix=prefix, require_ssl=require_ssl) for test in tests: if hasattr(test, '_tests'): diff --git a/gabbi/runner.py b/gabbi/runner.py index 25036a4..31e3773 100644 --- a/gabbi/runner.py +++ b/gabbi/runner.py @@ -92,10 +92,13 @@ def run(): ) args = parser.parse_args() + force_ssl = False split_url = urlparse.urlsplit(args.target) if split_url.scheme: target = split_url.netloc prefix = split_url.path + if split_url.scheme == 'https': + force_ssl = True else: target = args.target prefix = args.prefix @@ -118,6 +121,13 @@ def run(): handler(case.HTTPTestCase) data = yaml.safe_load(sys.stdin.read()) + # Only override the default if we are forcing a change, there may + # already be a default. + if force_ssl: + if 'defaults' in data: + data['defaults']['ssl'] = True + else: + data['defaults'] = {'ssl': True} loader = unittest.defaultTestLoader suite = driver.test_suite_from_dict(loader, 'input', data, '.', host, port, None, None, diff --git a/gabbi/tests/test_driver.py b/gabbi/tests/test_driver.py index 65cf991..b309e85 100644 --- a/gabbi/tests/test_driver.py +++ b/gabbi/tests/test_driver.py @@ -56,6 +56,21 @@ class DriverTest(unittest.TestCase): with self.assertRaises(AssertionError): driver.build_tests(self.test_dir, self.loader) + def test_build_require_ssl(self): + suite = driver.build_tests(self.test_dir, self.loader, + host='localhost', + require_ssl=True) + first_test = suite._tests[0]._tests[0] + full_url = first_test._parse_url(first_test.test_data['url']) + self.assertEqual('https://localhost:8001/', full_url) + + suite = driver.build_tests(self.test_dir, self.loader, + host='localhost', + require_ssl=False) + first_test = suite._tests[0]._tests[0] + full_url = first_test._parse_url(first_test.test_data['url']) + self.assertEqual('http://localhost:8001/', full_url) + def test_tests_key_required(self): test_yaml = {'name': 'house', 'url': '/'} diff --git a/gabbi/tests/test_runner.py b/gabbi/tests/test_runner.py index a5c9ea0..8d1d11c 100644 --- a/gabbi/tests/test_runner.py +++ b/gabbi/tests/test_runner.py @@ -228,15 +228,18 @@ class RunnerHostArgParse(unittest.TestCase): def _test_hostport(self, url_or_host, expected_host, portmock_yaml, mock_test_suite, mock_read, mock_exit, provided_prefix=None, expected_port=None, - expected_prefix=None,): + expected_prefix=None, expected_data=None): sys.argv = ['gabbi-run', url_or_host] if provided_prefix: sys.argv.append(provided_prefix) runner.run() + expected_data = expected_data or {} + mock_test_suite.assert_called_with( - unittest.defaultTestLoader, 'input', {}, '.', expected_host, - expected_port, None, None, prefix=expected_prefix + unittest.defaultTestLoader, 'input', expected_data, + '.', expected_host, expected_port, None, None, + prefix=expected_prefix ) def test_plain_url(self): @@ -245,6 +248,12 @@ class RunnerHostArgParse(unittest.TestCase): expected_port='80', expected_prefix='/news') + def test_ssl_url(self): + self._test_hostport('https://foobar.com/news', + 'foobar.com', + expected_prefix='/news', + expected_data={'defaults': {'ssl': True}}) + def test_simple_hostport(self): self._test_hostport('foobar.com:999', 'foobar.com',