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