From 28c98584c1f8bed4aa2c2fc22956f7a0f5fc8f3b Mon Sep 17 00:00:00 2001 From: Sarah Hoffmann Date: Fri, 3 Sep 2021 22:31:30 +0200 Subject: [PATCH] add tests for generic YAML config reader --- nominatim/config.py | 30 +++---- test/python/test_config.py | 173 +++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 15 deletions(-) diff --git a/nominatim/config.py b/nominatim/config.py index c859a9d1..5d56d024 100644 --- a/nominatim/config.py +++ b/nominatim/config.py @@ -54,7 +54,10 @@ class Configuration: def __getattr__(self, name): name = 'NOMINATIM_' + name - return self.environ.get(name) or self._config[name] + if name in self.environ: + return self.environ[name] + + return self._config[name] def get_bool(self, name): """ Return the given configuration parameter as a boolean. @@ -137,12 +140,9 @@ class Configuration: is loaded using this function and added at the position in the configuration tree. """ - configfile = self._find_config_file(filename, config) + assert Path(filename).suffix == '.yaml' - if configfile.suffix != '.yaml': - LOG.format("Format error while reading '%s': only YAML format supported.", - configfile) - raise UsageError("Cannot handle config file format.") + configfile = self._find_config_file(filename, config) return self._load_from_yaml(configfile) @@ -158,16 +158,16 @@ class Configuration: if cfg_filename: cfg_filename = Path(cfg_filename) - if not cfg_filename.is_absolute(): - cfg_filename = self.project_dir / cfg_filename + if cfg_filename.is_absolute(): + cfg_filename = cfg_filename.resolve() - cfg_filename = cfg_filename.resolve() + if not cfg_filename.is_file(): + LOG.fatal("Cannot find config file '%s'.", cfg_filename) + raise UsageError("Config file not found.") - if not cfg_filename.is_file(): - LOG.fatal("Cannot find config file '%s'.", cfg_filename) - raise UsageError("Config file not found.") + return cfg_filename - return cfg_filename + filename = cfg_filename search_paths = [self.project_dir, self.config_dir] @@ -203,8 +203,8 @@ class Configuration: configfile = self._find_config_file(loader.construct_scalar(node)) if configfile.suffix != '.yaml': - LOG.format("Format error while reading '%s': only YAML format supported.", - configfile) + LOG.fatal("Format error while reading '%s': only YAML format supported.", + configfile) raise UsageError("Cannot handle config file format.") return yaml.safe_load(configfile.read_text(encoding='utf-8')) diff --git a/test/python/test_config.py b/test/python/test_config.py index 6729f954..8b5cb11b 100644 --- a/test/python/test_config.py +++ b/test/python/test_config.py @@ -15,6 +15,20 @@ def make_config(src_dir): return _mk_config +@pytest.fixture +def make_config_path(src_dir, tmp_path): + """ Create a configuration object with project and config directories + in a temporary directory. + """ + def _mk_config(): + (tmp_path / 'project').mkdir() + (tmp_path / 'config').mkdir() + conf = Configuration(tmp_path / 'project', src_dir / 'settings') + conf.config_dir = tmp_path / 'config' + return conf + + return _mk_config + def test_no_project_dir(make_config): config = make_config() @@ -43,6 +57,17 @@ def test_prefer_os_environ_over_project_setting(make_config, monkeypatch, tmp_pa assert config.DATABASE_WEBUSER == 'nobody' +def test_prefer_os_environ_can_unset_project_setting(make_config, monkeypatch, tmp_path): + envfile = tmp_path / '.env' + envfile.write_text('NOMINATIM_DATABASE_WEBUSER=apache\n') + + monkeypatch.setenv('NOMINATIM_DATABASE_WEBUSER', '') + + config = make_config(tmp_path) + + assert config.DATABASE_WEBUSER == '' + + def test_get_os_env_add_defaults(make_config, monkeypatch): config = make_config() @@ -158,3 +183,151 @@ def test_get_import_style_extern(make_config, monkeypatch, value): monkeypatch.setenv('NOMINATIM_IMPORT_STYLE', value) assert str(config.get_import_style_file()) == value + + +def test_load_subconf_from_project_dir(make_config_path): + config = make_config_path() + + testfile = config.project_dir / 'test.yaml' + testfile.write_text('cow: muh\ncat: miau\n') + + testfile = config.config_dir / 'test.yaml' + testfile.write_text('cow: miau\ncat: muh\n') + + rules = config.load_sub_configuration('test.yaml') + + assert rules == dict(cow='muh', cat='miau') + + +def test_load_subconf_from_settings_dir(make_config_path): + config = make_config_path() + + testfile = config.config_dir / 'test.yaml' + testfile.write_text('cow: muh\ncat: miau\n') + + rules = config.load_sub_configuration('test.yaml') + + assert rules == dict(cow='muh', cat='miau') + + +def test_load_subconf_empty_env_conf(make_config_path, monkeypatch): + monkeypatch.setenv('NOMINATIM_MY_CONFIG', '') + config = make_config_path() + + testfile = config.config_dir / 'test.yaml' + testfile.write_text('cow: muh\ncat: miau\n') + + rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG') + + assert rules == dict(cow='muh', cat='miau') + + +def test_load_subconf_env_absolute_found(make_config_path, monkeypatch, tmp_path): + monkeypatch.setenv('NOMINATIM_MY_CONFIG', str(tmp_path / 'other.yaml')) + config = make_config_path() + + (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n') + (tmp_path / 'other.yaml').write_text('dog: muh\nfrog: miau\n') + + rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG') + + assert rules == dict(dog='muh', frog='miau') + + +def test_load_subconf_env_absolute_not_found(make_config_path, monkeypatch, tmp_path): + monkeypatch.setenv('NOMINATIM_MY_CONFIG', str(tmp_path / 'other.yaml')) + config = make_config_path() + + (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n') + + with pytest.raises(UsageError, match='Config file not found.'): + rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG') + + +@pytest.mark.parametrize("location", ['project_dir', 'config_dir']) +def test_load_subconf_env_relative_found(make_config_path, monkeypatch, location): + monkeypatch.setenv('NOMINATIM_MY_CONFIG', 'other.yaml') + config = make_config_path() + + (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n') + (getattr(config, location) / 'other.yaml').write_text('dog: bark\n') + + rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG') + + assert rules == dict(dog='bark') + + +def test_load_subconf_env_relative_not_found(make_config_path, monkeypatch): + monkeypatch.setenv('NOMINATIM_MY_CONFIG', 'other.yaml') + config = make_config_path() + + (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n') + + with pytest.raises(UsageError, match='Config file not found.'): + rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG') + + +def test_load_subconf_not_found(make_config_path): + config = make_config_path() + + with pytest.raises(UsageError, match='Config file not found.'): + rules = config.load_sub_configuration('test.yaml') + + +def test_load_subconf_include_absolute(make_config_path, tmp_path): + config = make_config_path() + + testfile = config.config_dir / 'test.yaml' + testfile.write_text(f'base: !include {tmp_path}/inc.yaml\n') + (tmp_path / 'inc.yaml').write_text('first: 1\nsecond: 2\n') + + rules = config.load_sub_configuration('test.yaml') + + assert rules == dict(base=dict(first=1, second=2)) + + +@pytest.mark.parametrize("location", ['project_dir', 'config_dir']) +def test_load_subconf_include_relative(make_config_path, tmp_path, location): + config = make_config_path() + + testfile = config.config_dir / 'test.yaml' + testfile.write_text(f'base: !include inc.yaml\n') + (getattr(config, location) / 'inc.yaml').write_text('first: 1\nsecond: 2\n') + + rules = config.load_sub_configuration('test.yaml') + + assert rules == dict(base=dict(first=1, second=2)) + + +def test_load_subconf_include_bad_format(make_config_path): + config = make_config_path() + + testfile = config.config_dir / 'test.yaml' + testfile.write_text(f'base: !include inc.txt\n') + (config.config_dir / 'inc.txt').write_text('first: 1\nsecond: 2\n') + + with pytest.raises(UsageError, match='Cannot handle config file format.'): + rules = config.load_sub_configuration('test.yaml') + + +def test_load_subconf_include_not_found(make_config_path): + config = make_config_path() + + testfile = config.config_dir / 'test.yaml' + testfile.write_text(f'base: !include inc.txt\n') + + with pytest.raises(UsageError, match='Config file not found.'): + rules = config.load_sub_configuration('test.yaml') + + +def test_load_subconf_include_recursive(make_config_path): + config = make_config_path() + + testfile = config.config_dir / 'test.yaml' + testfile.write_text(f'base: !include inc.yaml\n') + (config.config_dir / 'inc.yaml').write_text('- !include more.yaml\n- upper\n') + (config.config_dir / 'more.yaml').write_text('- the end\n') + + rules = config.load_sub_configuration('test.yaml') + + assert rules == dict(base=[['the end'], 'upper'])