From bcf7aaf90973f31a62d82040ada3fd8909a3331f Mon Sep 17 00:00:00 2001 From: "Karl O. Pinc" Date: Sun, 28 Jun 2020 15:23:26 -0500 Subject: [PATCH] Switch mock handling in tests; plugin config declarations are ok --- setup.py | 1 + src/pgwui_server/__init__.py | 46 +++--- tests/test___init__.py | 291 ++++++++++++++++++----------------- tox.ini | 3 + 4 files changed, 179 insertions(+), 162 deletions(-) diff --git a/setup.py b/setup.py index f207711..a9e9405 100644 --- a/setup.py +++ b/setup.py @@ -61,6 +61,7 @@ long_description = filter_readme() tests_require = [ 'pytest>=3.7.4', 'pytest-cov', + 'pgwui_testing==' + version, ] setup( diff --git a/src/pgwui_server/__init__.py b/src/pgwui_server/__init__.py index a85140d..e6baaa7 100644 --- a/src/pgwui_server/__init__.py +++ b/src/pgwui_server/__init__.py @@ -25,9 +25,10 @@ from ast import literal_eval from pyramid.config import Configurator import logging -import pkg_resources import sys +from pgwui_common import plugin + # Constants # All the settings recognized by PGWUI @@ -111,11 +112,12 @@ class HMACLengthError(BadHMACError): # Functions -def abort_on_bad_setting(errors, key): +def abort_on_bad_setting(errors, key, component_keys): '''Abort on a bad pgwui setting ''' if key[:6] == 'pgwui.': - if key[6:] not in SETTINGS: + if (key[6:] not in SETTINGS + and key not in component_keys): errors.append(UnknownSettingKeyError(key)) @@ -187,11 +189,12 @@ def validate_literal_column_headings(errors, settings): errors.append(BadLiteralColumnHeadingsError(value)) -def validate_settings(errors, settings): +def validate_settings(errors, settings, components): '''Be sure all settings validate ''' + component_keys = ['pgwui.{}'.format(component) for component in components] for key in settings.keys(): - abort_on_bad_setting(errors, key) + abort_on_bad_setting(errors, component_keys, key) validate_setting_values(errors, settings) validate_hmac(errors, settings) @@ -211,11 +214,11 @@ def exit_reporting_errors(errors): sys.exit(1) -def exit_on_invalid_settings(settings): +def exit_on_invalid_settings(settings, components): '''Exit when settings don't validate ''' errors = [] - validate_settings(errors, settings) + validate_settings(errors, settings, components) if errors: exit_reporting_errors(errors) @@ -245,14 +248,7 @@ def add_routes(config, settings): config.add_route(name, route) -def find_pgwui_components(): - '''Return list of all pgwui component names as strings - ''' - return [entry_point.resolve().__name__ for entry_point in - pkg_resources.iter_entry_points('pgwui.components')] - - -def autoconfig_components(settings): +def autoconfigurable_components(settings, components): '''Automatic pgwui component discovery ''' autoconfig = settings.get('pgwui.autoconfigure') @@ -262,25 +258,31 @@ def autoconfig_components(settings): if 'pyramid.include' in settings: log.info(AutoconfigureConflict()) - return find_pgwui_components() + return components -def pgwui_server_config(settings): - '''Configure pyramid +def apply_component_defaults(settings, components): + '''Apply component default settings to existing settings ''' - exit_on_invalid_settings(settings) - - components = autoconfig_components(settings) + components_to_config = autoconfigurable_components(settings, components) rp = settings.get('pgwui.route_prefix') with Configurator(settings=settings, route_prefix=rp) as config: config.include('pgwui_common') - for component in components: + for component in components_to_config: config.include(component) add_routes(config, settings) return config +def pgwui_server_config(settings): + '''Configure pyramid + ''' + components = plugin.find_pgwui_components() + exit_on_invalid_settings(settings, components) + return apply_component_defaults(settings, components) + + def main(global_config, **settings): '''Return a Pyramid WSGI application ''' diff --git a/tests/test___init__.py b/tests/test___init__.py index 9ceda1d..90d8153 100644 --- a/tests/test___init__.py +++ b/tests/test___init__.py @@ -1,4 +1,5 @@ -# Copyright (C) 2018, 2019 The Meme Factory, Inc. http://www.meme.com/ +# Copyright (C) 2018, 2019, 2020 The Meme Factory, Inc. +# http://www.karlpinc.com/ # This file is part of PGWUI_Server. # @@ -17,12 +18,18 @@ # . # -# Karl O. Pinc +# Karl O. Pinc import logging import pytest import sys +import pyramid.testing + +import pgwui_common.plugin +# Use as a regular module, not a plugin, so lint checks work +from pgwui_testing import testing + import pgwui_server.__init__ as pgwui_server_init @@ -35,6 +42,7 @@ TEST_SETTINGS = { # Use contextlib.AbstractContextManager for Python >= 3.6 +# (Or, better, use the magic mock maker that's not yet integrated.) class MockConfigurator(): def __init__(self, **kwargs): pass @@ -52,12 +60,10 @@ class MockConfigurator(): pass -class MockConfig(): - def __init__(self): - self.add_route_called = 0 +mock_add_route = testing.instance_method_mock_fixture('add_route') - def add_route(self, *args): - self.add_route_called += 1 +mock_find_pgwui_components = testing.make_mock_fixture( + pgwui_common.plugin, 'find_pgwui_components') # Unit tests @@ -67,7 +73,7 @@ class MockConfig(): def test_abort_on_bad_setting_unknown(): '''Nothing bad happens when there's a non-pgwui setting''' errors = [] - pgwui_server_init.abort_on_bad_setting(errors, 'foo') + pgwui_server_init.abort_on_bad_setting(errors, 'foo', []) assert errors == [] @@ -75,7 +81,7 @@ def test_abort_on_bad_setting_unknown(): def test_abort_on_bad_setting_bad(): '''Delivers an error on a bad pgwui setting''' errors = [] - pgwui_server_init.abort_on_bad_setting(errors, 'pgwui.foo') + pgwui_server_init.abort_on_bad_setting(errors, 'pgwui.foo', []) assert errors assert isinstance(errors[0], pgwui_server_init.UnknownSettingKeyError) @@ -84,11 +90,24 @@ def test_abort_on_bad_setting_bad(): def test_abort_on_bad_setting_good(): '''Does nothing when a known pgwui setting is supplied''' errors = [] - pgwui_server_init.abort_on_bad_setting(errors, 'pgwui.pg_host') + pgwui_server_init.abort_on_bad_setting(errors, 'pgwui.pg_host', []) + + assert errors == [] + + +def test_abort_on_bad_setting_plugin(): + '''Does nothing when a known plugin has a setting''' + errors = [] + pgwui_server_init.abort_on_bad_setting( + errors, 'pgwui.pgwui_upload', ['pgwui.pgwui_upload']) assert errors == [] +mock_abort_on_bad_setting = testing.make_mock_fixture( + pgwui_server_init, 'abort_on_bad_setting') + + # require_setting() def test_require_setting_missing(): @@ -108,6 +127,10 @@ def test_require_setting_present(): assert errors == [] +mock_require_setting = testing.make_mock_fixture( + pgwui_server_init, 'require_setting') + + # boolean_setting() def test_boolean_setting_missing(): @@ -143,30 +166,23 @@ def test_boolean_setting_notboolean(): assert isinstance(errors[0], pgwui_server_init.NotBooleanSettingError) +mock_boolean_setting = testing.make_mock_fixture( + pgwui_server_init, 'boolean_setting') + + # validate_setting_values() -def test_validate_setting_values(monkeypatch): +def test_validate_setting_values(mock_require_setting, mock_boolean_setting): '''Calls require_setting() and boolean_setting()''' - require_setting_called = False - boolean_setting_called = False - - def mock_require_setting(*args): - nonlocal require_setting_called - require_setting_called = True - def mock_boolean_setting(*args): - nonlocal boolean_setting_called - boolean_setting_called = True + pgwui_server_init.validate_setting_values([], {}) - monkeypatch.setattr(pgwui_server_init, 'require_setting', - mock_require_setting) - monkeypatch.setattr(pgwui_server_init, 'boolean_setting', - mock_boolean_setting) + assert mock_require_setting.called + assert mock_boolean_setting.called - pgwui_server_init.validate_setting_values([], {}) - assert require_setting_called - assert boolean_setting_called +mock_validate_setting_values = testing.make_mock_fixture( + pgwui_server_init, 'validate_setting_values') # do_validate_hmac() @@ -190,22 +206,24 @@ def test_do_validate_hmac_False(): assert result is False +mock_do_validate_hmac = testing.make_mock_fixture( + pgwui_server_init, 'do_validate_hmac') + + # validate_hmac() -def test_validate_hmac_unvalidated(monkeypatch): +def test_validate_hmac_unvalidated(mock_do_validate_hmac): '''No error is returned when hmac validation is off''' - monkeypatch.setattr(pgwui_server_init, 'do_validate_hmac', - lambda *args: False) + mock_do_validate_hmac.return_value = False errors = [] pgwui_server_init.validate_hmac(errors, {}) assert errors == [] -def test_validate_hmac_success(monkeypatch): +def test_validate_hmac_success(mock_do_validate_hmac): '''No error is returned when hmac is validated an the right length''' - monkeypatch.setattr(pgwui_server_init, 'do_validate_hmac', - lambda *args: True) + mock_do_validate_hmac.return_value = True errors = [] pgwui_server_init.validate_hmac( errors, {'session.secret': 'x' * pgwui_server_init.HMAC_LEN}) @@ -213,10 +231,9 @@ def test_validate_hmac_success(monkeypatch): assert errors == [] -def test_validate_hmac_missing(monkeypatch): +def test_validate_hmac_missing(mock_do_validate_hmac): '''Deliver error when hmac is validated and missing''' - monkeypatch.setattr(pgwui_server_init, 'do_validate_hmac', - lambda *args: True) + mock_do_validate_hmac.return_value = True errors = [] pgwui_server_init.validate_hmac(errors, {}) @@ -224,10 +241,9 @@ def test_validate_hmac_missing(monkeypatch): assert isinstance(errors[0], pgwui_server_init.NoHMACError) -def test_validate_hmac_length(monkeypatch): +def test_validate_hmac_length(mock_do_validate_hmac): '''Deliver error when hmac is validated and the wrong length''' - monkeypatch.setattr(pgwui_server_init, 'do_validate_hmac', - lambda *args: True) + mock_do_validate_hmac.return_value = True errors = [] pgwui_server_init.validate_hmac(errors, {'session.secret': ''}) @@ -235,6 +251,10 @@ def test_validate_hmac_length(monkeypatch): assert isinstance(errors[0], pgwui_server_init.HMACLengthError) +mock_validate_hmac = testing.make_mock_fixture( + pgwui_server_init, 'validate_hmac') + + # validate_literal_column_headings() def test_validate_literal_column_headings_nosetting(): @@ -283,29 +303,30 @@ def test_validate_literal_column_headings_bad(): errors[0], pgwui_server_init.BadLiteralColumnHeadingsError) +mock_validate_literal_column_headings = testing.make_mock_fixture( + pgwui_server_init, 'validate_literal_column_headings') + + # validate_settings() -def test_validate_settings(monkeypatch): +def test_validate_settings(mock_abort_on_bad_setting, + mock_validate_setting_values, + mock_validate_hmac): '''Calls abort_on_bad_setting() for each key in setting ''' - count = 0 - - def mock_abort_on_bad_setting(*args): - nonlocal count - count += 1 - - monkeypatch.setattr(pgwui_server_init, 'abort_on_bad_setting', - mock_abort_on_bad_setting) - monkeypatch.setattr(pgwui_server_init, 'validate_setting_values', - lambda *args: None) - monkeypatch.setattr(pgwui_server_init, 'validate_hmac', - lambda *args: None) settings = {'key1': 'value1', 'key2': 'value2'} errors = [] - pgwui_server_init.validate_settings(errors, settings) - assert count == len(settings) + pgwui_server_init.validate_settings(errors, settings, []) + + assert mock_validate_setting_values.called + assert mock_validate_hmac.called + assert mock_abort_on_bad_setting.call_count == len(settings) + + +mock_validate_settings = testing.make_mock_fixture( + pgwui_server_init, 'validate_settings') # exit_reporting_errors() @@ -364,43 +385,38 @@ def test_exit_reporting_errors_printed( assert errlines[1] != 'three' +mock_exit_reporting_errors = testing.make_mock_fixture( + pgwui_server_init, 'exit_reporting_errors') + + # exit_on_invalid_settings() -def test_exit_on_invalid_settings_invalid(monkeypatch): +def test_exit_on_invalid_settings_invalid(monkeypatch, + mock_exit_reporting_errors): '''Calls validate_settings and exit_reporting_errors() when setting is invalid ''' - def mock_validate_settings(errors, settings): + def mock_validate_settings(errors, settings, components): errors.append('error1') - exit_reporting_errors_called = False - - def mock_exit_reporting_errors(errors): - nonlocal exit_reporting_errors_called - exit_reporting_errors_called = True - monkeypatch.setattr(pgwui_server_init, 'validate_settings', mock_validate_settings) - monkeypatch.setattr(pgwui_server_init, 'exit_reporting_errors', - mock_exit_reporting_errors) - pgwui_server_init.exit_on_invalid_settings({}) + pgwui_server_init.exit_on_invalid_settings({}, []) - assert exit_reporting_errors_called + assert mock_exit_reporting_errors.called -def test_exit_on_invalid_settings_valid(monkeypatch): +def test_exit_on_invalid_settings_valid(mock_validate_settings): '''Returns, without exiting, when all settings are valid ''' - def mock_validate_settings(errors, settings): - pass + pgwui_server_init.exit_on_invalid_settings({}, []) - monkeypatch.setattr(pgwui_server_init, 'validate_settings', - mock_validate_settings) + assert True - pgwui_server_init.exit_on_invalid_settings({}) - assert True +mock_exit_on_invalid_settings = testing.make_mock_fixture( + pgwui_server_init, 'exit_on_invalid_settings') # parse_assignments() @@ -431,55 +447,29 @@ def test_parse_assignments_dict(): ]) -# find_pgwui_components() - -def test_find_pgwui_components(monkeypatch): - '''Returns list of entry points via iter_entry_points() - ''' - entry_points = ['a', 'b', 'c'] - - class MockEntryPoint(): - def __init__(self, val): - self.__name__ = val - - def resolve(self): - return self - - class MockPkgResources(): - def iter_entry_points(*args): - return [MockEntryPoint(name) for name in entry_points] +mock_parse_assignments = testing.make_mock_fixture( + pgwui_server_init, 'parse_assignments') - monkeypatch.setattr( - pgwui_server_init, 'pkg_resources', MockPkgResources()) - result = pgwui_server_init.find_pgwui_components() +# autoconfigurable_components() - assert result == entry_points - - -# autoconfig_components() - -def test_autoconfig_components_no_autoconfig(monkeypatch): +def test_autoconfiguable_components_no_autoconfig(): '''When the settings have no pgwui.autoconfigure return an empty list ''' - monkeypatch.setattr(pgwui_server_init, 'find_pgwui_components', - lambda *args: []) + test_components = ['some', 'components'] - result = pgwui_server_init.autoconfig_components({}) + result = pgwui_server_init.autoconfigurable_components({}, test_components) assert result == [] -def test_autoconfig_components_log_info(monkeypatch, caplog): +def test_autoconfigurable_components_log_info(caplog): '''When pyramid.include is in the settings an INFO message is logged ''' - monkeypatch.setattr(pgwui_server_init, 'find_pgwui_components', - lambda *args: []) - caplog.set_level(logging.INFO) - pgwui_server_init.autoconfig_components({'pgwui.autoconfigure': True, - 'pyramid.include': None}) + pgwui_server_init.autoconfigurable_components( + {'pgwui.autoconfigure': True, 'pyramid.include': None}, []) logs = caplog.record_tuples @@ -489,61 +479,82 @@ def test_autoconfig_components_log_info(monkeypatch, caplog): assert level == logging.INFO -def test_autoconfig_components_find_pgwui_components_called(monkeypatch): - '''When pyramid.include is in the settings an INFO message is logged +def test_autoconfigurable_components_components_returned(): + '''The suppiled components are returned when autoconfigure is True ''' - find_pgwui_components_called = False + test_components = ['some', 'components'] - def mock_find_pgwui_components(*args): - nonlocal find_pgwui_components_called - find_pgwui_components_called = True + result = pgwui_server_init.autoconfigurable_components( + {'pgwui.autoconfigure': True}, test_components) - monkeypatch.setattr(pgwui_server_init, 'find_pgwui_components', - mock_find_pgwui_components) + assert result == test_components - pgwui_server_init.autoconfig_components({'pgwui.autoconfigure': True, - 'mock_pgwui_component': 'foo'}) - assert find_pgwui_components_called +mock_autoconfigurable_components = testing.make_mock_fixture( + pgwui_server_init, 'autoconfigurable_components') # add_routes() -def test_add_routes_empty(): +def test_add_routes_empty(mock_add_route): '''When there is no pgwui.routes setting nothing gets added''' - config = MockConfig() - pgwui_server_init.add_routes(config, {}) - assert config.add_route_called == 0 + with pyramid.testing.testConfig() as config: + mocked_add_route = mock_add_route(config) + pgwui_server_init.add_routes(config, {}) + + assert not mocked_add_route.called -def test_add_routes_notempty(monkeypatch): +def test_add_routes_notempty(mock_add_route, mock_parse_assignments): '''When there is a pgwui.routes setting config.add_route() is called for each route''' - config = MockConfig() - monkeypatch.setattr(pgwui_server_init, 'parse_assignments', - lambda *args: [('name1', 'route1'), - ('name2', 'route2')]) - pgwui_server_init.add_routes(config, {'pgwui.routes': ''}) - assert config.add_route_called == 2 + test_routes = [('name1', 'route1'), + ('name2', 'route2')] + mock_parse_assignments.return_value = test_routes + with pyramid.testing.testConfig() as config: + mocked_add_route = mock_add_route(config) + pgwui_server_init.add_routes(config, {'pgwui.routes': ''}) + assert mocked_add_route.call_count == len(test_routes) -# pgwui_server_config() -def test_pgwui_server_config(monkeypatch): - '''Returns a configuration''' - monkeypatch.setattr(pgwui_server_init, 'exit_on_invalid_settings', - lambda *args: True) - monkeypatch.setattr(pgwui_server_init, 'autoconfig_components', - lambda *args: ['pgwui_mock_component_name']) +mock_add_routes = testing.make_mock_fixture( + pgwui_server_init, 'add_routes') + + +# apply_component_defaults() + + +def test_apply_component_defaults(monkeypatch, + mock_autoconfigurable_components, + mock_add_routes): + mock_autoconfigurable_components.return_value = \ + ['pgwui_mock_component_name'] monkeypatch.setattr(pgwui_server_init, 'Configurator', MockConfigurator) - monkeypatch.setattr(pgwui_server_init, 'add_routes', - lambda *args: None) - result = pgwui_server_init.pgwui_server_config({}) + result = pgwui_server_init.apply_component_defaults({}, []) assert isinstance(result, MockConfigurator) +mock_apply_component_defaults = testing.make_mock_fixture( + pgwui_server_init, 'apply_component_defaults') + + +# pgwui_server_config() + +def test_pgwui_server_config( + mock_find_pgwui_components, + mock_apply_component_defaults, mock_exit_on_invalid_settings): + '''Returns a configuration''' + test_configurator = 'test configurator' + mock_apply_component_defaults.return_value = test_configurator + + result = pgwui_server_init.pgwui_server_config({}) + + assert result == test_configurator + + # main() def test_main(monkeypatch): '''Returns a wsgi app''' diff --git a/tox.ini b/tox.ini index 809a130..7c02bc4 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,9 @@ deps = pytest-cov twine # coverage + # This is a bug, because we'd like to specify the pgwui_testing + # version to be equal to the pgwui_server version being tested. + pgwui_testing commands = check-manifest python setup.py sdist -- 2.34.1