2021-01-27 00:45:24 +03:00
|
|
|
import itertools
|
2021-01-15 16:42:03 +03:00
|
|
|
import sys
|
|
|
|
from pathlib import Path
|
|
|
|
|
2021-01-20 22:24:39 +03:00
|
|
|
import psycopg2
|
2021-01-23 19:25:14 +03:00
|
|
|
import psycopg2.extras
|
2021-01-20 22:24:39 +03:00
|
|
|
import pytest
|
|
|
|
|
2021-01-23 19:25:14 +03:00
|
|
|
SRC_DIR = Path(__file__) / '..' / '..' / '..'
|
|
|
|
|
2021-01-15 16:42:03 +03:00
|
|
|
# always test against the source
|
2021-01-23 19:25:14 +03:00
|
|
|
sys.path.insert(0, str(SRC_DIR.resolve()))
|
|
|
|
|
|
|
|
from nominatim.config import Configuration
|
2021-01-27 00:45:24 +03:00
|
|
|
from nominatim.db import connection
|
2021-01-23 19:25:14 +03:00
|
|
|
|
|
|
|
class _TestingCursor(psycopg2.extras.DictCursor):
|
|
|
|
""" Extension to the DictCursor class that provides execution
|
|
|
|
short-cuts that simplify writing assertions.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def scalar(self, sql, params=None):
|
|
|
|
""" Execute a query with a single return value and return this value.
|
|
|
|
Raises an assertion when not exactly one row is returned.
|
|
|
|
"""
|
|
|
|
self.execute(sql, params)
|
|
|
|
assert self.rowcount == 1
|
|
|
|
return self.fetchone()[0]
|
|
|
|
|
|
|
|
def row_set(self, sql, params=None):
|
|
|
|
""" Execute a query and return the result as a set of tuples.
|
|
|
|
"""
|
|
|
|
self.execute(sql, params)
|
|
|
|
if self.rowcount == 1:
|
|
|
|
return set(tuple(self.fetchone()))
|
|
|
|
|
|
|
|
return set((tuple(row) for row in self))
|
2021-01-15 16:42:03 +03:00
|
|
|
|
2021-02-17 23:43:15 +03:00
|
|
|
def table_exists(self, table):
|
|
|
|
""" Check that a table with the given name exists in the database.
|
|
|
|
"""
|
|
|
|
num = self.scalar("""SELECT count(*) FROM pg_tables
|
|
|
|
WHERE tablename = %s""", (table, ))
|
|
|
|
return num == 1
|
|
|
|
|
|
|
|
|
2021-01-20 22:24:39 +03:00
|
|
|
@pytest.fixture
|
|
|
|
def temp_db(monkeypatch):
|
2021-01-23 19:25:14 +03:00
|
|
|
""" Create an empty database for the test. The database name is also
|
|
|
|
exported into NOMINATIM_DATABASE_DSN.
|
|
|
|
"""
|
2021-01-20 22:24:39 +03:00
|
|
|
name = 'test_nominatim_python_unittest'
|
2021-01-27 00:45:24 +03:00
|
|
|
conn = psycopg2.connect(database='postgres')
|
|
|
|
|
|
|
|
conn.set_isolation_level(0)
|
|
|
|
with conn.cursor() as cur:
|
|
|
|
cur.execute('DROP DATABASE IF EXISTS {}'.format(name))
|
|
|
|
cur.execute('CREATE DATABASE {}'.format(name))
|
|
|
|
|
|
|
|
conn.close()
|
2021-01-20 22:24:39 +03:00
|
|
|
|
|
|
|
monkeypatch.setenv('NOMINATIM_DATABASE_DSN' , 'dbname=' + name)
|
|
|
|
|
|
|
|
yield name
|
|
|
|
|
2021-01-27 00:45:24 +03:00
|
|
|
conn = psycopg2.connect(database='postgres')
|
2021-01-23 19:25:14 +03:00
|
|
|
|
2021-01-27 00:45:24 +03:00
|
|
|
conn.set_isolation_level(0)
|
|
|
|
with conn.cursor() as cur:
|
|
|
|
cur.execute('DROP DATABASE IF EXISTS {}'.format(name))
|
|
|
|
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def temp_db_with_extensions(temp_db):
|
|
|
|
conn = psycopg2.connect(database=temp_db)
|
|
|
|
with conn.cursor() as cur:
|
|
|
|
cur.execute('CREATE EXTENSION hstore; CREATE EXTENSION postgis;')
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
return temp_db
|
2021-01-23 19:25:14 +03:00
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def temp_db_conn(temp_db):
|
|
|
|
""" Connection to the test database.
|
|
|
|
"""
|
2021-02-23 12:11:21 +03:00
|
|
|
with connection.connect('dbname=' + temp_db) as conn:
|
|
|
|
yield conn
|
2021-01-23 19:25:14 +03:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def temp_db_cursor(temp_db):
|
|
|
|
""" Connection and cursor towards the test database. The connection will
|
|
|
|
be in auto-commit mode.
|
|
|
|
"""
|
|
|
|
conn = psycopg2.connect('dbname=' + temp_db)
|
|
|
|
conn.set_isolation_level(0)
|
|
|
|
with conn.cursor(cursor_factory=_TestingCursor) as cur:
|
|
|
|
yield cur
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def def_config():
|
|
|
|
return Configuration(None, SRC_DIR.resolve() / 'settings')
|
2021-01-27 00:45:24 +03:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def status_table(temp_db_conn):
|
2021-01-30 17:50:34 +03:00
|
|
|
""" Create an empty version of the status table and
|
|
|
|
the status logging table.
|
2021-01-27 00:45:24 +03:00
|
|
|
"""
|
|
|
|
with temp_db_conn.cursor() as cur:
|
|
|
|
cur.execute("""CREATE TABLE import_status (
|
|
|
|
lastimportdate timestamp with time zone NOT NULL,
|
|
|
|
sequence_id integer,
|
|
|
|
indexed boolean
|
|
|
|
)""")
|
2021-01-30 17:50:34 +03:00
|
|
|
cur.execute("""CREATE TABLE import_osmosis_log (
|
|
|
|
batchend timestamp,
|
|
|
|
batchseq integer,
|
|
|
|
batchsize bigint,
|
|
|
|
starttime timestamp,
|
|
|
|
endtime timestamp,
|
|
|
|
event text
|
|
|
|
)""")
|
2021-01-27 00:45:24 +03:00
|
|
|
temp_db_conn.commit()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def place_table(temp_db_with_extensions, temp_db_conn):
|
|
|
|
""" Create an empty version of the place table.
|
|
|
|
"""
|
|
|
|
with temp_db_conn.cursor() as cur:
|
|
|
|
cur.execute("""CREATE TABLE place (
|
|
|
|
osm_id int8 NOT NULL,
|
|
|
|
osm_type char(1) NOT NULL,
|
|
|
|
class text NOT NULL,
|
|
|
|
type text NOT NULL,
|
|
|
|
name hstore,
|
|
|
|
admin_level smallint,
|
|
|
|
address hstore,
|
|
|
|
extratags hstore,
|
|
|
|
geometry Geometry(Geometry,4326) NOT NULL)""")
|
|
|
|
temp_db_conn.commit()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def place_row(place_table, temp_db_cursor):
|
|
|
|
""" A factory for rows in the place table. The table is created as a
|
|
|
|
prerequisite to the fixture.
|
|
|
|
"""
|
|
|
|
idseq = itertools.count(1001)
|
|
|
|
def _insert(osm_type='N', osm_id=None, cls='amenity', typ='cafe', names=None,
|
|
|
|
admin_level=None, address=None, extratags=None, geom=None):
|
|
|
|
temp_db_cursor.execute("INSERT INTO place VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
|
|
|
(osm_id or next(idseq), osm_type, cls, typ, names,
|
|
|
|
admin_level, address, extratags,
|
|
|
|
geom or 'SRID=4326;POINT(0 0 )'))
|
|
|
|
|
|
|
|
return _insert
|
2021-02-09 00:21:57 +03:00
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def placex_table(temp_db_with_extensions, temp_db_conn):
|
|
|
|
""" Create an empty version of the place table.
|
|
|
|
"""
|
|
|
|
with temp_db_conn.cursor() as cur:
|
|
|
|
cur.execute("""CREATE TABLE placex (
|
|
|
|
place_id BIGINT NOT NULL,
|
|
|
|
parent_place_id BIGINT,
|
|
|
|
linked_place_id BIGINT,
|
|
|
|
importance FLOAT,
|
|
|
|
indexed_date TIMESTAMP,
|
|
|
|
geometry_sector INTEGER,
|
|
|
|
rank_address SMALLINT,
|
|
|
|
rank_search SMALLINT,
|
|
|
|
partition SMALLINT,
|
|
|
|
indexed_status SMALLINT,
|
|
|
|
osm_id int8,
|
|
|
|
osm_type char(1),
|
|
|
|
class text,
|
|
|
|
type text,
|
|
|
|
name hstore,
|
|
|
|
admin_level smallint,
|
|
|
|
address hstore,
|
|
|
|
extratags hstore,
|
|
|
|
geometry Geometry(Geometry,4326),
|
|
|
|
wikipedia TEXT,
|
|
|
|
country_code varchar(2),
|
|
|
|
housenumber TEXT,
|
|
|
|
postcode TEXT,
|
|
|
|
centroid GEOMETRY(Geometry, 4326))
|
|
|
|
""")
|
|
|
|
temp_db_conn.commit()
|
|
|
|
|
|
|
|
|
|
|
|
|