2 Test for loading dotenv configuration.
4 from pathlib import Path
7 from nominatim.config import Configuration, flatten_config_list
8 from nominatim.errors import UsageError
11 def make_config(src_dir):
12 """ Create a configuration object from the given project directory.
14 def _mk_config(project_dir=None):
15 return Configuration(project_dir, src_dir / 'settings')
20 def make_config_path(src_dir, tmp_path):
21 """ Create a configuration object with project and config directories
22 in a temporary directory.
25 (tmp_path / 'project').mkdir()
26 (tmp_path / 'config').mkdir()
27 conf = Configuration(tmp_path / 'project', src_dir / 'settings')
28 conf.config_dir = tmp_path / 'config'
34 def test_no_project_dir(make_config):
35 config = make_config()
37 assert config.DATABASE_WEBUSER == 'www-data'
40 @pytest.mark.parametrize("val", ('apache', '"apache"'))
41 def test_prefer_project_setting_over_default(make_config, val, tmp_path):
42 envfile = tmp_path / '.env'
43 envfile.write_text('NOMINATIM_DATABASE_WEBUSER={}\n'.format(val))
45 config = make_config(tmp_path)
47 assert config.DATABASE_WEBUSER == 'apache'
50 def test_prefer_os_environ_over_project_setting(make_config, monkeypatch, tmp_path):
51 envfile = tmp_path / '.env'
52 envfile.write_text('NOMINATIM_DATABASE_WEBUSER=apache\n')
54 monkeypatch.setenv('NOMINATIM_DATABASE_WEBUSER', 'nobody')
56 config = make_config(tmp_path)
58 assert config.DATABASE_WEBUSER == 'nobody'
61 def test_prefer_os_environ_can_unset_project_setting(make_config, monkeypatch, tmp_path):
62 envfile = tmp_path / '.env'
63 envfile.write_text('NOMINATIM_DATABASE_WEBUSER=apache\n')
65 monkeypatch.setenv('NOMINATIM_DATABASE_WEBUSER', '')
67 config = make_config(tmp_path)
69 assert config.DATABASE_WEBUSER == ''
72 def test_get_os_env_add_defaults(make_config, monkeypatch):
73 config = make_config()
75 monkeypatch.delenv('NOMINATIM_DATABASE_WEBUSER', raising=False)
77 assert config.get_os_env()['NOMINATIM_DATABASE_WEBUSER'] == 'www-data'
80 def test_get_os_env_prefer_os_environ(make_config, monkeypatch):
81 config = make_config()
83 monkeypatch.setenv('NOMINATIM_DATABASE_WEBUSER', 'nobody')
85 assert config.get_os_env()['NOMINATIM_DATABASE_WEBUSER'] == 'nobody'
88 def test_get_libpq_dsn_convert_default(make_config):
89 config = make_config()
91 assert config.get_libpq_dsn() == 'dbname=nominatim'
94 def test_get_libpq_dsn_convert_php(make_config, monkeypatch):
95 config = make_config()
97 monkeypatch.setenv('NOMINATIM_DATABASE_DSN',
98 'pgsql:dbname=gis;password=foo;host=localhost')
100 assert config.get_libpq_dsn() == 'dbname=gis password=foo host=localhost'
103 @pytest.mark.parametrize("val,expect", [('foo bar', "'foo bar'"),
106 def test_get_libpq_dsn_convert_php_special_chars(make_config, monkeypatch, val, expect):
107 config = make_config()
109 monkeypatch.setenv('NOMINATIM_DATABASE_DSN',
110 'pgsql:dbname=gis;password={}'.format(val))
112 assert config.get_libpq_dsn() == "dbname=gis password={}".format(expect)
115 def test_get_libpq_dsn_convert_libpq(make_config, monkeypatch):
116 config = make_config()
118 monkeypatch.setenv('NOMINATIM_DATABASE_DSN',
119 'host=localhost dbname=gis password=foo')
121 assert config.get_libpq_dsn() == 'host=localhost dbname=gis password=foo'
124 @pytest.mark.parametrize("value,result",
125 [(x, True) for x in ('1', 'true', 'True', 'yes', 'YES')] +
126 [(x, False) for x in ('0', 'false', 'no', 'NO', 'x')])
127 def test_get_bool(make_config, monkeypatch, value, result):
128 config = make_config()
130 monkeypatch.setenv('NOMINATIM_FOOBAR', value)
132 assert config.get_bool('FOOBAR') == result
134 def test_get_bool_empty(make_config):
135 config = make_config()
137 assert config.DATABASE_MODULE_PATH == ''
138 assert not config.get_bool('DATABASE_MODULE_PATH')
141 @pytest.mark.parametrize("value,result", [('0', 0), ('1', 1),
142 ('85762513444', 85762513444)])
143 def test_get_int_success(make_config, monkeypatch, value, result):
144 config = make_config()
146 monkeypatch.setenv('NOMINATIM_FOOBAR', value)
148 assert config.get_int('FOOBAR') == result
151 @pytest.mark.parametrize("value", ['1b', 'fg', '0x23'])
152 def test_get_int_bad_values(make_config, monkeypatch, value):
153 config = make_config()
155 monkeypatch.setenv('NOMINATIM_FOOBAR', value)
157 with pytest.raises(UsageError):
158 config.get_int('FOOBAR')
161 def test_get_int_empty(make_config):
162 config = make_config()
164 assert config.DATABASE_MODULE_PATH == ''
166 with pytest.raises(UsageError):
167 config.get_int('DATABASE_MODULE_PATH')
170 def test_get_path_empty(make_config):
171 config = make_config()
173 assert config.DATABASE_MODULE_PATH == ''
174 assert not config.get_path('DATABASE_MODULE_PATH')
177 def test_get_path_absolute(make_config, monkeypatch):
178 config = make_config()
180 monkeypatch.setenv('NOMINATIM_FOOBAR', '/dont/care')
181 result = config.get_path('FOOBAR')
183 assert isinstance(result, Path)
184 assert str(result) == '/dont/care'
187 def test_get_path_relative(make_config, monkeypatch, tmp_path):
188 config = make_config(tmp_path)
190 monkeypatch.setenv('NOMINATIM_FOOBAR', 'an/oyster')
191 result = config.get_path('FOOBAR')
193 assert isinstance(result, Path)
194 assert str(result) == str(tmp_path / 'an/oyster')
197 def test_get_import_style_intern(make_config, src_dir, monkeypatch):
198 config = make_config()
200 monkeypatch.setenv('NOMINATIM_IMPORT_STYLE', 'street')
202 expected = src_dir / 'settings' / 'import-street.style'
204 assert config.get_import_style_file() == expected
207 def test_get_import_style_extern_relative(make_config_path, monkeypatch):
208 config = make_config_path()
209 (config.project_dir / 'custom.style').write_text('x')
211 monkeypatch.setenv('NOMINATIM_IMPORT_STYLE', 'custom.style')
213 assert str(config.get_import_style_file()) == str(config.project_dir / 'custom.style')
216 def test_get_import_style_extern_absolute(make_config, tmp_path, monkeypatch):
217 config = make_config()
218 cfgfile = tmp_path / 'test.style'
220 cfgfile.write_text('x')
222 monkeypatch.setenv('NOMINATIM_IMPORT_STYLE', str(cfgfile))
224 assert str(config.get_import_style_file()) == str(cfgfile)
227 def test_load_subconf_from_project_dir(make_config_path):
228 config = make_config_path()
230 testfile = config.project_dir / 'test.yaml'
231 testfile.write_text('cow: muh\ncat: miau\n')
233 testfile = config.config_dir / 'test.yaml'
234 testfile.write_text('cow: miau\ncat: muh\n')
236 rules = config.load_sub_configuration('test.yaml')
238 assert rules == dict(cow='muh', cat='miau')
241 def test_load_subconf_from_settings_dir(make_config_path):
242 config = make_config_path()
244 testfile = config.config_dir / 'test.yaml'
245 testfile.write_text('cow: muh\ncat: miau\n')
247 rules = config.load_sub_configuration('test.yaml')
249 assert rules == dict(cow='muh', cat='miau')
252 def test_load_subconf_empty_env_conf(make_config_path, monkeypatch):
253 monkeypatch.setenv('NOMINATIM_MY_CONFIG', '')
254 config = make_config_path()
256 testfile = config.config_dir / 'test.yaml'
257 testfile.write_text('cow: muh\ncat: miau\n')
259 rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG')
261 assert rules == dict(cow='muh', cat='miau')
264 def test_load_subconf_env_absolute_found(make_config_path, monkeypatch, tmp_path):
265 monkeypatch.setenv('NOMINATIM_MY_CONFIG', str(tmp_path / 'other.yaml'))
266 config = make_config_path()
268 (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n')
269 (tmp_path / 'other.yaml').write_text('dog: muh\nfrog: miau\n')
271 rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG')
273 assert rules == dict(dog='muh', frog='miau')
276 def test_load_subconf_env_absolute_not_found(make_config_path, monkeypatch, tmp_path):
277 monkeypatch.setenv('NOMINATIM_MY_CONFIG', str(tmp_path / 'other.yaml'))
278 config = make_config_path()
280 (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n')
282 with pytest.raises(UsageError, match='Config file not found.'):
283 rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG')
286 @pytest.mark.parametrize("location", ['project_dir', 'config_dir'])
287 def test_load_subconf_env_relative_found(make_config_path, monkeypatch, location):
288 monkeypatch.setenv('NOMINATIM_MY_CONFIG', 'other.yaml')
289 config = make_config_path()
291 (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n')
292 (getattr(config, location) / 'other.yaml').write_text('dog: bark\n')
294 rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG')
296 assert rules == dict(dog='bark')
299 def test_load_subconf_env_relative_not_found(make_config_path, monkeypatch):
300 monkeypatch.setenv('NOMINATIM_MY_CONFIG', 'other.yaml')
301 config = make_config_path()
303 (config.config_dir / 'test.yaml').write_text('cow: muh\ncat: miau\n')
305 with pytest.raises(UsageError, match='Config file not found.'):
306 rules = config.load_sub_configuration('test.yaml', config='MY_CONFIG')
309 def test_load_subconf_json(make_config_path):
310 config = make_config_path()
312 (config.project_dir / 'test.json').write_text('{"cow": "muh", "cat": "miau"}')
314 rules = config.load_sub_configuration('test.json')
316 assert rules == dict(cow='muh', cat='miau')
318 def test_load_subconf_not_found(make_config_path):
319 config = make_config_path()
321 with pytest.raises(UsageError, match='Config file not found.'):
322 config.load_sub_configuration('test.yaml')
325 def test_load_subconf_env_unknown_format(make_config_path):
326 config = make_config_path()
328 (config.project_dir / 'test.xml').write_text('<html></html>')
330 with pytest.raises(UsageError, match='unknown format'):
331 config.load_sub_configuration('test.xml')
334 def test_load_subconf_include_absolute(make_config_path, tmp_path):
335 config = make_config_path()
337 testfile = config.config_dir / 'test.yaml'
338 testfile.write_text(f'base: !include {tmp_path}/inc.yaml\n')
339 (tmp_path / 'inc.yaml').write_text('first: 1\nsecond: 2\n')
341 rules = config.load_sub_configuration('test.yaml')
343 assert rules == dict(base=dict(first=1, second=2))
346 @pytest.mark.parametrize("location", ['project_dir', 'config_dir'])
347 def test_load_subconf_include_relative(make_config_path, tmp_path, location):
348 config = make_config_path()
350 testfile = config.config_dir / 'test.yaml'
351 testfile.write_text(f'base: !include inc.yaml\n')
352 (getattr(config, location) / 'inc.yaml').write_text('first: 1\nsecond: 2\n')
354 rules = config.load_sub_configuration('test.yaml')
356 assert rules == dict(base=dict(first=1, second=2))
359 def test_load_subconf_include_bad_format(make_config_path):
360 config = make_config_path()
362 testfile = config.config_dir / 'test.yaml'
363 testfile.write_text(f'base: !include inc.txt\n')
364 (config.config_dir / 'inc.txt').write_text('first: 1\nsecond: 2\n')
366 with pytest.raises(UsageError, match='Cannot handle config file format.'):
367 rules = config.load_sub_configuration('test.yaml')
370 def test_load_subconf_include_not_found(make_config_path):
371 config = make_config_path()
373 testfile = config.config_dir / 'test.yaml'
374 testfile.write_text(f'base: !include inc.txt\n')
376 with pytest.raises(UsageError, match='Config file not found.'):
377 rules = config.load_sub_configuration('test.yaml')
380 def test_load_subconf_include_recursive(make_config_path):
381 config = make_config_path()
383 testfile = config.config_dir / 'test.yaml'
384 testfile.write_text(f'base: !include inc.yaml\n')
385 (config.config_dir / 'inc.yaml').write_text('- !include more.yaml\n- upper\n')
386 (config.config_dir / 'more.yaml').write_text('- the end\n')
388 rules = config.load_sub_configuration('test.yaml')
390 assert rules == dict(base=[['the end'], 'upper'])
393 @pytest.mark.parametrize("content", [[], None])
394 def test_flatten_config_list_empty(content):
395 assert flatten_config_list(content) == []
398 @pytest.mark.parametrize("content", [{'foo': 'bar'}, 'hello world', 3])
399 def test_flatten_config_list_no_list(content):
400 with pytest.raises(UsageError):
401 flatten_config_list(content)
404 def test_flatten_config_list_allready_flat():
405 assert flatten_config_list([1, 2, 456]) == [1, 2, 456]
408 def test_flatten_config_list_nested():
411 [{'first': '1st', 'second': '2nd'}, {}],
412 [[2, 3], [45, [56, 78], 66]],
415 assert flatten_config_list(content) == \
416 [34, {'first': '1st', 'second': '2nd'}, {},
417 2, 3, 45, 56, 78, 66, 'end']