]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_result_formatting_v1_reverse.py
implement actual database searches
[nominatim.git] / test / python / api / test_result_formatting_v1_reverse.py
1 # SPDX-License-Identifier: GPL-2.0-only
2 #
3 # This file is part of Nominatim. (https://nominatim.org)
4 #
5 # Copyright (C) 2023 by the Nominatim developer community.
6 # For a full list of authors see the git log.
7 """
8 Tests for formatting reverse results for the V1 API.
9
10 These test only ensure that the Python code is correct.
11 For functional tests see BDD test suite.
12 """
13 import json
14 import xml.etree.ElementTree as ET
15
16 import pytest
17
18 import nominatim.api.v1 as api_impl
19 import nominatim.api as napi
20
21 FORMATS = ['json', 'jsonv2', 'geojson', 'geocodejson', 'xml']
22
23 @pytest.mark.parametrize('fmt', FORMATS)
24 def test_format_reverse_minimal(fmt):
25     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
26                                  ('amenity', 'post_box'),
27                                  napi.Point(0.3, -8.9))
28
29     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt, {})
30
31     if fmt == 'xml':
32         root = ET.fromstring(raw)
33         assert root.tag == 'reversegeocode'
34     else:
35         result = json.loads(raw)
36         assert isinstance(result, dict)
37
38
39 @pytest.mark.parametrize('fmt', FORMATS)
40 def test_format_reverse_no_result(fmt):
41     raw = api_impl.format_result(napi.ReverseResults(), fmt, {})
42
43     if fmt == 'xml':
44         root = ET.fromstring(raw)
45         assert root.find('error').text == 'Unable to geocode'
46     else:
47         assert json.loads(raw) == {'error': 'Unable to geocode'}
48
49
50 @pytest.mark.parametrize('fmt', FORMATS)
51 def test_format_reverse_with_osm_id(fmt):
52     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
53                                  ('amenity', 'post_box'),
54                                  napi.Point(0.3, -8.9),
55                                  place_id=5564,
56                                  osm_object=('N', 23))
57
58     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt, {})
59
60     if fmt == 'xml':
61         root = ET.fromstring(raw).find('result')
62         assert root.attrib['osm_type'] == 'node'
63         assert root.attrib['osm_id'] == '23'
64     else:
65         result = json.loads(raw)
66         if fmt == 'geocodejson':
67             props = result['features'][0]['properties']['geocoding']
68         elif fmt == 'geojson':
69             props = result['features'][0]['properties']
70         else:
71             props = result
72         assert props['osm_type'] == 'node'
73         assert props['osm_id'] == 23
74
75
76 @pytest.mark.parametrize('fmt', FORMATS)
77 def test_format_reverse_with_address(fmt):
78     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
79                                  ('place', 'thing'),
80                                  napi.Point(1.0, 2.0),
81                                  country_code='fe',
82                                  address_rows=napi.AddressLines([
83                                    napi.AddressLine(place_id=None,
84                                                     osm_object=None,
85                                                     category=('place', 'county'),
86                                                     names={'name': 'Hello'},
87                                                     extratags=None,
88                                                     admin_level=5,
89                                                     fromarea=False,
90                                                     isaddress=True,
91                                                     rank_address=10,
92                                                     distance=0.0),
93                                    napi.AddressLine(place_id=None,
94                                                     osm_object=None,
95                                                     category=('place', 'county'),
96                                                     names={'name': 'ByeBye'},
97                                                     extratags=None,
98                                                     admin_level=5,
99                                                     fromarea=False,
100                                                     isaddress=False,
101                                                     rank_address=10,
102                                                     distance=0.0)
103                                  ]))
104
105     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
106                                  {'addressdetails': True})
107
108
109     if fmt == 'xml':
110         root = ET.fromstring(raw)
111         assert root.find('addressparts').find('county').text == 'Hello'
112     else:
113         result = json.loads(raw)
114         assert isinstance(result, dict)
115
116         if fmt == 'geocodejson':
117             props = result['features'][0]['properties']['geocoding']
118             assert 'admin' in props
119             assert props['county'] == 'Hello'
120         else:
121             if fmt == 'geojson':
122                 props = result['features'][0]['properties']
123             else:
124                 props = result
125             assert 'address' in props
126
127
128 def test_format_reverse_geocodejson_special_parts():
129     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
130                                  ('place', 'house'),
131                                  napi.Point(1.0, 2.0),
132                                  place_id=33,
133                                  country_code='fe',
134                                  address_rows=napi.AddressLines([
135                                    napi.AddressLine(place_id=None,
136                                                     osm_object=None,
137                                                     category=('place', 'house_number'),
138                                                     names={'ref': '1'},
139                                                     extratags=None,
140                                                     admin_level=15,
141                                                     fromarea=False,
142                                                     isaddress=True,
143                                                     rank_address=10,
144                                                     distance=0.0),
145                                    napi.AddressLine(place_id=None,
146                                                     osm_object=None,
147                                                     category=('place', 'postcode'),
148                                                     names={'ref': '99446'},
149                                                     extratags=None,
150                                                     admin_level=11,
151                                                     fromarea=False,
152                                                     isaddress=True,
153                                                     rank_address=10,
154                                                     distance=0.0),
155                                    napi.AddressLine(place_id=33,
156                                                     osm_object=None,
157                                                     category=('place', 'county'),
158                                                     names={'name': 'Hello'},
159                                                     extratags=None,
160                                                     admin_level=5,
161                                                     fromarea=False,
162                                                     isaddress=True,
163                                                     rank_address=10,
164                                                     distance=0.0)
165                                  ]))
166
167     raw = api_impl.format_result(napi.ReverseResults([reverse]), 'geocodejson',
168                                  {'addressdetails': True})
169
170     props = json.loads(raw)['features'][0]['properties']['geocoding']
171     assert props['housenumber'] == '1'
172     assert props['postcode'] == '99446'
173     assert 'county' not in props
174
175
176 @pytest.mark.parametrize('fmt', FORMATS)
177 def test_format_reverse_with_address_none(fmt):
178     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
179                                  ('place', 'thing'),
180                                  napi.Point(1.0, 2.0),
181                                  address_rows=napi.AddressLines())
182
183     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
184                                  {'addressdetails': True})
185
186
187     if fmt == 'xml':
188         root = ET.fromstring(raw)
189         assert root.find('addressparts') is None
190     else:
191         result = json.loads(raw)
192         assert isinstance(result, dict)
193
194         if fmt == 'geocodejson':
195             props = result['features'][0]['properties']['geocoding']
196             print(props)
197             assert 'admin' in props
198         else:
199             if fmt == 'geojson':
200                 props = result['features'][0]['properties']
201             else:
202                 props = result
203             assert 'address' in props
204
205
206 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
207 def test_format_reverse_with_extratags(fmt):
208     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
209                                  ('place', 'thing'),
210                                  napi.Point(1.0, 2.0),
211                                  extratags={'one': 'A', 'two':'B'})
212
213     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
214                                  {'extratags': True})
215
216     if fmt == 'xml':
217         root = ET.fromstring(raw)
218         assert root.find('extratags').find('tag').attrib['key'] == 'one'
219     else:
220         result = json.loads(raw)
221         if fmt == 'geojson':
222             extra = result['features'][0]['properties']['extratags']
223         else:
224             extra = result['extratags']
225
226         assert extra == {'one': 'A', 'two':'B'}
227
228
229 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
230 def test_format_reverse_with_extratags_none(fmt):
231     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
232                                  ('place', 'thing'),
233                                  napi.Point(1.0, 2.0))
234
235     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
236                                  {'extratags': True})
237
238     if fmt == 'xml':
239         root = ET.fromstring(raw)
240         assert root.find('extratags') is not None
241     else:
242         result = json.loads(raw)
243         if fmt == 'geojson':
244             extra = result['features'][0]['properties']['extratags']
245         else:
246             extra = result['extratags']
247
248         assert extra is None
249
250
251 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
252 def test_format_reverse_with_namedetails_with_name(fmt):
253     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
254                                  ('place', 'thing'),
255                                  napi.Point(1.0, 2.0),
256                                  names={'name': 'A', 'ref':'1'})
257
258     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
259                                  {'namedetails': True})
260
261     if fmt == 'xml':
262         root = ET.fromstring(raw)
263         assert root.find('namedetails').find('name').text == 'A'
264     else:
265         result = json.loads(raw)
266         if fmt == 'geojson':
267             extra = result['features'][0]['properties']['namedetails']
268         else:
269             extra = result['namedetails']
270
271         assert extra == {'name': 'A', 'ref':'1'}
272
273
274 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
275 def test_format_reverse_with_namedetails_without_name(fmt):
276     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
277                                  ('place', 'thing'),
278                                  napi.Point(1.0, 2.0))
279
280     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
281                                  {'namedetails': True})
282
283     if fmt == 'xml':
284         root = ET.fromstring(raw)
285         assert root.find('namedetails') is not None
286     else:
287         result = json.loads(raw)
288         if fmt == 'geojson':
289             extra = result['features'][0]['properties']['namedetails']
290         else:
291             extra = result['namedetails']
292
293         assert extra is None
294
295
296 @pytest.mark.parametrize('fmt', ['json', 'jsonv2'])
297 def test_search_details_with_icon_available(fmt):
298     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
299                                  ('amenity', 'restaurant'),
300                                  napi.Point(1.0, 2.0))
301
302     result = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
303                                     {'icon_base_url': 'foo'})
304
305     js = json.loads(result)
306
307     assert js['icon'] == 'foo/food_restaurant.p.20.png'
308
309
310 @pytest.mark.parametrize('fmt', ['json', 'jsonv2'])
311 def test_search_details_with_icon_not_available(fmt):
312     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
313                                  ('amenity', 'tree'),
314                                  napi.Point(1.0, 2.0))
315
316     result = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
317                                     {'icon_base_url': 'foo'})
318
319     assert 'icon' not in json.loads(result)
320