2 Tests for command line interface wrapper.
 
   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
 
  10 import nominatim.db.properties
 
  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
 
  27 from mocks import MockParamCapture
 
  30 def mock_run_legacy(monkeypatch):
 
  31     mock = MockParamCapture()
 
  32     monkeypatch.setattr(nominatim.cli, 'run_legacy_script', mock)
 
  37 def mock_func_factory(monkeypatch):
 
  38     def get_mock(module, func):
 
  39         mock = MockParamCapture()
 
  41         monkeypatch.setattr(module, func, mock)
 
  50     @pytest.fixture(autouse=True)
 
  51     def setup_cli_call(self, cli_call):
 
  52         self.call_nominatim = cli_call
 
  55     def test_cli_help(self, capsys):
 
  56         """ Running nominatim tool without arguments prints help.
 
  58         assert self.call_nominatim() == 1
 
  60         captured = capsys.readouterr()
 
  61         assert captured.out.startswith('usage:')
 
  64     @pytest.mark.parametrize("command,script", [
 
  65                              (('export',), 'export')
 
  67     def test_legacy_commands_simple(self, mock_run_legacy, command, script):
 
  68         assert self.call_nominatim(*command) == 0
 
  70         assert mock_run_legacy.called == 1
 
  71         assert mock_run_legacy.last_args[0] == script + '.php'
 
  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')
 
  80         assert self.call_nominatim('admin', *params) == 0
 
  82         assert mock_run_legacy.called == 1
 
  85     def test_admin_command_check_database(self, mock_func_factory):
 
  86         mock = mock_func_factory(nominatim.tools.check_database, 'check_database')
 
  88         assert self.call_nominatim('admin', '--check-database') == 0
 
  89         assert mock.called == 1
 
  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
 
  97         assert mock_run_legacy.called == 1
 
 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
 
 105         assert mock_run_legacy.called == 1
 
 108     def test_serve_command(self, mock_func_factory):
 
 109         func = mock_func_factory(nominatim.cli, 'run_php_server')
 
 111         self.call_nominatim('serve')
 
 113         assert func.called == 1
 
 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'),
 
 124     def test_api_commands_simple(self, mock_func_factory, params):
 
 125         mock_run_api = mock_func_factory(nominatim.clicmd.api, 'run_api_script')
 
 127         assert self.call_nominatim(*params) == 0
 
 129         assert mock_run_api.called == 1
 
 130         assert mock_run_api.last_args[0] == params[0]
 
 136     @pytest.fixture(autouse=True)
 
 137     def setup_cli_call(self, cli_call, temp_db):
 
 138         self.call_nominatim = cli_call
 
 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
 
 148             def update_sql_functions(self, *args):
 
 149                 self.update_sql_functions_called = True
 
 151             def finalize_import(self, *args):
 
 152                 self.finalize_import_called = True
 
 154         tok = DummyTokenizer()
 
 155         monkeypatch.setattr(nominatim.tokenizer.factory, 'get_tokenizer_for_db',
 
 157         monkeypatch.setattr(nominatim.tokenizer.factory, 'create_tokenizer',
 
 160         self.tokenizer_mock = tok
 
 163     def test_import_missing_file(self):
 
 164         assert self.call_nominatim('import', '--osm-file', 'sfsafegwedgw.reh.erh') == 1
 
 167     def test_import_bad_file(self):
 
 168         assert self.call_nominatim('import', '--osm-file', '.') == 1
 
 171     def test_import_full(self, mock_func_factory):
 
 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')
 
 191         cf_mock = mock_func_factory(nominatim.tools.refresh, 'create_functions')
 
 193         assert self.call_nominatim('import', '--osm-file', __file__) == 0
 
 194         assert self.tokenizer_mock.finalize_import_called
 
 196         assert cf_mock.called > 1
 
 199             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 202     def test_import_continue_load_data(self, mock_func_factory):
 
 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')
 
 214         assert self.call_nominatim('import', '--continue', 'load-data') == 0
 
 215         assert self.tokenizer_mock.finalize_import_called
 
 218             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 221     def test_import_continue_indexing(self, mock_func_factory, placex_table,
 
 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')
 
 231         assert self.call_nominatim('import', '--continue', 'indexing') == 0
 
 234             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 236         assert temp_db_conn.index_exists('idx_placex_pendingsector')
 
 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')
 
 243     def test_import_continue_postprocess(self, mock_func_factory):
 
 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')
 
 251         assert self.call_nominatim('import', '--continue', 'db-postprocess') == 0
 
 253         assert self.tokenizer_mock.finalize_import_called
 
 256             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 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')
 
 263         assert self.call_nominatim('freeze') == 0
 
 265         assert mock_drop.called == 1
 
 266         assert mock_flatnode.called == 1
 
 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)
 
 274         assert self.call_nominatim('admin', *params) == 0
 
 275         assert mock.called == 1
 
 278     @pytest.mark.parametrize("params,do_bnds,do_ranks", [
 
 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')
 
 289         assert self.call_nominatim('index', *params) == 0
 
 291         assert bnd_mock.called == do_bnds
 
 292         assert rank_mock.called == do_ranks
 
 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')
 
 299             self.call_nominatim('special-phrases', '--import-from-wiki', '--no-replace')
 
 301             self.call_nominatim('special-phrases', '--import-from-wiki')
 
 303         assert func.called == 1
 
 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())
 
 312             self.call_nominatim('special-phrases', '--import-from-csv', csv_path, '--no-replace')
 
 314             self.call_nominatim('special-phrases', '--import-from-csv', csv_path)
 
 316         assert func.called == 1
 
 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'),
 
 325     def test_refresh_command(self, mock_func_factory, command, func):
 
 326         func_mock = mock_func_factory(nominatim.tools.refresh, func)
 
 328         assert self.call_nominatim('refresh', '--' + command) == 0
 
 329         assert func_mock.called == 1
 
 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')
 
 336         assert self.call_nominatim('refresh', '--postcodes') == 0
 
 337         assert func_mock.called == 1
 
 338         assert idx_mock.called == 1
 
 340     def test_refresh_create_functions(self, mock_func_factory):
 
 341         func_mock = mock_func_factory(nominatim.tools.refresh, 'create_functions')
 
 343         assert self.call_nominatim('refresh', '--functions') == 0
 
 344         assert func_mock.called == 1
 
 345         assert self.tokenizer_mock.update_sql_functions_called
 
 348     def test_refresh_importance_computed_after_wiki_import(self, monkeypatch):
 
 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'))
 
 355         assert self.call_nominatim('refresh', '--importance', '--wiki-data') == 0
 
 357         assert calls == ['import', 'update']