]> git.openstreetmap.org Git - nominatim.git/commitdiff
extend BDD API tests to query via Python frameworks
authorSarah Hoffmann <lonvia@denofr.de>
Tue, 6 Dec 2022 10:20:50 +0000 (11:20 +0100)
committerSarah Hoffmann <lonvia@denofr.de>
Tue, 3 Jan 2023 09:03:00 +0000 (10:03 +0100)
A new config option ENGINE allows to choose between php and any of the
supported Python engines.

nominatim/server/falcon/server.py
nominatim/server/sanic/server.py
nominatim/server/starlette/server.py
test/bdd/environment.py
test/bdd/steps/nominatim_environment.py
test/bdd/steps/steps_api_queries.py

index ccb0164ce668175f4e60c7ad11eb81cac8054b96..b7b1aaa5bf5e70b4d9dd4e40fc33a94cbc043b71 100644 (file)
@@ -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')
 
index 8329950dbabdcf77d756af2c940f65666ecb1b7e..d00676b915eb65737767e66f0fe5d9da5ba77c87 100644 (file)
@@ -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)
index 38eac8dce24c66089b1dcd0c8c3a14ce5864de04..bed82f70f2f198c3a49a3519c4301a91caa8296b 100644 (file)
@@ -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
index 7a7b943d8e6af050fc6d9e8f89b9ceda9b1fc26f..305c88e962ef7c5be4962dca233276e0e7646f24 100644 (file)
@@ -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
 }
 
index 6179ca3404030e593552958356f85f65a950982c..158b7974dfcf7c96389235563bccf97bb4d2c657 100644 (file)
@@ -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
+
+
+
index 22517338bab04f664198222680f273ae358882a1..7bf38d14526f13f3e9273e3c795a01f16700f110 100644 (file)
@@ -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)