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.check_database
 
  19 import nominatim.tools.database_import
 
  20 import nominatim.tools.freeze
 
  21 import nominatim.tools.refresh
 
  22 import nominatim.tools.postcodes
 
  23 import nominatim.tokenizer.factory
 
  25 from mocks import MockParamCapture
 
  28 def mock_run_legacy(monkeypatch):
 
  29     mock = MockParamCapture()
 
  30     monkeypatch.setattr(nominatim.cli, 'run_legacy_script', mock)
 
  35 def mock_func_factory(monkeypatch):
 
  36     def get_mock(module, func):
 
  37         mock = MockParamCapture()
 
  39         monkeypatch.setattr(module, func, mock)
 
  48     @pytest.fixture(autouse=True)
 
  49     def setup_cli_call(self, cli_call):
 
  50         self.call_nominatim = cli_call
 
  53     def test_cli_help(self, capsys):
 
  54         """ Running nominatim tool without arguments prints help.
 
  56         assert self.call_nominatim() == 1
 
  58         captured = capsys.readouterr()
 
  59         assert captured.out.startswith('usage:')
 
  62     @pytest.mark.parametrize("command,script", [
 
  63                              (('add-data', '--file', 'foo.osm'), 'update'),
 
  64                              (('export',), 'export')
 
  66     def test_legacy_commands_simple(self, mock_run_legacy, command, script):
 
  67         assert self.call_nominatim(*command) == 0
 
  69         assert mock_run_legacy.called == 1
 
  70         assert mock_run_legacy.last_args[0] == script + '.php'
 
  73     @pytest.mark.parametrize("params", [('--warm', ),
 
  74                                         ('--warm', '--reverse-only'),
 
  75                                         ('--warm', '--search-only')])
 
  76     def test_admin_command_legacy(self, mock_func_factory, params):
 
  77         mock_run_legacy = mock_func_factory(nominatim.clicmd.admin, 'run_legacy_script')
 
  79         assert self.call_nominatim('admin', *params) == 0
 
  81         assert mock_run_legacy.called == 1
 
  84     def test_admin_command_check_database(self, mock_func_factory):
 
  85         mock = mock_func_factory(nominatim.tools.check_database, 'check_database')
 
  87         assert self.call_nominatim('admin', '--check-database') == 0
 
  88         assert mock.called == 1
 
  91     @pytest.mark.parametrize("name,oid", [('file', 'foo.osm'), ('diff', 'foo.osc'),
 
  92                                           ('node', 12), ('way', 8), ('relation', 32)])
 
  93     def test_add_data_command(self, mock_run_legacy, name, oid):
 
  94         assert self.call_nominatim('add-data', '--' + name, str(oid)) == 0
 
  96         assert mock_run_legacy.called == 1
 
  97         assert mock_run_legacy.last_args == ('update.php', '--import-' + name, oid)
 
 100     def test_serve_command(self, mock_func_factory):
 
 101         func = mock_func_factory(nominatim.cli, 'run_php_server')
 
 103         self.call_nominatim('serve')
 
 105         assert func.called == 1
 
 108     @pytest.mark.parametrize("params", [('search', '--query', 'new'),
 
 109                                         ('reverse', '--lat', '0', '--lon', '0'),
 
 110                                         ('lookup', '--id', 'N1'),
 
 111                                         ('details', '--node', '1'),
 
 112                                         ('details', '--way', '1'),
 
 113                                         ('details', '--relation', '1'),
 
 114                                         ('details', '--place_id', '10001'),
 
 116     def test_api_commands_simple(self, mock_func_factory, params):
 
 117         mock_run_api = mock_func_factory(nominatim.clicmd.api, 'run_api_script')
 
 119         assert self.call_nominatim(*params) == 0
 
 121         assert mock_run_api.called == 1
 
 122         assert mock_run_api.last_args[0] == params[0]
 
 128     @pytest.fixture(autouse=True)
 
 129     def setup_cli_call(self, cli_call, temp_db):
 
 130         self.call_nominatim = cli_call
 
 133     @pytest.fixture(autouse=True)
 
 134     def setup_tokenizer_mock(self, monkeypatch):
 
 135         class DummyTokenizer:
 
 136             def __init__(self, *args, **kwargs):
 
 137                 self.update_sql_functions_called = False
 
 138                 self.finalize_import_called = False
 
 140             def update_sql_functions(self, *args):
 
 141                 self.update_sql_functions_called = True
 
 143             def finalize_import(self, *args):
 
 144                 self.finalize_import_called = True
 
 146         tok = DummyTokenizer()
 
 147         monkeypatch.setattr(nominatim.tokenizer.factory, 'get_tokenizer_for_db',
 
 149         monkeypatch.setattr(nominatim.tokenizer.factory, 'create_tokenizer',
 
 152         self.tokenizer_mock = tok
 
 155     def test_import_missing_file(self):
 
 156         assert self.call_nominatim('import', '--osm-file', 'sfsafegwedgw.reh.erh') == 1
 
 159     def test_import_bad_file(self):
 
 160         assert self.call_nominatim('import', '--osm-file', '.') == 1
 
 163     def test_import_full(self, mock_func_factory):
 
 165             mock_func_factory(nominatim.tools.database_import, 'setup_database_skeleton'),
 
 166             mock_func_factory(nominatim.tools.database_import, 'import_osm_data'),
 
 167             mock_func_factory(nominatim.tools.refresh, 'import_wikipedia_articles'),
 
 168             mock_func_factory(nominatim.tools.database_import, 'truncate_data_tables'),
 
 169             mock_func_factory(nominatim.tools.database_import, 'load_data'),
 
 170             mock_func_factory(nominatim.tools.database_import, 'create_tables'),
 
 171             mock_func_factory(nominatim.tools.database_import, 'create_table_triggers'),
 
 172             mock_func_factory(nominatim.tools.database_import, 'create_partition_tables'),
 
 173             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
 
 174             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
 
 175             mock_func_factory(nominatim.tools.refresh, 'load_address_levels_from_file'),
 
 176             mock_func_factory(nominatim.tools.postcodes, 'update_postcodes'),
 
 177             mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_full'),
 
 178             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
 
 179             mock_func_factory(nominatim.db.properties, 'set_property')
 
 182         cf_mock = mock_func_factory(nominatim.tools.refresh, 'create_functions')
 
 184         assert self.call_nominatim('import', '--osm-file', __file__) == 0
 
 185         assert self.tokenizer_mock.finalize_import_called
 
 187         assert cf_mock.called > 1
 
 190             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 193     def test_import_continue_load_data(self, mock_func_factory):
 
 195             mock_func_factory(nominatim.tools.database_import, 'truncate_data_tables'),
 
 196             mock_func_factory(nominatim.tools.database_import, 'load_data'),
 
 197             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
 
 198             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
 
 199             mock_func_factory(nominatim.tools.postcodes, 'update_postcodes'),
 
 200             mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_full'),
 
 201             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
 
 202             mock_func_factory(nominatim.db.properties, 'set_property')
 
 205         assert self.call_nominatim('import', '--continue', 'load-data') == 0
 
 206         assert self.tokenizer_mock.finalize_import_called
 
 209             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 212     def test_import_continue_indexing(self, mock_func_factory, placex_table,
 
 215             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
 
 216             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
 
 217             mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_full'),
 
 218             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
 
 219             mock_func_factory(nominatim.db.properties, 'set_property')
 
 222         assert self.call_nominatim('import', '--continue', 'indexing') == 0
 
 225             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 227         assert temp_db_conn.index_exists('idx_placex_pendingsector')
 
 229         # Calling it again still works for the index
 
 230         assert self.call_nominatim('import', '--continue', 'indexing') == 0
 
 231         assert temp_db_conn.index_exists('idx_placex_pendingsector')
 
 234     def test_import_continue_postprocess(self, mock_func_factory):
 
 236             mock_func_factory(nominatim.tools.database_import, 'create_search_indices'),
 
 237             mock_func_factory(nominatim.tools.database_import, 'create_country_names'),
 
 238             mock_func_factory(nominatim.tools.refresh, 'setup_website'),
 
 239             mock_func_factory(nominatim.db.properties, 'set_property')
 
 242         assert self.call_nominatim('import', '--continue', 'db-postprocess') == 0
 
 244         assert self.tokenizer_mock.finalize_import_called
 
 247             assert mock.called == 1, "Mock '{}' not called".format(mock.func_name)
 
 250     def test_freeze_command(self, mock_func_factory):
 
 251         mock_drop = mock_func_factory(nominatim.tools.freeze, 'drop_update_tables')
 
 252         mock_flatnode = mock_func_factory(nominatim.tools.freeze, 'drop_flatnode_file')
 
 254         assert self.call_nominatim('freeze') == 0
 
 256         assert mock_drop.called == 1
 
 257         assert mock_flatnode.called == 1
 
 261     @pytest.mark.parametrize("func, params", [('analyse_indexing', ('--analyse-indexing', ))])
 
 262     def test_admin_command_tool(self, mock_func_factory, func, params):
 
 263         mock = mock_func_factory(nominatim.tools.admin, func)
 
 265         assert self.call_nominatim('admin', *params) == 0
 
 266         assert mock.called == 1
 
 269     @pytest.mark.parametrize("params,do_bnds,do_ranks", [
 
 271                               (['--boundaries-only'], 1, 0),
 
 272                               (['--no-boundaries'], 0, 1),
 
 273                               (['--boundaries-only', '--no-boundaries'], 0, 0)])
 
 274     def test_index_command(self, mock_func_factory, table_factory,
 
 275                            params, do_bnds, do_ranks):
 
 276         table_factory('import_status', 'indexed bool')
 
 277         bnd_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_boundaries')
 
 278         rank_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_by_rank')
 
 280         assert self.call_nominatim('index', *params) == 0
 
 282         assert bnd_mock.called == do_bnds
 
 283         assert rank_mock.called == do_ranks
 
 285     @pytest.mark.parametrize("no_replace", [(True), (False)])
 
 286     def test_special_phrases_wiki_command(self, mock_func_factory, no_replace):
 
 287         func = mock_func_factory(nominatim.clicmd.special_phrases.SPImporter, 'import_phrases')
 
 290             self.call_nominatim('special-phrases', '--import-from-wiki', '--no-replace')
 
 292             self.call_nominatim('special-phrases', '--import-from-wiki')
 
 294         assert func.called == 1
 
 296     @pytest.mark.parametrize("no_replace", [(True), (False)])
 
 297     def test_special_phrases_csv_command(self, src_dir, mock_func_factory, no_replace):
 
 298         func = mock_func_factory(nominatim.clicmd.special_phrases.SPImporter, 'import_phrases')
 
 299         testdata = src_dir / 'test' / 'testdb'
 
 300         csv_path = str((testdata / 'full_en_phrases_test.csv').resolve())
 
 303             self.call_nominatim('special-phrases', '--import-from-csv', csv_path, '--no-replace')
 
 305             self.call_nominatim('special-phrases', '--import-from-csv', csv_path)
 
 307         assert func.called == 1
 
 309     @pytest.mark.parametrize("command,func", [
 
 310                              ('word-counts', 'recompute_word_counts'),
 
 311                              ('address-levels', 'load_address_levels_from_file'),
 
 312                              ('wiki-data', 'import_wikipedia_articles'),
 
 313                              ('importance', 'recompute_importance'),
 
 314                              ('website', 'setup_website'),
 
 316     def test_refresh_command(self, mock_func_factory, command, func):
 
 317         func_mock = mock_func_factory(nominatim.tools.refresh, func)
 
 319         assert self.call_nominatim('refresh', '--' + command) == 0
 
 320         assert func_mock.called == 1
 
 323     def test_refresh_postcodes(self, mock_func_factory, place_table):
 
 324         func_mock = mock_func_factory(nominatim.tools.postcodes, 'update_postcodes')
 
 325         idx_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_postcodes')
 
 327         assert self.call_nominatim('refresh', '--postcodes') == 0
 
 328         assert func_mock.called == 1
 
 329         assert idx_mock.called == 1
 
 331     def test_refresh_create_functions(self, mock_func_factory):
 
 332         func_mock = mock_func_factory(nominatim.tools.refresh, 'create_functions')
 
 334         assert self.call_nominatim('refresh', '--functions') == 0
 
 335         assert func_mock.called == 1
 
 336         assert self.tokenizer_mock.update_sql_functions_called
 
 339     def test_refresh_importance_computed_after_wiki_import(self, monkeypatch):
 
 341         monkeypatch.setattr(nominatim.tools.refresh, 'import_wikipedia_articles',
 
 342                             lambda *args, **kwargs: calls.append('import') or 0)
 
 343         monkeypatch.setattr(nominatim.tools.refresh, 'recompute_importance',
 
 344                             lambda *args, **kwargs: calls.append('update'))
 
 346         assert self.call_nominatim('refresh', '--importance', '--wiki-data') == 0
 
 348         assert calls == ['import', 'update']