mirror of
https://github.com/osm-search/Nominatim.git
synced 2024-12-25 22:12:45 +03:00
add unit tests for lookup function
This commit is contained in:
parent
370c9b38c0
commit
189f74a40d
@ -20,4 +20,11 @@ from .status import (StatusResult as StatusResult)
|
|||||||
from .types import (PlaceID as PlaceID,
|
from .types import (PlaceID as PlaceID,
|
||||||
OsmID as OsmID,
|
OsmID as OsmID,
|
||||||
PlaceRef as PlaceRef,
|
PlaceRef as PlaceRef,
|
||||||
|
GeometryFormat as GeometryFormat,
|
||||||
LookupDetails as LookupDetails)
|
LookupDetails as LookupDetails)
|
||||||
|
from .results import (SourceTable as SourceTable,
|
||||||
|
AddressLine as AddressLine,
|
||||||
|
AddressLines as AddressLines,
|
||||||
|
WordInfo as WordInfo,
|
||||||
|
WordInfos as WordInfos,
|
||||||
|
SearchResult as SearchResult)
|
||||||
|
@ -155,6 +155,12 @@ class NominatimAPI:
|
|||||||
self._loop.close()
|
self._loop.close()
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def config(self) -> Configuration:
|
||||||
|
""" Return the configuration used by the API.
|
||||||
|
"""
|
||||||
|
return self._async_api.config
|
||||||
|
|
||||||
def status(self) -> StatusResult:
|
def status(self) -> StatusResult:
|
||||||
""" Return the status of the database.
|
""" Return the status of the database.
|
||||||
"""
|
"""
|
||||||
|
@ -23,8 +23,8 @@ def _select_column_geometry(column: SaColumn,
|
|||||||
"""
|
"""
|
||||||
if geometry_output & ntyp.GeometryFormat.GEOJSON:
|
if geometry_output & ntyp.GeometryFormat.GEOJSON:
|
||||||
return sa.literal_column(f"""
|
return sa.literal_column(f"""
|
||||||
ST_AsGeoJSON(CASE WHEN ST_NPoints({0}) > 5000
|
ST_AsGeoJSON(CASE WHEN ST_NPoints({column.name}) > 5000
|
||||||
THEN ST_SimplifyPreserveTopology({0}, 0.0001)
|
THEN ST_SimplifyPreserveTopology({column.name}, 0.0001)
|
||||||
ELSE {column.name} END)
|
ELSE {column.name} END)
|
||||||
""").label('geometry_geojson')
|
""").label('geometry_geojson')
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class AddressLine:
|
|||||||
names: Dict[str, str]
|
names: Dict[str, str]
|
||||||
extratags: Optional[Dict[str, str]]
|
extratags: Optional[Dict[str, str]]
|
||||||
|
|
||||||
admin_level: int
|
admin_level: Optional[int]
|
||||||
fromarea: bool
|
fromarea: bool
|
||||||
isaddress: bool
|
isaddress: bool
|
||||||
rank_address: int
|
rank_address: int
|
||||||
@ -187,10 +187,16 @@ def _result_row_to_address_row(row: SaRow) -> AddressLine:
|
|||||||
if 'place_type' in row:
|
if 'place_type' in row:
|
||||||
extratags['place_type'] = row.place_type
|
extratags['place_type'] = row.place_type
|
||||||
|
|
||||||
|
names = row.name
|
||||||
|
if getattr(row, 'housenumber', None) is not None:
|
||||||
|
if names is None:
|
||||||
|
names = {}
|
||||||
|
names['housenumber'] = row.housenumber
|
||||||
|
|
||||||
return AddressLine(place_id=row.place_id,
|
return AddressLine(place_id=row.place_id,
|
||||||
osm_object=(row.osm_type, row.osm_id),
|
osm_object=None if row.osm_type is None else (row.osm_type, row.osm_id),
|
||||||
category=(getattr(row, 'class'), row.type),
|
category=(getattr(row, 'class'), row.type),
|
||||||
names=row.name,
|
names=names,
|
||||||
extratags=extratags,
|
extratags=extratags,
|
||||||
admin_level=row.admin_level,
|
admin_level=row.admin_level,
|
||||||
fromarea=row.fromarea,
|
fromarea=row.fromarea,
|
||||||
@ -235,7 +241,7 @@ def _placex_select_address_row(conn: SearchConnection,
|
|||||||
t = conn.t.placex
|
t = conn.t.placex
|
||||||
return sa.select(t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name,
|
return sa.select(t.c.place_id, t.c.osm_type, t.c.osm_id, t.c.name,
|
||||||
t.c.class_.label('class'), t.c.type,
|
t.c.class_.label('class'), t.c.type,
|
||||||
t.c.admin_level,
|
t.c.admin_level, t.c.housenumber,
|
||||||
sa.literal_column("""ST_GeometryType(geometry) in
|
sa.literal_column("""ST_GeometryType(geometry) in
|
||||||
('ST_Polygon','ST_MultiPolygon')""").label('fromarea'),
|
('ST_Polygon','ST_MultiPolygon')""").label('fromarea'),
|
||||||
t.c.rank_address,
|
t.c.rank_address,
|
||||||
|
@ -10,8 +10,10 @@ Helper fixtures for API call tests.
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
import time
|
import time
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
import nominatim.api as napi
|
import nominatim.api as napi
|
||||||
|
from nominatim.db.sql_preprocessor import SQLPreprocessor
|
||||||
|
|
||||||
class APITester:
|
class APITester:
|
||||||
|
|
||||||
@ -34,6 +36,47 @@ class APITester:
|
|||||||
self.async_to_sync(self.exec_async(sql, data))
|
self.async_to_sync(self.exec_async(sql, data))
|
||||||
|
|
||||||
|
|
||||||
|
def add_placex(self, **kw):
|
||||||
|
name = kw.get('name')
|
||||||
|
if isinstance(name, str):
|
||||||
|
name = {'name': name}
|
||||||
|
|
||||||
|
self.add_data('placex',
|
||||||
|
{'place_id': kw.get('place_id', 1000),
|
||||||
|
'osm_type': kw.get('osm_type', 'W'),
|
||||||
|
'osm_id': kw.get('osm_id', 4),
|
||||||
|
'class_': kw.get('class_', 'highway'),
|
||||||
|
'type': kw.get('type', 'residential'),
|
||||||
|
'name': name,
|
||||||
|
'address': kw.get('address'),
|
||||||
|
'extratags': kw.get('extratags'),
|
||||||
|
'parent_place_id': kw.get('parent_place_id'),
|
||||||
|
'linked_place_id': kw.get('linked_place_id'),
|
||||||
|
'admin_level': kw.get('admin_level', 15),
|
||||||
|
'country_code': kw.get('country_code'),
|
||||||
|
'housenumber': kw.get('housenumber'),
|
||||||
|
'postcode': kw.get('postcode'),
|
||||||
|
'wikipedia': kw.get('wikipedia'),
|
||||||
|
'rank_search': kw.get('rank_search', 30),
|
||||||
|
'rank_address': kw.get('rank_address', 30),
|
||||||
|
'importance': kw.get('importance'),
|
||||||
|
'centroid': 'SRID=4326;POINT(%f %f)' % kw.get('centroid', (23.0, 34.0)),
|
||||||
|
'indexed_date': kw.get('indexed_date',
|
||||||
|
dt.datetime(2022, 12, 7, 14, 14, 46, 0)),
|
||||||
|
'geometry': 'SRID=4326;' + kw.get('geometry', 'POINT(23 34)')})
|
||||||
|
|
||||||
|
|
||||||
|
def add_address_placex(self, object_id, **kw):
|
||||||
|
self.add_placex(**kw)
|
||||||
|
self.add_data('addressline',
|
||||||
|
{'place_id': object_id,
|
||||||
|
'address_place_id': kw.get('place_id', 1000),
|
||||||
|
'distance': kw.get('distance', 0.0),
|
||||||
|
'cached_rank_address': kw.get('rank_address', 30),
|
||||||
|
'fromarea': kw.get('fromarea', False),
|
||||||
|
'isaddress': kw.get('isaddress', True)})
|
||||||
|
|
||||||
|
|
||||||
async def exec_async(self, sql, *args, **kwargs):
|
async def exec_async(self, sql, *args, **kwargs):
|
||||||
async with self.api._async_api.begin() as conn:
|
async with self.api._async_api.begin() as conn:
|
||||||
return await conn.execute(sql, *args, **kwargs)
|
return await conn.execute(sql, *args, **kwargs)
|
||||||
@ -45,10 +88,15 @@ class APITester:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def apiobj(temp_db_with_extensions):
|
def apiobj(temp_db_with_extensions, temp_db_conn):
|
||||||
""" Create an asynchronous SQLAlchemy engine for the test DB.
|
""" Create an asynchronous SQLAlchemy engine for the test DB.
|
||||||
"""
|
"""
|
||||||
testapi = APITester()
|
testapi = APITester()
|
||||||
testapi.async_to_sync(testapi.create_tables())
|
testapi.async_to_sync(testapi.create_tables())
|
||||||
|
|
||||||
|
SQLPreprocessor(temp_db_conn, testapi.api.config)\
|
||||||
|
.run_sql_file(temp_db_conn, 'functions/address_lookup.sql')
|
||||||
|
|
||||||
yield testapi
|
yield testapi
|
||||||
|
|
||||||
testapi.api.close()
|
testapi.api.close()
|
||||||
|
264
test/python/api/test_api_lookup.py
Normal file
264
test/python/api/test_api_lookup.py
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# This file is part of Nominatim. (https://nominatim.org)
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023 by the Nominatim developer community.
|
||||||
|
# For a full list of authors see the git log.
|
||||||
|
"""
|
||||||
|
Tests for lookup API call.
|
||||||
|
"""
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import nominatim.api as napi
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('idobj', (napi.PlaceID(332), napi.OsmID('W', 4),
|
||||||
|
napi.OsmID('W', 4, 'highway')))
|
||||||
|
def test_lookup_in_placex(apiobj, idobj):
|
||||||
|
import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
|
||||||
|
apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
|
||||||
|
class_='highway', type='residential',
|
||||||
|
name={'name': 'Road'}, address={'city': 'Barrow'},
|
||||||
|
extratags={'surface': 'paved'},
|
||||||
|
parent_place_id=34, linked_place_id=55,
|
||||||
|
admin_level=15, country_code='gb',
|
||||||
|
housenumber='4',
|
||||||
|
postcode='34425', wikipedia='en:Faa',
|
||||||
|
rank_search=27, rank_address=26,
|
||||||
|
importance=0.01,
|
||||||
|
centroid=(23, 34),
|
||||||
|
indexed_date=import_date,
|
||||||
|
geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(idobj, napi.LookupDetails())
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
|
||||||
|
assert result.source_table.name == 'PLACEX'
|
||||||
|
assert result.category == ('highway', 'residential')
|
||||||
|
assert result.centroid == (pytest.approx(23.0), pytest.approx(34.0))
|
||||||
|
|
||||||
|
assert result.place_id == 332
|
||||||
|
assert result.parent_place_id == 34
|
||||||
|
assert result.linked_place_id == 55
|
||||||
|
assert result.osm_object == ('W', 4)
|
||||||
|
assert result.admin_level == 15
|
||||||
|
|
||||||
|
assert result.names == {'name': 'Road'}
|
||||||
|
assert result.address == {'city': 'Barrow'}
|
||||||
|
assert result.extratags == {'surface': 'paved'}
|
||||||
|
|
||||||
|
assert result.housenumber == '4'
|
||||||
|
assert result.postcode == '34425'
|
||||||
|
assert result.wikipedia == 'en:Faa'
|
||||||
|
|
||||||
|
assert result.rank_search == 27
|
||||||
|
assert result.rank_address == 26
|
||||||
|
assert result.importance == pytest.approx(0.01)
|
||||||
|
|
||||||
|
assert result.country_code == 'gb'
|
||||||
|
assert result.indexed_date == import_date
|
||||||
|
|
||||||
|
assert result.address_rows is None
|
||||||
|
assert result.linked_rows is None
|
||||||
|
assert result.parented_rows is None
|
||||||
|
assert result.name_keywords is None
|
||||||
|
assert result.address_keywords is None
|
||||||
|
|
||||||
|
assert result.geometry == {'type': 'ST_LineString'}
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_in_placex_minimal_info(apiobj):
|
||||||
|
import_date = dt.datetime(2022, 12, 7, 14, 14, 46, 0)
|
||||||
|
apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
|
||||||
|
class_='highway', type='residential',
|
||||||
|
admin_level=15,
|
||||||
|
rank_search=27, rank_address=26,
|
||||||
|
centroid=(23, 34),
|
||||||
|
indexed_date=import_date,
|
||||||
|
geometry='LINESTRING(23 34, 23.1 34, 23.1 34.1, 23 34)')
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(napi.PlaceID(332), napi.LookupDetails())
|
||||||
|
|
||||||
|
assert result is not None
|
||||||
|
|
||||||
|
assert result.source_table.name == 'PLACEX'
|
||||||
|
assert result.category == ('highway', 'residential')
|
||||||
|
assert result.centroid == (pytest.approx(23.0), pytest.approx(34.0))
|
||||||
|
|
||||||
|
assert result.place_id == 332
|
||||||
|
assert result.parent_place_id is None
|
||||||
|
assert result.linked_place_id is None
|
||||||
|
assert result.osm_object == ('W', 4)
|
||||||
|
assert result.admin_level == 15
|
||||||
|
|
||||||
|
assert result.names is None
|
||||||
|
assert result.address is None
|
||||||
|
assert result.extratags is None
|
||||||
|
|
||||||
|
assert result.housenumber is None
|
||||||
|
assert result.postcode is None
|
||||||
|
assert result.wikipedia is None
|
||||||
|
|
||||||
|
assert result.rank_search == 27
|
||||||
|
assert result.rank_address == 26
|
||||||
|
assert result.importance is None
|
||||||
|
|
||||||
|
assert result.country_code is None
|
||||||
|
assert result.indexed_date == import_date
|
||||||
|
|
||||||
|
assert result.address_rows is None
|
||||||
|
assert result.linked_rows is None
|
||||||
|
assert result.parented_rows is None
|
||||||
|
assert result.name_keywords is None
|
||||||
|
assert result.address_keywords is None
|
||||||
|
|
||||||
|
assert result.geometry == {'type': 'ST_LineString'}
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_in_placex_with_geometry(apiobj):
|
||||||
|
apiobj.add_placex(place_id=332,
|
||||||
|
geometry='LINESTRING(23 34, 23.1 34)')
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(napi.PlaceID(332),
|
||||||
|
napi.LookupDetails(geometry_output=napi.GeometryFormat.GEOJSON))
|
||||||
|
|
||||||
|
assert result.geometry == {'geojson': '{"type":"LineString","coordinates":[[23,34],[23.1,34]]}'}
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_placex_with_address_details(apiobj):
|
||||||
|
apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl',
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
apiobj.add_address_placex(332, fromarea=False, isaddress=False,
|
||||||
|
distance=0.0034,
|
||||||
|
place_id=1000, osm_type='N', osm_id=3333,
|
||||||
|
class_='place', type='suburb', name='Smallplace',
|
||||||
|
country_code='pl', admin_level=13,
|
||||||
|
rank_search=24, rank_address=23)
|
||||||
|
apiobj.add_address_placex(332, fromarea=True, isaddress=True,
|
||||||
|
place_id=1001, osm_type='N', osm_id=3334,
|
||||||
|
class_='place', type='city', name='Bigplace',
|
||||||
|
country_code='pl',
|
||||||
|
rank_search=17, rank_address=16)
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(napi.PlaceID(332),
|
||||||
|
napi.LookupDetails(address_details=True))
|
||||||
|
|
||||||
|
assert result.address_rows == [
|
||||||
|
napi.AddressLine(place_id=332, osm_object=('W', 4),
|
||||||
|
category=('highway', 'residential'),
|
||||||
|
names={'name': 'Street'}, extratags={},
|
||||||
|
admin_level=15, fromarea=True, isaddress=True,
|
||||||
|
rank_address=26, distance=0.0),
|
||||||
|
napi.AddressLine(place_id=1000, osm_object=('N', 3333),
|
||||||
|
category=('place', 'suburb'),
|
||||||
|
names={'name': 'Smallplace'}, extratags={},
|
||||||
|
admin_level=13, fromarea=False, isaddress=True,
|
||||||
|
rank_address=23, distance=0.0034),
|
||||||
|
napi.AddressLine(place_id=1001, osm_object=('N', 3334),
|
||||||
|
category=('place', 'city'),
|
||||||
|
names={'name': 'Bigplace'}, extratags={},
|
||||||
|
admin_level=15, fromarea=True, isaddress=True,
|
||||||
|
rank_address=16, distance=0.0),
|
||||||
|
napi.AddressLine(place_id=None, osm_object=None,
|
||||||
|
category=('place', 'country_code'),
|
||||||
|
names={'ref': 'pl'}, extratags={},
|
||||||
|
admin_level=None, fromarea=True, isaddress=False,
|
||||||
|
rank_address=4, distance=0.0)
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_place_wth_linked_places_none_existing(apiobj):
|
||||||
|
apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl', linked_place_id=45,
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(napi.PlaceID(332),
|
||||||
|
napi.LookupDetails(linked_places=True))
|
||||||
|
|
||||||
|
assert result.linked_rows == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_place_with_linked_places_existing(apiobj):
|
||||||
|
apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl', linked_place_id=45,
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
apiobj.add_placex(place_id=1001, osm_type='W', osm_id=5,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl', linked_place_id=332,
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
apiobj.add_placex(place_id=1002, osm_type='W', osm_id=6,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl', linked_place_id=332,
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(napi.PlaceID(332),
|
||||||
|
napi.LookupDetails(linked_places=True))
|
||||||
|
|
||||||
|
assert result.linked_rows == [
|
||||||
|
napi.AddressLine(place_id=1001, osm_object=('W', 5),
|
||||||
|
category=('highway', 'residential'),
|
||||||
|
names={'name': 'Street'}, extratags={},
|
||||||
|
admin_level=15, fromarea=False, isaddress=True,
|
||||||
|
rank_address=26, distance=0.0),
|
||||||
|
napi.AddressLine(place_id=1002, osm_object=('W', 6),
|
||||||
|
category=('highway', 'residential'),
|
||||||
|
names={'name': 'Street'}, extratags={},
|
||||||
|
admin_level=15, fromarea=False, isaddress=True,
|
||||||
|
rank_address=26, distance=0.0),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_place_with_parented_places_not_existing(apiobj):
|
||||||
|
apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl', parent_place_id=45,
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(napi.PlaceID(332),
|
||||||
|
napi.LookupDetails(parented_places=True))
|
||||||
|
|
||||||
|
assert result.parented_rows == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_lookup_place_with_parented_places_existing(apiobj):
|
||||||
|
apiobj.add_placex(place_id=332, osm_type='W', osm_id=4,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl', parent_place_id=45,
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
apiobj.add_placex(place_id=1001, osm_type='N', osm_id=5,
|
||||||
|
class_='place', type='house', housenumber='23',
|
||||||
|
country_code='pl', parent_place_id=332,
|
||||||
|
rank_search=30, rank_address=30)
|
||||||
|
apiobj.add_placex(place_id=1002, osm_type='W', osm_id=6,
|
||||||
|
class_='highway', type='residential', name='Street',
|
||||||
|
country_code='pl', parent_place_id=332,
|
||||||
|
rank_search=27, rank_address=26)
|
||||||
|
|
||||||
|
result = apiobj.api.lookup(napi.PlaceID(332),
|
||||||
|
napi.LookupDetails(parented_places=True))
|
||||||
|
|
||||||
|
assert result.parented_rows == [
|
||||||
|
napi.AddressLine(place_id=1001, osm_object=('N', 5),
|
||||||
|
category=('place', 'house'),
|
||||||
|
names={'housenumber': '23'}, extratags={},
|
||||||
|
admin_level=15, fromarea=False, isaddress=True,
|
||||||
|
rank_address=30, distance=0.0),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('gtype', (napi.GeometryFormat.KML,
|
||||||
|
napi.GeometryFormat.SVG,
|
||||||
|
napi.GeometryFormat.TEXT))
|
||||||
|
def test_lookup_unsupported_geometry(apiobj, gtype):
|
||||||
|
apiobj.add_placex(place_id=332)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
apiobj.api.lookup(napi.PlaceID(332),
|
||||||
|
napi.LookupDetails(geometry_output=gtype))
|
Loading…
Reference in New Issue
Block a user