diff --git a/internal/tests/pytests/conftest.py b/internal/tests/pytests/conftest.py index 47c7127c2c..795a45f802 100644 --- a/internal/tests/pytests/conftest.py +++ b/internal/tests/pytests/conftest.py @@ -5,6 +5,7 @@ import pytest import getpass import shutil +from unittest import mock from pathlib import Path from netCDF4 import Dataset @@ -80,12 +81,30 @@ def metplus_config(request): the failed tests. To use this fixture, add metplus_config to the test function arguments and set a variable called config to metplus_config, e.g. config = metplus_config. + + This fixture also replaces config.logger with a MagicMock object. This + allows tests to assert the logger was called with a specific message. + + e.g. + def test_example(metplus_config): + config = metplus_config + some_function(config) + config.logger.info.assert_called_once_with("Info message") + config.logger.error.assert_not_called() + + See documentation for unittest.mock for full functionality. """ script_dir = os.path.dirname(__file__) args = [os.path.join(script_dir, "minimum_pytest.conf")] config = config_metplus.setup(args) + + # Set mock logger + old_logger = config.logger + config.logger = mock.MagicMock() + yield config + config.logger = old_logger # don't remove output base if test fails if request.node.rep_call.failed: return @@ -115,6 +134,7 @@ def read_configs(extra_configs): return read_configs + @pytest.fixture(scope="module") def make_dummy_nc(): return make_nc diff --git a/internal/tests/pytests/util/config_validate/met_config_validate.conf b/internal/tests/pytests/util/config_validate/met_config_validate.conf new file mode 100644 index 0000000000..77f5e679d3 --- /dev/null +++ b/internal/tests/pytests/util/config_validate/met_config_validate.conf @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////////// +// Fake config for testing config_validate.py +// +censor_thresh = []; +censor_val = []; +cat_thresh = [ NA ]; +cnt_thresh = [ NA ]; + +// +// Forecast and observation fields to be verified +// +fcst = { + ${METPLUS_FCST_FILE_TYPE} + ${METPLUS_FCST_FIELD} +} + +obs = { + ${METPLUS_OBS_FILE_TYPE} + ${METPLUS_OBS_FIELD} +} +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/internal/tests/pytests/util/config_validate/test_config_validate.py b/internal/tests/pytests/util/config_validate/test_config_validate.py index 0cd4a3193c..23d97906e4 100644 --- a/internal/tests/pytests/util/config_validate/test_config_validate.py +++ b/internal/tests/pytests/util/config_validate/test_config_validate.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 import pytest - +from unittest import mock import pprint import os from datetime import datetime -from metplus.util.config_validate import * +from metplus.util import config_validate as cv @pytest.mark.parametrize( @@ -51,7 +51,7 @@ @pytest.mark.util def test_is_var_item_valid(metplus_config, item_list, extension, is_valid): conf = metplus_config - assert is_var_item_valid(item_list, '1', extension, conf)[0] == is_valid + assert cv.is_var_item_valid(item_list, '1', extension, conf)[0] == is_valid @pytest.mark.parametrize( @@ -97,4 +97,94 @@ def test_is_var_item_valid_levels(metplus_config, item_list, configs_to_set, is_ for key, value in configs_to_set.items(): conf.set('config', key, value) - assert is_var_item_valid(item_list, '1', 'LEVELS', conf)[0] == is_valid + assert cv.is_var_item_valid(item_list, '1', 'LEVELS', conf)[0] == is_valid + + + +@pytest.mark.parametrize( + 'deprecated_list, expected', + [ + ([], (True, [])), + (['METPLUS_FCST_FILE_TYPE'], (False, [])), + (['SOME_OTHER_CONFIG_ITEM'], (True, [])), + ] +) +@pytest.mark.util +def test_check_for_deprecated_met_config(metplus_config, deprecated_list, expected): + script_dir = os.path.dirname(__file__) + met_config = os.path.join(script_dir, 'met_config_validate.conf') + + metplus_config.set('config', 'TEST_CONFIG_FILE', met_config) + + with mock.patch.object(cv, 'DEPRECATED_MET_LIST', deprecated_list): + actual = cv.check_for_deprecated_met_config(metplus_config) + assert actual == expected + + +@pytest.mark.util +def test_check_for_deprecated_config_simple(metplus_config): + actual = cv.check_for_deprecated_config(metplus_config) + assert actual == (True, []) + metplus_config.logger.error.assert_not_called + + +@pytest.mark.parametrize( + 'dep_item,deprecated_dict, expected, err_msgs', + [ + ( + "TEST_DEPRECATED", + { + 'upgrade': 'ensemble', + 'alt': 'ice cream', + 'copy': True + }, + (False, ["sed -i 's|^TEST_DEPRECATED|ice cream|g' dir/config1.conf", "sed -i 's|{TEST_DEPRECATED}|{ice cream}|g' dir/config1.conf"]), + ['DEPRECATED CONFIG ITEMS WERE FOUND. PLEASE FOLLOW THE INSTRUCTIONS TO UPDATE THE CONFIG FILES', + 'TEST_DEPRECATED should be replaced with ice cream'], + ), + ( + "TEST_DEPRECATED_", + { + 'upgrade': 'ensemble', + 'alt': 'nth degree', + 'copy': False + }, + (False, []), + ['DEPRECATED CONFIG ITEMS WERE FOUND. PLEASE FOLLOW THE INSTRUCTIONS TO UPDATE THE CONFIG FILES'], + ), + ( + "TEST_DEPRECATED_NO_ALT", + {}, + (False, []), + ['DEPRECATED CONFIG ITEMS WERE FOUND. PLEASE FOLLOW THE INSTRUCTIONS TO UPDATE THE CONFIG FILES', + 'TEST_DEPRECATED_NO_ALT should be removed'], + ), + ( + "TEST_DEPRECATED_NO_dict", + [], + (True, []), + [], + ), + ] +) +@pytest.mark.util +def test_check_for_deprecated_config(metplus_config, + dep_item, + deprecated_dict, + expected, + err_msgs): + + depr_dict = {dep_item: deprecated_dict} + config = metplus_config + config.set('config', dep_item.replace('','2'), 'old value') + config.set('config', 'CONFIG_INPUT', 'dir/config1.conf') + + with mock.patch.object(cv, 'DEPRECATED_DICT', depr_dict): + actual = cv.check_for_deprecated_config(metplus_config) + assert actual == expected + + if err_msgs: + for msg in err_msgs: + config.logger.error.assert_any_call(msg) + else: + config.logger.error.assert_not_called diff --git a/internal/tests/pytests/util/constants/test_constants.py b/internal/tests/pytests/util/constants/test_constants.py new file mode 100644 index 0000000000..d4f9fcd43e --- /dev/null +++ b/internal/tests/pytests/util/constants/test_constants.py @@ -0,0 +1,19 @@ +import pytest +from metplus.util.constants import DEPRECATED_DICT + +@pytest.mark.util +def test_deprecated_dict(): + """ + Test that the structure of DEPRECATED_DICT is as expected. + If not config_validate may give unexpected answers, and + will not necessarily raise an error. + """ + allowed_keys = {'alt','copy','upgrade'} + allowed_upgrade_values = {'ensemble'} + + for _, v in DEPRECATED_DICT.items(): + assert isinstance(v, dict) + assert set(v.keys()).issubset(allowed_keys) + + if 'upgrade' in v.keys(): + assert v['upgrade'] in allowed_upgrade_values