]> git.openstreetmap.org Git - nominatim.git/blob - test/python/test_cli.py
c3625c4af6d12bcd2ec2f050888287518026477f
[nominatim.git] / test / python / test_cli.py
1 """
2 Tests for command line interface wrapper.
3
4 These tests just check that the various command line parameters route to the
5 correct functionionality. They use a lot of monkeypatching to avoid executing
6 the actual functions.
7 """
8 from pathlib import Path
9
10 import pytest
11
12 import nominatim.db.properties
13 import nominatim.cli
14 import nominatim.clicmd.api
15 import nominatim.clicmd.refresh
16 import nominatim.clicmd.admin
17 import nominatim.clicmd.setup
18 import nominatim.indexer.indexer
19 import nominatim.tools.admin
20 import nominatim.tools.check_database
21 import nominatim.tools.database_import
22 import nominatim.tools.freeze
23 import nominatim.tools.refresh
24 import nominatim.tools.postcodes
25 import nominatim.tokenizer.factory
26
27 from mocks import MockParamCapture
28
29 @pytest.fixture
30 def mock_run_legacy(monkeypatch):
31     mock = MockParamCapture()
32     monkeypatch.setattr(nominatim.cli, 'run_legacy_script', mock)
33     return mock
34
35
36 @pytest.fixture
37 def mock_func_factory(monkeypatch):
38     def get_mock(module, func):
39         mock = MockParamCapture()
40         mock.func_name = func
41         monkeypatch.setattr(module, func, mock)
42         return mock
43
44     return get_mock
45
46
47
48 class TestCli:
49
50     @pytest.fixture(autouse=True)
51     def setup_cli_call(self, cli_call):
52         self.call_nominatim = cli_call
53
54
55     def test_cli_help(self, capsys):
56         """ Running nominatim tool without arguments prints help.
57         """
58         assert 1 == self.call_nominatim()
59
60         captured = capsys.readouterr()
61         assert captured.out.startswith('usage:')
62
63
64     @pytest.mark.parametrize("command,script", [
65                              (('add-data', '--file', 'foo.osm'), 'update'),
66                              (('export',), 'export')
67                              ])
68     def test_legacy_commands_simple(self, mock_run_legacy, command, script):
69         assert 0 == self.call_nominatim(*command)
70
71         assert mock_run_legacy.called == 1
72         assert mock_run_legacy.last_args[0] == script + '.php'
73
74
75     @pytest.mark.parametrize("params", [('--warm', ),
76                                         ('--warm', '--reverse-only'),
77                                         ('--warm', '--search-only')])
78     def test_admin_command_legacy(self, mock_func_factory, params):
79         mock_run_legacy = mock_func_factory(nominatim.clicmd.admin, 'run_legacy_script')
80
81         assert 0 == self.call_nominatim('admin', *params)
82
83         assert mock_run_legacy.called == 1
84
85
86     def test_admin_command_check_database(self, mock_func_factory):
87         mock = mock_func_factory(nominatim.tools.check_database, 'check_database')
88
89         assert 0 == self.call_nominatim('admin', '--check-database')
90         assert mock.called == 1
91
92
93     @pytest.mark.parametrize("name,oid", [('file', 'foo.osm'), ('diff', 'foo.osc'),
94                                           ('node', 12), ('way', 8), ('relation', 32)])
95     def test_add_data_command(self, mock_run_legacy, name, oid):
96         assert 0 == self.call_nominatim('add-data', '--' + name, str(oid))
97
98         assert mock_run_legacy.called == 1
99         assert mock_run_legacy.last_args == ('update.php', '--import-' + name, oid)
100
101
102     def test_serve_command(self, mock_func_factory):
103         func = mock_func_factory(nominatim.cli, 'run_php_server')
104
105         self.call_nominatim('serve')
106
107         assert func.called == 1
108
109
110     @pytest.mark.parametrize("params", [
111                              ('search', '--query', 'new'),
112                              ('reverse', '--lat', '0', '--lon', '0'),
113                              ('lookup', '--id', 'N1'),
114                              ('details', '--node', '1'),
115                              ('details', '--way', '1'),
116                              ('details', '--relation', '1'),
117                              ('details', '--place_id', '10001'),
118                              ('status',)
119                              ])
120     def test_api_commands_simple(self, mock_func_factory, params):
121         mock_run_api = mock_func_factory(nominatim.clicmd.api, 'run_api_script')
122
123         assert 0 == self.call_nominatim(*params)
124
125         assert mock_run_api.called == 1
126         assert mock_run_api.last_args[0] == params[0]
127
128
129
130 class TestCliWithDb:
131
132     @pytest.fixture(autouse=True)
133     def setup_cli_call(self, cli_call, temp_db):
134         self.call_nominatim = cli_call
135
136
137     @pytest.fixture(autouse=True)
138     def setup_tokenizer_mock(self, monkeypatch):
139         class DummyTokenizer:
140             def __init__(self, *args, **kwargs):
141                 self.update_sql_functions_called = False
142                 self.finalize_import_called = False
143
144             def update_sql_functions(self, *args):
145                 self.update_sql_functions_called = True
146
147             def finalize_import(self, *args):
148                 self.finalize_import_called = True
149
150         tok = DummyTokenizer()
151         monkeypatch.setattr(nominatim.tokenizer.factory, 'get_tokenizer_for_db' ,
152                             lambda *args: tok)
153         monkeypatch.setattr(nominatim.tokenizer.factory, 'create_tokenizer' ,
154                             lambda *args: tok)
155
156         self.tokenizer_mock = tok
157
158
159     def test_import_missing_file(self):
160         assert 1 == self.call_nominatim('import', '--osm-file', 'sfsafegweweggdgw.reh.erh')
161
162
163     def test_import_bad_file(self):
164         assert 1 == self.call_nominatim('import', '--osm-file', '.')
165
166
167     def test_import_full(self, mock_func_factory):
168         mocks = [
169             mock_func_factory(nominatim.tools.database_import, 'setup_database_skeleton'),
170             mock_func_factory(nominatim.tools.database_import, 'import_osm_data'),
171             mock_func_factory(nominatim.tools.refresh, 'import_wikipedia_articles'),
172             mock_func_factory(nominatim.tools.database_import, 'truncate_data_tables'),
173             mock_func_factory(nominatim.tools.database_import, 'load_data'),
174             mock_func_factory(nominatim.tools.database_import, 'create_tables'),
175             mock_func_factory(nominatim.tools.database_import, 'create_table_triggers'),
176             mock_func_factory(nominatim.tools.database_import, 'create_partition_tables'),
177             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
178             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
179             mock_func_factory(nominatim.tools.refresh, 'load_address_levels_from_file'),
180             mock_func_factory(nominatim.tools.postcodes, 'update_postcodes'),
181             mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_full'),
182             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
183             mock_func_factory(nominatim.db.properties, 'set_property')
184         ]
185
186         cf_mock = mock_func_factory(nominatim.tools.refresh, 'create_functions')
187
188         assert 0 == self.call_nominatim('import', '--osm-file', __file__)
189         assert self.tokenizer_mock.finalize_import_called
190
191         assert cf_mock.called > 1
192
193         for mock in mocks:
194             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
195
196
197     def test_import_continue_load_data(self, mock_func_factory):
198         mocks = [
199             mock_func_factory(nominatim.tools.database_import, 'truncate_data_tables'),
200             mock_func_factory(nominatim.tools.database_import, 'load_data'),
201             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
202             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
203             mock_func_factory(nominatim.tools.postcodes, 'update_postcodes'),
204             mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_full'),
205             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
206             mock_func_factory(nominatim.db.properties, 'set_property')
207         ]
208
209         assert 0 == self.call_nominatim('import', '--continue', 'load-data')
210         assert self.tokenizer_mock.finalize_import_called
211
212         for mock in mocks:
213             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
214
215
216     def test_import_continue_indexing(self, mock_func_factory, placex_table,
217                                       temp_db_conn):
218         mocks = [
219             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
220             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
221             mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_full'),
222             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
223             mock_func_factory(nominatim.db.properties, 'set_property')
224         ]
225
226         assert 0 == self.call_nominatim('import', '--continue', 'indexing')
227
228         for mock in mocks:
229             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
230
231         assert temp_db_conn.index_exists('idx_placex_pendingsector')
232
233         # Calling it again still works for the index
234         assert 0 == self.call_nominatim('import', '--continue', 'indexing')
235         assert temp_db_conn.index_exists('idx_placex_pendingsector')
236
237
238     def test_import_continue_postprocess(self, mock_func_factory):
239         mocks = [
240             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
241             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
242             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
243             mock_func_factory(nominatim.db.properties, 'set_property')
244         ]
245
246         assert 0 == self.call_nominatim('import', '--continue', 'db-postprocess')
247
248         assert self.tokenizer_mock.finalize_import_called
249
250         for mock in mocks:
251             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
252
253
254     def test_freeze_command(self, mock_func_factory):
255         mock_drop = mock_func_factory(nominatim.tools.freeze, 'drop_update_tables')
256         mock_flatnode = mock_func_factory(nominatim.tools.freeze, 'drop_flatnode_file')
257
258         assert 0 == self.call_nominatim('freeze')
259
260         assert mock_drop.called == 1
261         assert mock_flatnode.called == 1
262
263
264
265     @pytest.mark.parametrize("func, params", [('analyse_indexing', ('--analyse-indexing', ))])
266     def test_admin_command_tool(self, mock_func_factory, func, params):
267         mock = mock_func_factory(nominatim.tools.admin, func)
268
269         assert 0 == self.call_nominatim('admin', *params)
270         assert mock.called == 1
271
272
273     @pytest.mark.parametrize("params,do_bnds,do_ranks", [
274                               ([], 1, 1),
275                               (['--boundaries-only'], 1, 0),
276                               (['--no-boundaries'], 0, 1),
277                               (['--boundaries-only', '--no-boundaries'], 0, 0)])
278     def test_index_command(self, mock_func_factory, table_factory,
279                            params, do_bnds, do_ranks):
280         table_factory('import_status', 'indexed bool')
281         bnd_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_boundaries')
282         rank_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_by_rank')
283
284         assert 0 == self.call_nominatim('index', *params)
285
286         assert bnd_mock.called == do_bnds
287         assert rank_mock.called == do_ranks
288
289     @pytest.mark.parametrize("no_replace", [(True), (False)])
290     def test_special_phrases_wiki_command(self, mock_func_factory, no_replace):
291         func = mock_func_factory(nominatim.clicmd.special_phrases.SPImporter, 'import_phrases')
292
293         if no_replace:
294             self.call_nominatim('special-phrases', '--import-from-wiki', '--no-replace')
295         else:
296             self.call_nominatim('special-phrases', '--import-from-wiki')
297
298         assert func.called == 1
299
300     @pytest.mark.parametrize("no_replace", [(True), (False)])
301     def test_special_phrases_csv_command(self, src_dir, mock_func_factory, no_replace):
302         func = mock_func_factory(nominatim.clicmd.special_phrases.SPImporter, 'import_phrases')
303         testdata = src_dir / 'test' / 'testdb'
304         csv_path = str((testdata / 'full_en_phrases_test.csv').resolve())
305
306         if no_replace:
307             self.call_nominatim('special-phrases', '--import-from-csv', csv_path, '--no-replace')
308         else:
309             self.call_nominatim('special-phrases', '--import-from-csv', csv_path)
310
311         assert func.called == 1
312
313     @pytest.mark.parametrize("command,func", [
314                              ('word-counts', 'recompute_word_counts'),
315                              ('address-levels', 'load_address_levels_from_file'),
316                              ('wiki-data', 'import_wikipedia_articles'),
317                              ('importance', 'recompute_importance'),
318                              ('website', 'setup_website'),
319                              ])
320     def test_refresh_command(self, mock_func_factory, command, func):
321         func_mock = mock_func_factory(nominatim.tools.refresh, func)
322
323         assert 0 == self.call_nominatim('refresh', '--' + command)
324         assert func_mock.called == 1
325
326
327     def test_refresh_postcodes(self, mock_func_factory):
328         func_mock = mock_func_factory(nominatim.tools.postcodes, 'update_postcodes')
329         idx_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_postcodes')
330
331         assert 0 == self.call_nominatim('refresh', '--postcodes')
332         assert func_mock.called == 1
333
334     def test_refresh_create_functions(self, mock_func_factory):
335         func_mock = mock_func_factory(nominatim.tools.refresh, 'create_functions')
336
337         assert 0 == self.call_nominatim('refresh', '--functions')
338         assert func_mock.called == 1
339         assert self.tokenizer_mock.update_sql_functions_called
340
341
342     def test_refresh_importance_computed_after_wiki_import(self, monkeypatch):
343         calls = []
344         monkeypatch.setattr(nominatim.tools.refresh, 'import_wikipedia_articles',
345                             lambda *args, **kwargs: calls.append('import') or 0)
346         monkeypatch.setattr(nominatim.tools.refresh, 'recompute_importance',
347                             lambda *args, **kwargs: calls.append('update'))
348
349         assert 0 == self.call_nominatim('refresh', '--importance', '--wiki-data')
350
351         assert calls == ['import', 'update']
352
353
354