]> git.openstreetmap.org Git - nominatim.git/blob - test/bdd/test_api.py
release 5.1.0.post8
[nominatim.git] / test / bdd / test_api.py
1 # SPDX-License-Identifier: GPL-3.0-or-later
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2025 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Collector for all BDD API tests.
9
10 These tests work on a static test database that is the same for all tests.
11 The source data for the database can be found in the test/testdb directory.
12 """
13 from pathlib import Path
14 import xml.etree.ElementTree as ET
15
16 import pytest
17 from pytest_bdd.parsers import re as step_parse
18 from pytest_bdd import scenarios, when, given, then
19
20 from nominatim_db import cli
21 from nominatim_db.config import Configuration
22
23 from utils.db import DBManager
24 from utils.api_runner import APIRunner
25 from utils.api_result import APIResult
26
27
28 TESTDB_PATH = (Path(__file__) / '..' / '..' / 'testdb').resolve()
29
30 CONTENT_TYPES = {
31     'json': 'application/json; charset=utf-8',
32     'xml': 'text/xml; charset=utf-8',
33     'geojson': 'application/json; charset=utf-8',
34     'geocodejson': 'application/json; charset=utf-8',
35     'html': 'text/html; charset=utf-8'
36 }
37
38
39 @pytest.fixture(autouse=True, scope='session')
40 def session_api_test_db(pytestconfig):
41     """ Create a Nominatim database from the official API test data.
42         Will only recreate an existing database if --nominatim-purge
43         was set.
44     """
45     dbname = pytestconfig.getini('nominatim_api_test_db')
46
47     config = Configuration(None).get_os_env()
48     config['NOMINATIM_DATABASE_DSN'] = f"pgsql:dbname={dbname}"
49     config['NOMINATIM_LANGUAGES'] = 'en,de,fr,ja'
50     config['NOMINATIM_USE_US_TIGER_DATA'] = 'yes'
51     if pytestconfig.option.NOMINATIM_TOKENIZER is not None:
52         config['NOMINATIM_TOKENIZER'] = pytestconfig.option.NOMINATIM_TOKENIZER
53
54     dbm = DBManager(purge=pytestconfig.option.NOMINATIM_PURGE)
55
56     if not dbm.check_for_db(dbname):
57         try:
58             cli.nominatim(cli_args=['import', '--project-dir', str(TESTDB_PATH),
59                                     '--osm-file', str(TESTDB_PATH / 'apidb-test-data.pbf')],
60                           environ=config)
61             cli.nominatim(cli_args=['add-data', '--project-dir', str(TESTDB_PATH),
62                                     '--tiger-data', str(TESTDB_PATH / 'tiger')],
63                           environ=config)
64             cli.nominatim(cli_args=['freeze', '--project-dir', str(TESTDB_PATH)],
65                           environ=config)
66             cli.nominatim(cli_args=['special-phrases', '--project-dir', str(TESTDB_PATH),
67                                     '--import-from-csv',
68                                     str(TESTDB_PATH / 'full_en_phrases_test.csv')],
69                           environ=config)
70         except:  # noqa: E722
71             dbm.drop_db(dbname)
72             raise
73
74
75 @pytest.fixture
76 def test_config_env(pytestconfig):
77     dbname = pytestconfig.getini('nominatim_api_test_db')
78
79     config = Configuration(None).get_os_env()
80     config['NOMINATIM_DATABASE_DSN'] = f"pgsql:dbname={dbname}"
81     config['NOMINATIM_LANGUAGES'] = 'en,de,fr,ja'
82     config['NOMINATIM_USE_US_TIGER_DATA'] = 'yes'
83     if pytestconfig.option.NOMINATIM_TOKENIZER is not None:
84         config['NOMINATIM_TOKENIZER'] = pytestconfig.option.NOMINATIM_TOKENIZER
85
86     return config
87
88
89 @pytest.fixture
90 def api_http_request_headers():
91     return {}
92
93
94 @given('the HTTP header', target_fixture='api_http_request_headers')
95 def set_additional_http_headers(api_http_request_headers, datatable):
96     api_http_request_headers.update(zip(datatable[0], datatable[1]))
97     return api_http_request_headers
98
99
100 @given('an unknown database', target_fixture='test_config_env')
101 def setup_connection_unknown_database(test_config_env):
102     test_config_env['NOMINATIM_DATABASE_DSN'] = "pgsql:dbname=gerlkghngergn6732nf"
103     return test_config_env
104
105
106 @when(step_parse(r'sending v1/(?P<endpoint>\S+)(?: with format (?P<fmt>\S+))?'),
107       target_fixture='api_response')
108 def send_api_status(test_config_env, api_http_request_headers, pytestconfig,
109                     datatable, endpoint, fmt):
110     runner = APIRunner(test_config_env, pytestconfig.option.NOMINATIM_API_ENGINE)
111     return runner.run_step(endpoint, {}, datatable, fmt, api_http_request_headers)
112
113
114 @then(step_parse(r'a HTTP (?P<status>\d+) is returned'), converters={'status': int})
115 def check_http_result(api_response, status):
116     assert api_response.status == status
117
118
119 @then(step_parse('the page content equals "(?P<content>.*)"'))
120 def check_page_content_exact(api_response, content):
121     assert api_response.body == content
122
123
124 @then('the result is valid html')
125 def check_for_html_correctness(api_response):
126     assert api_response.headers['content-type'] == CONTENT_TYPES['html']
127
128     try:
129         tree = ET.fromstring(api_response.body)
130     except Exception as ex:
131         assert False, f"Could not parse page: {ex}\n{api_response.body}"
132
133     assert tree.tag == 'html'
134
135     body = tree.find('./body')
136     assert body is not None
137     assert body.find('.//script') is None
138
139
140 @then(step_parse(r'the result is valid (?P<fmt>\S+)(?: with (?P<num>\d+) results?)?'),
141       target_fixture='nominatim_result')
142 def parse_api_json_response(api_response, fmt, num):
143     assert api_response.headers['content-type'] == CONTENT_TYPES[fmt]
144
145     result = APIResult(fmt, api_response.endpoint, api_response.body)
146
147     if num:
148         assert len(result) == int(num)
149
150     return result
151
152
153 scenarios('features/api')