]> git.openstreetmap.org Git - nominatim.git/blob - test/python/test_cli.py
03325b8da64e78d4907479d52ec7d097ec844340
[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 datetime as dt
9 import time
10 from pathlib import Path
11
12 import psycopg2
13 import pytest
14
15 import nominatim.cli
16 import nominatim.clicmd.api
17 import nominatim.clicmd.refresh
18 import nominatim.clicmd.admin
19 import nominatim.indexer.indexer
20 import nominatim.tools.admin
21 import nominatim.tools.check_database
22 import nominatim.tools.freeze
23 import nominatim.tools.refresh
24 import nominatim.tools.replication
25 from nominatim.errors import UsageError
26 from nominatim.db import status
27
28 SRC_DIR = (Path(__file__) / '..' / '..' / '..').resolve()
29
30 def call_nominatim(*args):
31     return nominatim.cli.nominatim(module_dir='build/module',
32                                    osm2pgsql_path='build/osm2pgsql/osm2pgsql',
33                                    phplib_dir=str(SRC_DIR / 'lib-php'),
34                                    data_dir=str(SRC_DIR / 'data'),
35                                    phpcgi_path='/usr/bin/php-cgi',
36                                    sqllib_dir=str(SRC_DIR / 'lib-sql'),
37                                    config_dir=str(SRC_DIR / 'settings'),
38                                    cli_args=args)
39
40 class MockParamCapture:
41     """ Mock that records the parameters with which a function was called
42         as well as the number of calls.
43     """
44     def __init__(self, retval=0):
45         self.called = 0
46         self.return_value = retval
47
48     def __call__(self, *args, **kwargs):
49         self.called += 1
50         self.last_args = args
51         self.last_kwargs = kwargs
52         return self.return_value
53
54 @pytest.fixture
55 def mock_run_legacy(monkeypatch):
56     mock = MockParamCapture()
57     monkeypatch.setattr(nominatim.cli, 'run_legacy_script', mock)
58     return mock
59
60 @pytest.fixture
61 def mock_func_factory(monkeypatch):
62     def get_mock(module, func):
63         mock = MockParamCapture()
64         monkeypatch.setattr(module, func, mock)
65         return mock
66
67     return get_mock
68
69 def test_cli_help(capsys):
70     """ Running nominatim tool without arguments prints help.
71     """
72     assert 1 == call_nominatim()
73
74     captured = capsys.readouterr()
75     assert captured.out.startswith('usage:')
76
77
78 @pytest.mark.parametrize("command,script", [
79                          (('import', '--continue', 'load-data'), 'setup'),
80                          (('special-phrases',), 'specialphrases'),
81                          (('add-data', '--tiger-data', 'tiger'), 'setup'),
82                          (('add-data', '--file', 'foo.osm'), 'update'),
83                          (('export',), 'export')
84                          ])
85 def test_legacy_commands_simple(mock_run_legacy, command, script):
86     assert 0 == call_nominatim(*command)
87
88     assert mock_run_legacy.called == 1
89     assert mock_run_legacy.last_args[0] == script + '.php'
90
91
92 def test_freeze_command(mock_func_factory, temp_db):
93     mock_drop = mock_func_factory(nominatim.tools.freeze, 'drop_update_tables')
94     mock_flatnode = mock_func_factory(nominatim.tools.freeze, 'drop_flatnode_file')
95
96     assert 0 == call_nominatim('freeze')
97
98     assert mock_drop.called == 1
99     assert mock_flatnode.called == 1
100
101
102 @pytest.mark.parametrize("params", [('--warm', ),
103                                     ('--warm', '--reverse-only'),
104                                     ('--warm', '--search-only')])
105 def test_admin_command_legacy(mock_func_factory, params):
106     mock_run_legacy = mock_func_factory(nominatim.clicmd.admin, 'run_legacy_script')
107
108     assert 0 == call_nominatim('admin', *params)
109
110     assert mock_run_legacy.called == 1
111
112
113 @pytest.mark.parametrize("func, params", [('analyse_indexing', ('--analyse-indexing', ))])
114 def test_admin_command_tool(temp_db, mock_func_factory, func, params):
115     mock = mock_func_factory(nominatim.tools.admin, func)
116
117     assert 0 == call_nominatim('admin', *params)
118     assert mock.called == 1
119
120
121 def test_admin_command_check_database(mock_func_factory):
122     mock = mock_func_factory(nominatim.tools.check_database, 'check_database')
123
124     assert 0 == call_nominatim('admin', '--check-database')
125     assert mock.called == 1
126
127
128 @pytest.mark.parametrize("name,oid", [('file', 'foo.osm'), ('diff', 'foo.osc'),
129                                       ('node', 12), ('way', 8), ('relation', 32)])
130 def test_add_data_command(mock_run_legacy, name, oid):
131     assert 0 == call_nominatim('add-data', '--' + name, str(oid))
132
133     assert mock_run_legacy.called == 1
134     assert mock_run_legacy.last_args == ('update.php', '--import-' + name, oid)
135
136
137 @pytest.mark.parametrize("params,do_bnds,do_ranks", [
138                           ([], 1, 1),
139                           (['--boundaries-only'], 1, 0),
140                           (['--no-boundaries'], 0, 1),
141                           (['--boundaries-only', '--no-boundaries'], 0, 0)])
142 def test_index_command(mock_func_factory, temp_db_cursor, params, do_bnds, do_ranks):
143     temp_db_cursor.execute("CREATE TABLE import_status (indexed bool)")
144     bnd_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_boundaries')
145     rank_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_by_rank')
146
147     assert 0 == call_nominatim('index', *params)
148
149     assert bnd_mock.called == do_bnds
150     assert rank_mock.called == do_ranks
151
152
153 @pytest.mark.parametrize("command,params", [
154                          ('wiki-data', ('setup.php', '--import-wikipedia-articles')),
155                          ('importance', ('update.php', '--recompute-importance')),
156                          ])
157 def test_refresh_legacy_command(mock_func_factory, temp_db, command, params):
158     mock_run_legacy = mock_func_factory(nominatim.clicmd.refresh, 'run_legacy_script')
159
160     assert 0 == call_nominatim('refresh', '--' + command)
161
162     assert mock_run_legacy.called == 1
163     assert len(mock_run_legacy.last_args) >= len(params)
164     assert mock_run_legacy.last_args[:len(params)] == params
165
166 @pytest.mark.parametrize("command,func", [
167                          ('postcodes', 'update_postcodes'),
168                          ('word-counts', 'recompute_word_counts'),
169                          ('address-levels', 'load_address_levels_from_file'),
170                          ('functions', 'create_functions'),
171                          ('website', 'setup_website'),
172                          ])
173 def test_refresh_command(mock_func_factory, temp_db, command, func):
174     func_mock = mock_func_factory(nominatim.tools.refresh, func)
175
176     assert 0 == call_nominatim('refresh', '--' + command)
177     assert func_mock.called == 1
178
179
180 def test_refresh_importance_computed_after_wiki_import(mock_func_factory, temp_db):
181     mock_run_legacy = mock_func_factory(nominatim.clicmd.refresh, 'run_legacy_script')
182
183     assert 0 == call_nominatim('refresh', '--importance', '--wiki-data')
184
185     assert mock_run_legacy.called == 2
186     assert mock_run_legacy.last_args == ('update.php', '--recompute-importance')
187
188
189 @pytest.mark.parametrize("params,func", [
190                          (('--init', '--no-update-functions'), 'init_replication'),
191                          (('--check-for-updates',), 'check_for_updates')
192                          ])
193 def test_replication_command(mock_func_factory, temp_db, params, func):
194     func_mock = mock_func_factory(nominatim.tools.replication, func)
195
196     assert 0 == call_nominatim('replication', *params)
197     assert func_mock.called == 1
198
199
200 def test_replication_update_bad_interval(monkeypatch, temp_db):
201     monkeypatch.setenv('NOMINATIM_REPLICATION_UPDATE_INTERVAL', 'xx')
202
203     assert call_nominatim('replication') == 1
204
205
206 def test_replication_update_bad_interval_for_geofabrik(monkeypatch, temp_db):
207     monkeypatch.setenv('NOMINATIM_REPLICATION_URL',
208                        'https://download.geofabrik.de/europe/ireland-and-northern-ireland-updates')
209
210     assert call_nominatim('replication') == 1
211
212
213 @pytest.mark.parametrize("state", [nominatim.tools.replication.UpdateState.UP_TO_DATE,
214                                    nominatim.tools.replication.UpdateState.NO_CHANGES])
215 def test_replication_update_once_no_index(mock_func_factory, temp_db, temp_db_conn,
216                                           status_table, state):
217     status.set_status(temp_db_conn, date=dt.datetime.now(dt.timezone.utc), seq=1)
218     func_mock = mock_func_factory(nominatim.tools.replication, 'update')
219
220     assert 0 == call_nominatim('replication', '--once', '--no-index')
221
222
223 def test_replication_update_continuous(monkeypatch, temp_db_conn, status_table):
224     status.set_status(temp_db_conn, date=dt.datetime.now(dt.timezone.utc), seq=1)
225     states = [nominatim.tools.replication.UpdateState.UP_TO_DATE,
226               nominatim.tools.replication.UpdateState.UP_TO_DATE]
227     monkeypatch.setattr(nominatim.tools.replication, 'update',
228                         lambda *args, **kwargs: states.pop())
229
230     index_mock = MockParamCapture()
231     monkeypatch.setattr(nominatim.indexer.indexer.Indexer, 'index_boundaries', index_mock)
232     monkeypatch.setattr(nominatim.indexer.indexer.Indexer, 'index_by_rank', index_mock)
233
234     with pytest.raises(IndexError):
235         call_nominatim('replication')
236
237     assert index_mock.called == 4
238
239
240 def test_replication_update_continuous_no_change(monkeypatch, temp_db_conn, status_table):
241     status.set_status(temp_db_conn, date=dt.datetime.now(dt.timezone.utc), seq=1)
242     states = [nominatim.tools.replication.UpdateState.NO_CHANGES,
243               nominatim.tools.replication.UpdateState.UP_TO_DATE]
244     monkeypatch.setattr(nominatim.tools.replication, 'update',
245                         lambda *args, **kwargs: states.pop())
246
247     index_mock = MockParamCapture()
248     monkeypatch.setattr(nominatim.indexer.indexer.Indexer, 'index_boundaries', index_mock)
249     monkeypatch.setattr(nominatim.indexer.indexer.Indexer, 'index_by_rank', index_mock)
250
251     sleep_mock = MockParamCapture()
252     monkeypatch.setattr(time, 'sleep', sleep_mock)
253
254     with pytest.raises(IndexError):
255         call_nominatim('replication')
256
257     assert index_mock.called == 2
258     assert sleep_mock.called == 1
259     assert sleep_mock.last_args[0] == 60
260
261
262 def test_serve_command(mock_func_factory):
263     func = mock_func_factory(nominatim.cli, 'run_php_server')
264
265     call_nominatim('serve')
266
267     assert func.called == 1
268
269 @pytest.mark.parametrize("params", [
270                          ('search', '--query', 'new'),
271                          ('reverse', '--lat', '0', '--lon', '0'),
272                          ('lookup', '--id', 'N1'),
273                          ('details', '--node', '1'),
274                          ('details', '--way', '1'),
275                          ('details', '--relation', '1'),
276                          ('details', '--place_id', '10001'),
277                          ('status',)
278                          ])
279 def test_api_commands_simple(mock_func_factory, params):
280     mock_run_api = mock_func_factory(nominatim.clicmd.api, 'run_api_script')
281
282     assert 0 == call_nominatim(*params)
283
284     assert mock_run_api.called == 1
285     assert mock_run_api.last_args[0] == params[0]