forked from hans/Nominatim
add unit tests for new Python API
This commit is contained in:
@@ -38,6 +38,14 @@ class NominatimAPIAsync:
|
||||
future=True)
|
||||
|
||||
|
||||
async def close(self) -> None:
|
||||
""" Close all active connections to the database. The NominatimAPIAsync
|
||||
object remains usable after closing. If a new API functions is
|
||||
called, new connections are created.
|
||||
"""
|
||||
await self.engine.dispose()
|
||||
|
||||
|
||||
async def status(self) -> StatusResult:
|
||||
""" Return the status of the database.
|
||||
"""
|
||||
@@ -53,6 +61,14 @@ class NominatimAPI:
|
||||
self.async_api = NominatimAPIAsync(project_dir, environ)
|
||||
|
||||
|
||||
def close(self) -> None:
|
||||
""" Close all active connections to the database. The NominatimAPIAsync
|
||||
object remains usable after closing. If a new API functions is
|
||||
called, new connections are created.
|
||||
"""
|
||||
asyncio.get_event_loop().run_until_complete(self.async_api.close())
|
||||
|
||||
|
||||
def status(self) -> StatusResult:
|
||||
""" Return the status of the database.
|
||||
"""
|
||||
|
||||
@@ -60,7 +60,7 @@ async def get_status(engine: AsyncEngine) -> StatusResult:
|
||||
async with engine.begin() as conn:
|
||||
status.data_updated = await _get_database_date(conn)
|
||||
status.database_version = await _get_database_version(conn)
|
||||
except asyncpg.PostgresError as err:
|
||||
return StatusResult(700, str(err))
|
||||
except asyncpg.PostgresError:
|
||||
return StatusResult(700, 'No database')
|
||||
|
||||
return status
|
||||
|
||||
22
test/python/api/conftest.py
Normal file
22
test/python/api/conftest.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# This file is part of Nominatim. (https://nominatim.org)
|
||||
#
|
||||
# Copyright (C) 2022 by the Nominatim developer community.
|
||||
# For a full list of authors see the git log.
|
||||
"""
|
||||
Helper fixtures for API call tests.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
import time
|
||||
|
||||
from nominatim.api import NominatimAPI
|
||||
|
||||
@pytest.fixture
|
||||
def apiobj(temp_db):
|
||||
""" Create an asynchronous SQLAlchemy engine for the test DB.
|
||||
"""
|
||||
api = NominatimAPI(Path('/invalid'), {})
|
||||
yield api
|
||||
api.close()
|
||||
60
test/python/api/test_api_status.py
Normal file
60
test/python/api/test_api_status.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# This file is part of Nominatim. (https://nominatim.org)
|
||||
#
|
||||
# Copyright (C) 2022 by the Nominatim developer community.
|
||||
# For a full list of authors see the git log.
|
||||
"""
|
||||
Tests for the status API call.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import datetime as dt
|
||||
import pytest
|
||||
|
||||
from nominatim.version import version_str
|
||||
from nominatim.api import NominatimAPI
|
||||
|
||||
def test_status_no_extra_info(apiobj, table_factory):
|
||||
table_factory('import_status',
|
||||
definition="lastimportdate timestamp with time zone NOT NULL")
|
||||
table_factory('nominatim_properties',
|
||||
definition='property TEXT, value TEXT')
|
||||
|
||||
result = apiobj.status()
|
||||
|
||||
assert result.status == 0
|
||||
assert result.message == 'OK'
|
||||
assert result.software_version == version_str()
|
||||
assert result.database_version is None
|
||||
assert result.data_updated is None
|
||||
|
||||
|
||||
def test_status_full(apiobj, table_factory):
|
||||
table_factory('import_status',
|
||||
definition="lastimportdate timestamp with time zone NOT NULL",
|
||||
content=(('2022-12-07 15:14:46+01',),))
|
||||
table_factory('nominatim_properties',
|
||||
definition='property TEXT, value TEXT',
|
||||
content=(('database_version', '99.5.4-2'), ))
|
||||
|
||||
result = apiobj.status()
|
||||
|
||||
assert result.status == 0
|
||||
assert result.message == 'OK'
|
||||
assert result.software_version == version_str()
|
||||
assert result.database_version == '99.5.4-2'
|
||||
assert result.data_updated == dt.datetime(2022, 12, 7, 14, 14, 46, 0, tzinfo=dt.timezone.utc)
|
||||
|
||||
|
||||
def test_status_database_not_found(monkeypatch):
|
||||
monkeypatch.setenv('NOMINATIM_DATABASE_DSN', 'dbname=rgjdfkgjedkrgdfkngdfkg')
|
||||
|
||||
api = NominatimAPI(Path('/invalid'), {})
|
||||
|
||||
result = api.status()
|
||||
|
||||
assert result.status == 700
|
||||
assert result.message == 'No database'
|
||||
assert result.software_version == version_str()
|
||||
assert result.database_version is None
|
||||
assert result.data_updated is None
|
||||
@@ -11,6 +11,7 @@ These tests just check that the various command line parameters route to the
|
||||
correct functionionality. They use a lot of monkeypatching to avoid executing
|
||||
the actual functions.
|
||||
"""
|
||||
import importlib
|
||||
import pytest
|
||||
|
||||
import nominatim.indexer.indexer
|
||||
@@ -59,7 +60,7 @@ def test_cli_add_data_tiger_data(cli_call, cli_tokenizer_mock, mock_func_factory
|
||||
assert mock.called == 1
|
||||
|
||||
|
||||
def test_cli_serve_command(cli_call, mock_func_factory):
|
||||
def test_cli_serve_php(cli_call, mock_func_factory):
|
||||
func = mock_func_factory(nominatim.cli, 'run_php_server')
|
||||
|
||||
cli_call('serve') == 0
|
||||
@@ -67,6 +68,47 @@ def test_cli_serve_command(cli_call, mock_func_factory):
|
||||
assert func.called == 1
|
||||
|
||||
|
||||
def test_cli_serve_sanic(cli_call, mock_func_factory):
|
||||
mod = pytest.importorskip("sanic")
|
||||
func = mock_func_factory(mod.Sanic, "run")
|
||||
|
||||
cli_call('serve', '--engine', 'sanic') == 0
|
||||
|
||||
assert func.called == 1
|
||||
|
||||
|
||||
def test_cli_serve_starlette_custom_server(cli_call, mock_func_factory):
|
||||
pytest.importorskip("starlette")
|
||||
mod = pytest.importorskip("uvicorn")
|
||||
func = mock_func_factory(mod, "run")
|
||||
|
||||
cli_call('serve', '--engine', 'starlette', '--server', 'foobar:4545') == 0
|
||||
|
||||
assert func.called == 1
|
||||
assert func.last_kwargs['host'] == 'foobar'
|
||||
assert func.last_kwargs['port'] == 4545
|
||||
|
||||
|
||||
def test_cli_serve_starlette_custom_server_bad_port(cli_call, mock_func_factory):
|
||||
pytest.importorskip("starlette")
|
||||
mod = pytest.importorskip("uvicorn")
|
||||
func = mock_func_factory(mod, "run")
|
||||
|
||||
cli_call('serve', '--engine', 'starlette', '--server', 'foobar:45:45') == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("engine", ['falcon', 'starlette'])
|
||||
def test_cli_serve_uvicorn_based(cli_call, engine, mock_func_factory):
|
||||
pytest.importorskip(engine)
|
||||
mod = pytest.importorskip("uvicorn")
|
||||
func = mock_func_factory(mod, "run")
|
||||
|
||||
cli_call('serve', '--engine', engine) == 0
|
||||
|
||||
assert func.called == 1
|
||||
assert func.last_kwargs['host'] == '127.0.0.1'
|
||||
assert func.last_kwargs['port'] == 8088
|
||||
|
||||
def test_cli_export_command(cli_call, mock_run_legacy):
|
||||
assert cli_call('export', '--output-all-postcodes') == 0
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
"""
|
||||
Tests for API access commands of command-line interface wrapper.
|
||||
"""
|
||||
import json
|
||||
import pytest
|
||||
|
||||
import nominatim.clicmd.api
|
||||
import nominatim.api
|
||||
from nominatim.apicmd.status import StatusResult
|
||||
|
||||
|
||||
@pytest.mark.parametrize("endpoint", (('search', 'reverse', 'lookup', 'details', 'status')))
|
||||
@@ -27,9 +30,8 @@ def test_no_api_without_phpcgi(endpoint):
|
||||
('details', '--node', '1'),
|
||||
('details', '--way', '1'),
|
||||
('details', '--relation', '1'),
|
||||
('details', '--place_id', '10001'),
|
||||
('status',)])
|
||||
class TestCliApiCall:
|
||||
('details', '--place_id', '10001')])
|
||||
class TestCliApiCallPhp:
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_cli_call(self, params, cli_call, mock_func_factory, tmp_path):
|
||||
@@ -55,6 +57,29 @@ class TestCliApiCall:
|
||||
assert self.run_nominatim() == 1
|
||||
|
||||
|
||||
class TestCliStatusCall:
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup_status_mock(self, monkeypatch):
|
||||
monkeypatch.setattr(nominatim.api.NominatimAPI, 'status',
|
||||
lambda self: StatusResult(200, 'OK'))
|
||||
|
||||
|
||||
def test_status_simple(self, cli_call, tmp_path):
|
||||
result = cli_call('status', '--project-dir', str(tmp_path))
|
||||
|
||||
assert result == 0
|
||||
|
||||
|
||||
def test_status_json_format(self, cli_call, tmp_path, capsys):
|
||||
result = cli_call('status', '--project-dir', str(tmp_path),
|
||||
'--format', 'json')
|
||||
|
||||
assert result == 0
|
||||
|
||||
json.loads(capsys.readouterr().out)
|
||||
|
||||
|
||||
QUERY_PARAMS = {
|
||||
'search': ('--query', 'somewhere'),
|
||||
'reverse': ('--lat', '20', '--lon', '30'),
|
||||
|
||||
59
test/python/result_formatter/test_v1.py
Normal file
59
test/python/result_formatter/test_v1.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# This file is part of Nominatim. (https://nominatim.org)
|
||||
#
|
||||
# Copyright (C) 2022 by the Nominatim developer community.
|
||||
# For a full list of authors see the git log.
|
||||
"""
|
||||
Tests for formatting results for the V1 API.
|
||||
"""
|
||||
import datetime as dt
|
||||
import pytest
|
||||
|
||||
import nominatim.result_formatter.v1 as format_module
|
||||
from nominatim.apicmd.status import StatusResult
|
||||
from nominatim.version import version_str
|
||||
|
||||
STATUS_FORMATS = {'text', 'json'}
|
||||
|
||||
class TestStatusResultFormat:
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def make_formatter(self):
|
||||
self.formatter = format_module.create(StatusResult)
|
||||
|
||||
|
||||
def test_format_list(self):
|
||||
assert set(self.formatter.list_formats()) == STATUS_FORMATS
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fmt', list(STATUS_FORMATS))
|
||||
def test_supported(self, fmt):
|
||||
assert self.formatter.supports_format(fmt)
|
||||
|
||||
|
||||
def test_unsupported(self):
|
||||
assert not self.formatter.supports_format('gagaga')
|
||||
|
||||
|
||||
def test_format_text(self):
|
||||
assert self.formatter.format(StatusResult(0, 'message here'), 'text') == 'message here'
|
||||
|
||||
|
||||
def test_format_json_minimal(self):
|
||||
status = StatusResult(700, 'Bad format.')
|
||||
|
||||
result = self.formatter.format(status, 'json')
|
||||
|
||||
assert result == '{"status": 700, "message": "Bad format.", "software_version": "%s"}' % (version_str())
|
||||
|
||||
|
||||
def test_format_json_full(self):
|
||||
status = StatusResult(0, 'OK')
|
||||
status.data_updated = dt.datetime(2010, 2, 7, 20, 20, 3, 0, tzinfo=dt.timezone.utc)
|
||||
status.database_version = '5.6'
|
||||
|
||||
result = self.formatter.format(status, 'json')
|
||||
|
||||
assert result == '{"status": 0, "message": "OK", "data_updated": "2010-02-07T20:20:03+00:00", "software_version": "%s", "database_version": "5.6"}' % (version_str())
|
||||
Reference in New Issue
Block a user