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