From: Sarah Hoffmann Date: Tue, 6 Dec 2022 10:20:50 +0000 (+0100) Subject: extend BDD API tests to query via Python frameworks X-Git-Tag: v4.3.0~118^2~10 X-Git-Url: https://git.openstreetmap.org./nominatim.git/commitdiff_plain/7219ee653276359d4485ff8b89e1841e42af71c5 extend BDD API tests to query via Python frameworks A new config option ENGINE allows to choose between php and any of the supported Python engines. --- diff --git a/nominatim/server/falcon/server.py b/nominatim/server/falcon/server.py index ccb0164c..b7b1aaa5 100644 --- a/nominatim/server/falcon/server.py +++ b/nominatim/server/falcon/server.py @@ -7,7 +7,7 @@ """ Server implementation using the falcon webserver framework. """ -from typing import Type, Any +from typing import Type, Any, Optional, Mapping from pathlib import Path import falcon @@ -26,8 +26,8 @@ class NominatimV1: """ Implementation of V1 version of the Nominatim API. """ - def __init__(self, project_dir: Path) -> None: - self.api = NominatimAPIAsync(project_dir) + def __init__(self, project_dir: Path, environ: Optional[Mapping[str, str]]) -> None: + self.api = NominatimAPIAsync(project_dir, environ) self.formatters = {} for rtype in (StatusResult, ): @@ -67,12 +67,13 @@ class NominatimV1: self.format_response(req, resp, result) -def get_application(project_dir: Path) -> falcon.asgi.App: +def get_application(project_dir: Path, + environ: Optional[Mapping[str, str]] = None) -> falcon.asgi.App: """ Create a Nominatim falcon ASGI application. """ app = falcon.asgi.App() - api = NominatimV1(project_dir) + api = NominatimV1(project_dir, environ) app.add_route('/status', api, suffix='status') diff --git a/nominatim/server/sanic/server.py b/nominatim/server/sanic/server.py index 8329950d..d00676b9 100644 --- a/nominatim/server/sanic/server.py +++ b/nominatim/server/sanic/server.py @@ -7,7 +7,7 @@ """ Server implementation using the sanic webserver framework. """ -from typing import Any, Optional +from typing import Any, Optional, Mapping from pathlib import Path import sanic @@ -64,12 +64,13 @@ async def status(request: sanic.Request) -> sanic.HTTPResponse: return api_response(request,await request.app.ctx.api.status()) -def get_application(project_dir: Path) -> sanic.Sanic: +def get_application(project_dir: Path, + environ: Optional[Mapping[str, str]] = None) -> sanic.Sanic: """ Create a Nominatim sanic ASGI application. """ app = sanic.Sanic("NominatimInstance") - app.ctx.api = NominatimAPIAsync(project_dir) + app.ctx.api = NominatimAPIAsync(project_dir, environ) app.ctx.formatters = {} for rtype in (StatusResult, ): app.ctx.formatters[rtype] = formatting.create(rtype) diff --git a/nominatim/server/starlette/server.py b/nominatim/server/starlette/server.py index 38eac8dc..bed82f70 100644 --- a/nominatim/server/starlette/server.py +++ b/nominatim/server/starlette/server.py @@ -7,7 +7,7 @@ """ Server implementation using the starlette webserver framework. """ -from typing import Any, Type +from typing import Any, Type, Optional, Mapping from pathlib import Path from starlette.applications import Starlette @@ -67,11 +67,12 @@ V1_ROUTES = [ Route('/status', endpoint=on_status) ] -def get_application(project_dir: Path) -> Starlette: +def get_application(project_dir: Path, + environ: Optional[Mapping[str, str]] = None) -> Starlette: """ Create a Nominatim falcon ASGI application. """ app = Starlette(debug=True, routes=V1_ROUTES) - app.state.API = NominatimAPIAsync(project_dir) + app.state.API = NominatimAPIAsync(project_dir, environ) return app diff --git a/test/bdd/environment.py b/test/bdd/environment.py index 7a7b943d..305c88e9 100644 --- a/test/bdd/environment.py +++ b/test/bdd/environment.py @@ -28,6 +28,7 @@ userconfig = { 'SERVER_MODULE_PATH' : None, 'TOKENIZER' : None, # Test with a custom tokenizer 'STYLE' : 'extratags', + 'API_ENGINE': 'php', 'PHPCOV' : False, # set to output directory to enable code coverage } diff --git a/test/bdd/steps/nominatim_environment.py b/test/bdd/steps/nominatim_environment.py index 6179ca34..158b7974 100644 --- a/test/bdd/steps/nominatim_environment.py +++ b/test/bdd/steps/nominatim_environment.py @@ -5,9 +5,13 @@ # Copyright (C) 2022 by the Nominatim developer community. # For a full list of authors see the git log. from pathlib import Path +import importlib import sys import tempfile +from asgi_lifespan import LifespanManager +import httpx + import psycopg2 import psycopg2.extras @@ -49,6 +53,12 @@ class NominatimEnvironment: self.api_db_done = False self.website_dir = None + self.api_engine = None + if config['API_ENGINE'] != 'php': + if not hasattr(self, f"create_api_request_func_{config['API_ENGINE']}"): + raise RuntimeError(f"Unknown API engine '{config['API_ENGINE']}'") + self.api_engine = getattr(self, f"create_api_request_func_{config['API_ENGINE']}")() + def connect_database(self, dbname): """ Return a connection to the database with the given name. Uses configured host, user and port. @@ -323,3 +333,49 @@ class NominatimEnvironment: WHERE class='place' and type='houses' and osm_type='W' and ST_GeometryType(geometry) = 'ST_LineString'""") + + + def create_api_request_func_starlette(self): + import nominatim.server.starlette.server + + async def _request(endpoint, params, project_dir, environ): + app = nominatim.server.starlette.server.get_application(project_dir, environ) + + async with LifespanManager(app): + async with httpx.AsyncClient(app=app, base_url="http://nominatim.test") as client: + response = await client.get(f"/{endpoint}", params=params) + + return response.text, response.status_code + + return _request + + + def create_api_request_func_sanic(self): + import nominatim.server.sanic.server + + async def _request(endpoint, params, project_dir, environ): + app = nominatim.server.sanic.server.get_application(project_dir, environ) + + _, response = await app.asgi_client.get(f"/{endpoint}", params=params) + + return response.text, response.status_code + + return _request + + + def create_api_request_func_falcon(self): + import nominatim.server.falcon.server + import falcon.testing + + async def _request(endpoint, params, project_dir, environ): + app = nominatim.server.falcon.server.get_application(project_dir, environ) + + async with falcon.testing.ASGIConductor(app) as conductor: + response = await conductor.get(f"/{endpoint}", params=params) + + return response.text, response.status_code + + return _request + + + diff --git a/test/bdd/steps/steps_api_queries.py b/test/bdd/steps/steps_api_queries.py index 22517338..7bf38d14 100644 --- a/test/bdd/steps/steps_api_queries.py +++ b/test/bdd/steps/steps_api_queries.py @@ -9,10 +9,12 @@ Queries may either be run directly via PHP using the query script or via the HTTP interface using php-cgi. """ +from pathlib import Path import json import os import re import logging +import asyncio from urllib.parse import urlencode from utils import run_script @@ -72,6 +74,16 @@ def send_api_query(endpoint, params, fmt, context): for h in context.table.headings: params[h] = context.table[0][h] + if context.nominatim.api_engine is None: + return send_api_query_php(endpoint, params, context) + + return asyncio.run(context.nominatim.api_engine(endpoint, params, + Path(context.nominatim.website_dir.name), + context.nominatim.test_env)) + + + +def send_api_query_php(endpoint, params, context): env = dict(BASE_SERVER_ENV) env['QUERY_STRING'] = urlencode(params)