From ab0a659c186afe74c72457a76f4f1f6124c38c38 Mon Sep 17 00:00:00 2001 From: HaimHorowitzAgnostiq <98767279+HaimHorowitzAgnostiq@users.noreply.github.com> Date: Fri, 11 Feb 2022 09:40:05 -0500 Subject: [PATCH] Tests for config file (#99) * fixes issue #38 * added example tests - one failing * added more tests * added more tests * update purge config * added another test * start stub for get config test * added test for get_config * added a new test for write_config * update init tests * add test for config manager init * updated CHANGELOG and version, added function descriptions to config_test * update minor formatting * update changelog * update changelog Co-authored-by: Faiyaz Hasan --- CHANGELOG.md | 6 + VERSION | 2 +- covalent/_shared_files/config.py | 3 +- tests/covalent_tests/shared_files/__init__.py | 0 .../shared_files/config_test.py | 195 +++++++++++++++++- 5 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 tests/covalent_tests/shared_files/__init__.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f8856ce..fe0d9160c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.24.13] - 2022-02-11 + +### Added + +- Tests for covalent/_shared_files/config.py + ## [0.24.12] - 2022-02-10 ### Added diff --git a/VERSION b/VERSION index 61c77acea..43437b897 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.24.12 +0.24.13 diff --git a/covalent/_shared_files/config.py b/covalent/_shared_files/config.py index 81f5c1e42..94f5b1e89 100644 --- a/covalent/_shared_files/config.py +++ b/covalent/_shared_files/config.py @@ -134,7 +134,8 @@ def purge_config(self) -> None: None """ - shutil.rmtree(os.path.dirname(self.config_file), ignore_errors=True) + dir_name = os.path.dirname(self.config_file) + shutil.rmtree(dir_name, ignore_errors=True) def get(self, key: str) -> Any: """ diff --git a/tests/covalent_tests/shared_files/__init__.py b/tests/covalent_tests/shared_files/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/covalent_tests/shared_files/config_test.py b/tests/covalent_tests/shared_files/config_test.py index b717debf1..bb3a2b2a4 100644 --- a/tests/covalent_tests/shared_files/config_test.py +++ b/tests/covalent_tests/shared_files/config_test.py @@ -19,16 +19,82 @@ # Relief from the License may be granted by purchasing a commercial license. import os +import tempfile from unittest.mock import patch -from covalent._shared_files.config import _ConfigManager +import pytest +import toml + +from covalent._shared_files.config import _ConfigManager, get_config, reload_config, set_config +from covalent._shared_files.defaults import _DEFAULT_CONFIG CONFIG_DIR = os.path.join(os.path.dirname(__file__), "test_files") +@pytest.mark.parametrize( + "dir_env,conf_dir", + [ + ("COVALENT_CONFIG_DIR", "covalent/covalent.conf"), + ("XDG_CONFIG_DIR", "covalent/covalent.conf"), + ("HOME", ".config/covalent/covalent.conf"), + ], +) +def test_config_manager_init_directory_setting(monkeypatch, dir_env, conf_dir): + """Test the init method for the config manager.""" + + with tempfile.TemporaryDirectory() as tmp_dir: + monkeypatch.setenv(dir_env, tmp_dir) + cm = _ConfigManager() + assert cm.config_file == f"{tmp_dir}/{conf_dir}" + + +@pytest.mark.parametrize( + "path_exists,write_config_called,update_config_called", + [(False, True, False), (True, False, True)], +) +def test_config_manager_init_write_update_config( + mocker, monkeypatch, path_exists, write_config_called, update_config_called +): + """Test the init method for the config manager.""" + + config_keys = [ + "sdk.log_dir", + "dispatcher.cache_dir", + "dispatcher.results_dir", + "dispatcher.log_dir", + "user_interface.log_dir", + ] + + with tempfile.TemporaryDirectory() as tmp_dir: + monkeypatch.setenv("COVALENT_CONFIG_DIR", tmp_dir) + update_config_mock = mocker.patch( + "covalent._shared_files.config._ConfigManager.update_config" + ) + write_config_mock = mocker.patch( + "covalent._shared_files.config._ConfigManager.write_config" + ) + get_mock = mocker.patch( + "covalent._shared_files.config._ConfigManager.get", side_effect=config_keys + ) + path_mock = mocker.patch("pathlib.Path.__init__", return_value=None) + + mocker.patch("os.path.exists", return_value=path_exists) + + cm = _ConfigManager() + assert hasattr(cm, "config_data") + assert write_config_mock.called is write_config_called + assert update_config_mock.called is update_config_called + + get_mock_calls = get_mock.mock_calls + path_mock_calls = path_mock.mock_calls + + for key in config_keys: + assert mocker.call(key) in get_mock_calls and path_mock_calls + + @patch.dict(os.environ, {"COVALENT_CONFIG_DIR": CONFIG_DIR}, clear=True) def test_read_config(): - """Test that configuration file is properly read""" + """Test that configuration file is read properly.""" config_manager = _ConfigManager() config_manager.read_config() @@ -57,7 +123,7 @@ def test_read_config(): @patch.dict(os.environ, {"COVALENT_CONFIG_DIR": CONFIG_DIR}, clear=True) def test_update_config(): - """Test that updating the existing config data with the config file works""" + """Test that updating the existing config data with the config file works.""" config_manager = _ConfigManager() config_manager.config_data = { @@ -125,3 +191,126 @@ def test_update_config(): } assert config_manager.config_data == expected_dict + + +def test_set_config_str_key(mocker): + """Test the set_config method when the input is a string.""" + + cm_set_mock = mocker.patch("covalent._shared_files.config._config_manager.set") + cm_write_config = mocker.patch("covalent._shared_files.config._config_manager.write_config") + set_config("mock_section.mock_variable", "mock_value") + cm_set_mock.assert_called_once_with("mock_section.mock_variable", "mock_value") + cm_write_config.assert_called_once_with() + + +def test_set_config_dict_key(mocker): + """Test the set_config method when the input is a dictionary.""" + + cm_set_mock = mocker.patch("covalent._shared_files.config._config_manager.set") + cm_write_config = mocker.patch("covalent._shared_files.config._config_manager.write_config") + set_config({"mock_section.mock_variable": "mock_value"}) + cm_set_mock.assert_called_once_with("mock_section.mock_variable", "mock_value") + cm_write_config.assert_called_once_with() + + +def test_generate_default_config(mocker): + """Tests that the default configuration was loaded.""" + + cm = _ConfigManager() + cm_deepcopy_mock = mocker.patch("covalent._shared_files.config.copy.deepcopy", return_value={}) + + cm.generate_default_config() + cm_deepcopy_mock.assert_called_once_with(_DEFAULT_CONFIG) + assert cm.config_data == _DEFAULT_CONFIG + + +def test_read_config(mocker): + """Test the read_config method for the config manager.""" + + cm = _ConfigManager() + test_data = {"test": "test"} + toml_load_mock = mocker.patch( + "covalent._shared_files.config.toml.load", return_value=test_data + ) + cm.read_config() + toml_load_mock.assert_called_with(cm.config_file) + assert cm.config_data == test_data + + +def test_get(): + """Test the get method for the config manager.""" + + cm = _ConfigManager() + + assert cm.get("dispatcher.port") == cm.config_data["dispatcher"]["port"] + + +def test_generate_default_config(): + """Test that the default configuration was loaded.""" + + cm = _ConfigManager() + cm.generate_default_config() + assert cm.config_data == _DEFAULT_CONFIG + assert cm.config_data is not _DEFAULT_CONFIG + + +def test_reload_config(mocker): + """Test the reload_config method.""" + + cm_read_config = mocker.patch("covalent._shared_files.config._config_manager.read_config") + reload_config() + cm_read_config.assert_called_once_with() + + +def test_purge_config(mocker): + """Test the purge_config method for config manager.""" + + cm = _ConfigManager() + os_dir_mock = mocker.patch( + "covalent._shared_files.config.os.path.dirname", return_value="mock_dir" + ) + rmtree_mock = mocker.patch("covalent._shared_files.config.shutil.rmtree") + cm.purge_config() + os_dir_mock.assert_called_once_with(cm.config_file) + rmtree_mock.assert_called_once_with("mock_dir", ignore_errors=True) + + +def test_get_config(): + """Test config retrieval function.""" + + from covalent._shared_files.config import _config_manager + + # Case 1 - Empty list + assert get_config(entries=[]) == _config_manager.config_data + + # Case 2 - List with one item + assert ( + get_config(entries=["dispatcher.port"]) + == _config_manager.config_data["dispatcher"]["port"] + ) + + # Case 3 - String + assert ( + get_config(entries="dispatcher.port") == _config_manager.config_data["dispatcher"]["port"] + ) + + # Case 4 - List with > 1 items + + test_list = ["dispatcher.address", "dispatcher.port"] + + assert get_config(entries=test_list) == { + "dispatcher.address": _config_manager.config_data["dispatcher"]["address"], + "dispatcher.port": _config_manager.config_data["dispatcher"]["port"], + } + + +def test_write_config(mocker): + """Test the write_config method for config manager.""" + + cm = _ConfigManager() + toml_dump_mock = mocker.patch("covalent._shared_files.config.toml.dump") + open_mock = mocker.patch("covalent._shared_files.config.open") + mock_file = open_mock.return_value.__enter__.return_value + cm.write_config() + toml_dump_mock.assert_called_once_with(cm.config_data, mock_file) + open_mock.assert_called_once_with(cm.config_file, "w")