]> git.openstreetmap.org Git - nominatim.git/blob - test/python/api/test_result_formatting_v1_reverse.py
enable CI tests for Ubuntu 24
[nominatim.git] / test / python / api / test_result_formatting_v1_reverse.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) 2024 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     reverse.localize(napi.Locales())
105
106     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
107                                  {'addressdetails': True})
108
109
110     if fmt == 'xml':
111         root = ET.fromstring(raw)
112         assert root.find('addressparts').find('county').text == 'Hello'
113     else:
114         result = json.loads(raw)
115         assert isinstance(result, dict)
116
117         if fmt == 'geocodejson':
118             props = result['features'][0]['properties']['geocoding']
119             assert 'admin' in props
120             assert props['county'] == 'Hello'
121         else:
122             if fmt == 'geojson':
123                 props = result['features'][0]['properties']
124             else:
125                 props = result
126             assert 'address' in props
127
128
129 def test_format_reverse_geocodejson_special_parts():
130     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
131                                  ('place', 'house'),
132                                  napi.Point(1.0, 2.0),
133                                  place_id=33,
134                                  country_code='fe',
135                                  address_rows=napi.AddressLines([
136                                    napi.AddressLine(place_id=None,
137                                                     osm_object=None,
138                                                     category=('place', 'house_number'),
139                                                     names={'ref': '1'},
140                                                     extratags=None,
141                                                     admin_level=15,
142                                                     fromarea=False,
143                                                     isaddress=True,
144                                                     rank_address=10,
145                                                     distance=0.0),
146                                    napi.AddressLine(place_id=None,
147                                                     osm_object=None,
148                                                     category=('place', 'postcode'),
149                                                     names={'ref': '99446'},
150                                                     extratags=None,
151                                                     admin_level=11,
152                                                     fromarea=False,
153                                                     isaddress=True,
154                                                     rank_address=10,
155                                                     distance=0.0),
156                                    napi.AddressLine(place_id=33,
157                                                     osm_object=None,
158                                                     category=('place', 'county'),
159                                                     names={'name': 'Hello'},
160                                                     extratags=None,
161                                                     admin_level=5,
162                                                     fromarea=False,
163                                                     isaddress=True,
164                                                     rank_address=10,
165                                                     distance=0.0)
166                                  ]))
167
168     reverse.localize(napi.Locales())
169
170     raw = api_impl.format_result(napi.ReverseResults([reverse]), 'geocodejson',
171                                  {'addressdetails': True})
172
173     props = json.loads(raw)['features'][0]['properties']['geocoding']
174     assert props['housenumber'] == '1'
175     assert props['postcode'] == '99446'
176     assert 'county' not in props
177
178
179 @pytest.mark.parametrize('fmt', FORMATS)
180 def test_format_reverse_with_address_none(fmt):
181     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
182                                  ('place', 'thing'),
183                                  napi.Point(1.0, 2.0),
184                                  address_rows=napi.AddressLines())
185
186     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
187                                  {'addressdetails': True})
188
189
190     if fmt == 'xml':
191         root = ET.fromstring(raw)
192         assert root.find('addressparts') is None
193     else:
194         result = json.loads(raw)
195         assert isinstance(result, dict)
196
197         if fmt == 'geocodejson':
198             props = result['features'][0]['properties']['geocoding']
199             print(props)
200             assert 'admin' in props
201         else:
202             if fmt == 'geojson':
203                 props = result['features'][0]['properties']
204             else:
205                 props = result
206             assert 'address' in props
207
208
209 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
210 def test_format_reverse_with_extratags(fmt):
211     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
212                                  ('place', 'thing'),
213                                  napi.Point(1.0, 2.0),
214                                  extratags={'one': 'A', 'two':'B'})
215
216     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
217                                  {'extratags': True})
218
219     if fmt == 'xml':
220         root = ET.fromstring(raw)
221         assert root.find('extratags').find('tag').attrib['key'] == 'one'
222     else:
223         result = json.loads(raw)
224         if fmt == 'geojson':
225             extra = result['features'][0]['properties']['extratags']
226         else:
227             extra = result['extratags']
228
229         assert extra == {'one': 'A', 'two':'B'}
230
231
232 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
233 def test_format_reverse_with_extratags_none(fmt):
234     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
235                                  ('place', 'thing'),
236                                  napi.Point(1.0, 2.0))
237
238     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
239                                  {'extratags': True})
240
241     if fmt == 'xml':
242         root = ET.fromstring(raw)
243         assert root.find('extratags') is not None
244     else:
245         result = json.loads(raw)
246         if fmt == 'geojson':
247             extra = result['features'][0]['properties']['extratags']
248         else:
249             extra = result['extratags']
250
251         assert extra is None
252
253
254 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
255 def test_format_reverse_with_namedetails_with_name(fmt):
256     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
257                                  ('place', 'thing'),
258                                  napi.Point(1.0, 2.0),
259                                  names={'name': 'A', 'ref':'1'})
260
261     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
262                                  {'namedetails': True})
263
264     if fmt == 'xml':
265         root = ET.fromstring(raw)
266         assert root.find('namedetails').find('name').text == 'A'
267     else:
268         result = json.loads(raw)
269         if fmt == 'geojson':
270             extra = result['features'][0]['properties']['namedetails']
271         else:
272             extra = result['namedetails']
273
274         assert extra == {'name': 'A', 'ref':'1'}
275
276
277 @pytest.mark.parametrize('fmt', ['json', 'jsonv2', 'geojson', 'xml'])
278 def test_format_reverse_with_namedetails_without_name(fmt):
279     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
280                                  ('place', 'thing'),
281                                  napi.Point(1.0, 2.0))
282
283     raw = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
284                                  {'namedetails': True})
285
286     if fmt == 'xml':
287         root = ET.fromstring(raw)
288         assert root.find('namedetails') is not None
289     else:
290         result = json.loads(raw)
291         if fmt == 'geojson':
292             extra = result['features'][0]['properties']['namedetails']
293         else:
294             extra = result['namedetails']
295
296         assert extra is None
297
298
299 @pytest.mark.parametrize('fmt', ['json', 'jsonv2'])
300 def test_search_details_with_icon_available(fmt):
301     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
302                                  ('amenity', 'restaurant'),
303                                  napi.Point(1.0, 2.0))
304
305     result = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
306                                     {'icon_base_url': 'foo'})
307
308     js = json.loads(result)
309
310     assert js['icon'] == 'foo/food_restaurant.p.20.png'
311
312
313 @pytest.mark.parametrize('fmt', ['json', 'jsonv2'])
314 def test_search_details_with_icon_not_available(fmt):
315     reverse = napi.ReverseResult(napi.SourceTable.PLACEX,
316                                  ('amenity', 'tree'),
317                                  napi.Point(1.0, 2.0))
318
319     result = api_impl.format_result(napi.ReverseResults([reverse]), fmt,
320                                     {'icon_base_url': 'foo'})
321
322     assert 'icon' not in json.loads(result)
323