diff --git a/lib/SearchDescription.php b/lib/SearchDescription.php index f0106063..2053082f 100644 --- a/lib/SearchDescription.php +++ b/lib/SearchDescription.php @@ -86,18 +86,6 @@ class SearchDescription $this->sType = $sType; } - /** - * Check if this might be a full address search. - * - * @return bool True if the search contains name, address and housenumber. - */ - public function looksLikeFullAddress() - { - return (!empty($this->aName)) - && (!empty($this->aAddress) || $this->sCountryCode) - && preg_match('/[0-9]+/', $this->sHouseNumber); - } - /** * Check if any operator is set. * @@ -1027,7 +1015,7 @@ class SearchDescription 'Name terms (stop words)' => $this->aNameNonSearch, 'Address terms' => $this->aAddress, 'Address terms (stop words)' => $this->aAddressNonSearch, - 'Address terms (full words)' => $this->aFullNameAddress, + 'Address terms (full words)' => $this->aFullNameAddress ?? '', 'Special search' => $this->iOperator, 'Class' => $this->sClass, 'Type' => $this->sType, @@ -1039,7 +1027,7 @@ class SearchDescription public function dumpAsHtmlTableRow(&$aWordIDs) { $kf = function ($k) use (&$aWordIDs) { - return $aWordIDs[$k]; + return $aWordIDs[$k] ?? '['.$k.']'; }; echo ''; diff --git a/lib/output.php b/lib/output.php index 823a6631..8de81576 100644 --- a/lib/output.php +++ b/lib/output.php @@ -16,58 +16,3 @@ function formatOSMType($sType, $bIncludeExternal = true) return ''; } - -function osmLink($aFeature, $sRefText = false) -{ - $sOSMType = formatOSMType($aFeature['osm_type'], false); - if ($sOSMType) { - return ''.$sOSMType.' '.($sRefText?$sRefText:$aFeature['osm_id']).''; - } - return ''; -} - -function wikipediaLink($aFeature) -{ - if ($aFeature['wikipedia']) { - list($sLanguage, $sArticle) = explode(':', $aFeature['wikipedia']); - return ''.$aFeature['wikipedia'].''; - } - return ''; -} - -function detailsLink($aFeature, $sTitle = false, $sExtraProperties = false) -{ - if (!$aFeature['place_id']) return ''; - - $sHtml = ''.($sTitle?$sTitle:$aFeature['place_id']).''; - - return $sHtml; -} - -function detailsPermaLink($aFeature, $sRefText = false, $sExtraProperties = false) -{ - $sOSMType = formatOSMType($aFeature['osm_type'], false); - - if ($sOSMType) { - $sHtml = ''; - - if ($sRefText) { - $sHtml .= $sRefText.''; - } else { - $sHtml .= $sOSMType.' '.$aFeature['osm_id'].''; - } - - return $sHtml; - } - return detailsLink($aFeature, $sRefText, $sExtraProperties); -} diff --git a/test/bdd/api/details/language.feature b/test/bdd/api/details/language.feature new file mode 100644 index 00000000..6611c81b --- /dev/null +++ b/test/bdd/api/details/language.feature @@ -0,0 +1,62 @@ +@APIDB +Feature: Localization of search results + + Scenario: default language + When sending details query for R1155955 + Then results contain + | ID | localname | + | 0 | Liechtenstein | + + Scenario: accept-language first + When sending details query for R1155955 + | accept-language | + | zh,de | + Then results contain + | ID | localname | + | 0 | 列支敦士登 | + + Scenario: accept-language missing + When sending details query for R1155955 + | accept-language | + | xx,fr,en,de | + Then results contain + | ID | localname | + | 0 | Liechtenstein | + + Scenario: http accept language header first + Given the HTTP header + | accept-language | + | fo;q=0.8,en-ca;q=0.5,en;q=0.3 | + When sending details query for R1155955 + Then results contain + | ID | localname | + | 0 | Liktinstein | + + Scenario: http accept language header and accept-language + Given the HTTP header + | accept-language | + | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 | + When sending details query for R1155955 + | accept-language | + | fo,en | + Then results contain + | ID | localname | + | 0 | Liktinstein | + + Scenario: http accept language header fallback + Given the HTTP header + | accept-language | + | fo-ca,en-ca;q=0.5 | + When sending details query for R1155955 + Then results contain + | ID | localname | + | 0 | Liktinstein | + + Scenario: http accept language header fallback (upper case) + Given the HTTP header + | accept-language | + | fo-FR;q=0.8,en-ca;q=0.5 | + When sending details query for R1155955 + Then results contain + | ID | localname | + | 0 | Liktinstein | diff --git a/test/bdd/api/details/params.feature b/test/bdd/api/details/params.feature index 03b91d29..87c3356c 100644 --- a/test/bdd/api/details/params.feature +++ b/test/bdd/api/details/params.feature @@ -8,14 +8,15 @@ Feature: Object details And result has attributes geometry And result has not attributes keywords,address,linked_places,parentof - Scenario: JSON Details with keywords + Scenario: JSON Details with pretty printing When sending json details query for W297699560 - | keywords | - | 1 | + | pretty | + | 1 | Then the result is valid json - And result has attributes keywords + And result has attributes geometry + And result has not attributes keywords,address,linked_places,parentof - Scenario: JSON Details with addressdetails + Scenario: JSON Details with addressdetails When sending json details query for W297699560 | addressdetails | | 1 | @@ -36,22 +37,46 @@ Feature: Object details Then the result is valid json And result has attributes hierarchy - Scenario: JSON Details with linkedplaces - When sending json details query for R123924 - | linkedplaces | - | 1 | + Scenario: JSON Details with grouped hierarchy + When sending json details query for W297699560 + | hierarchy | group_hierarchy | + | 1 | 1 | Then the result is valid json + And result has attributes hierarchy - Scenario Outline: HTML Details with keywords + Scenario Outline: JSON Details with keywords When sending json details query for | keywords | | 1 | Then the result is valid json + And result has attributes keywords Examples: | osmid | | W297699560 | | W243055645 | | W243055716 | + | W43327921 | + + # ticket #1343 + Scenario: Details of a country with keywords + When sending details query for R1155955 + | keywords | + | 1 | + Then the result is valid json + + Scenario Outline: JSON details with full geometry + When sending json details query for + | polygon_geojson | + | 1 | + Then the result is valid json + And result has attributes geometry + + Examples: + | osmid | + | W297699560 | + | W243055645 | + | W243055716 | + | W43327921 | diff --git a/test/bdd/api/details/simple.feature b/test/bdd/api/details/simple.feature index 906c4ce5..a9b6d6a7 100644 --- a/test/bdd/api/details/simple.feature +++ b/test/bdd/api/details/simple.feature @@ -2,36 +2,54 @@ Feature: Object details Check details page for correctness + Scenario: Details by place ID + When sending details query for 107077 + Then the result is valid json + And results contain + | place_id | + | 107077 | + Scenario Outline: Details via OSM id - When sending details query for - Then the result is valid + When sending details query for + Then the result is valid json + And results contain + | osm_type | osm_id | + | | | Examples: - | format | object | - | json | 107077 | - | json | N5484325405 | - | json | W43327921 | - | json | R123924 | + | type | id | + | N | 5484325405 | + | W | 43327921 | + | R | 123924 | + + Scenario: Details for interpolation way just return the dependent street + When sending details query for W1 + Then the result is valid json + And results contain + | category | + | highway | + + Scenario Outline: Details for different class types for the same OSM id + When sending details query for N300209696: + Then the result is valid json + And results contain + | osm_type | osm_id | category | + | N | 300209696 | | + + Examples: + | class | + | tourism | + | natural | + | mountain_pass | Scenario Outline: Details via unknown OSM id - When sending details query for + When sending details query for Then a HTTP 400 is returned Examples: - | format | object | - | json | 1 | - | json | R1 | + | object | + | 1 | + | R1 | + | N300209696:highway | - Scenario: Details with keywords - When sending details query for W43327921 - | keywords | - | 1 | - Then the result is valid json - - # ticket #1343 - Scenario: Details of a country with keywords - When sending details query for R1155955 - | keywords | - | 1 | - Then the result is valid json diff --git a/test/bdd/api/reverse/params.feature b/test/bdd/api/reverse/params.feature index 374272d0..d6ef3794 100644 --- a/test/bdd/api/reverse/params.feature +++ b/test/bdd/api/reverse/params.feature @@ -25,6 +25,39 @@ Feature: Parameters for Reverse API | -45.3,; | | gkjd,50 | + Scenario Outline: Zoom levels between 4 and 18 are allowed + When sending reverse coordinates 47.14122383,9.52169581334 + | zoom | + | | + Then exactly 1 result is returned + And result addresses contain + | country_code | + | li | + + Examples: + | zoom | + | 4 | + | 5 | + | 6 | + | 7 | + | 8 | + | 9 | + | 10 | + | 11 | + | 12 | + | 13 | + | 14 | + | 15 | + | 16 | + | 17 | + | 18 | + + Scenario: Non-numerical zoom levels return an error + When sending reverse coordinates 47.14122383,9.52169581334 + | zoom | + | adfe | + Then a HTTP 400 is returned + Scenario Outline: Reverse Geocoding with extratags When sending reverse coordinates 47.1395013150811,9.522098077031046 | extratags | diff --git a/test/bdd/api/reverse/simple.feature b/test/bdd/api/reverse/simple.feature index 78bd47a7..4da311e7 100644 --- a/test/bdd/api/reverse/simple.feature +++ b/test/bdd/api/reverse/simple.feature @@ -131,3 +131,7 @@ Feature: Simple Reverse Tests | 48.966 | 8.448.2 | | Nan | 8.448 | | 48.966 | Nan | + + Scenario: Reverse Debug output returns no errors + When sending debug reverse coordinates 47.11,9.57 + Then a HTTP 200 is returned diff --git a/test/bdd/api/search/queries.feature b/test/bdd/api/search/queries.feature index 3a62c581..ea353f45 100644 --- a/test/bdd/api/search/queries.feature +++ b/test/bdd/api/search/queries.feature @@ -80,6 +80,15 @@ Feature: Search queries | class | type | | amenity | restaurant | + Scenario: Search with specific amenity also work in country + When sending json search query "restaurants in liechtenstein" with address + Then result addresses contain + | country | + | Liechtenstein | + And results contain + | class | type | + | amenity | restaurant | + Scenario: Search with key-value amenity When sending json search query "[club=scout] Vaduz" Then results contain @@ -114,6 +123,19 @@ Feature: Search queries | class | type | | leisure | firepit | + Scenario Outline: Key/value search near given coordinate can be restricted to country + When sending json search query "[natural=peak] 47.06512,9.53965" with address + | countrycodes | + | | + Then result addresses contain + | country_code | + | | + + Examples: + | cc | + | li | + | ch | + Scenario: Name search near given coordinate When sending json search query "sporry" with address Then result addresses contain @@ -146,6 +168,14 @@ Feature: Search queries | li | Then exactly 0 results are returned + Scenario: Country searches only return results for the given country + When sending search query "Ans Trail" with address + | countrycodes | + | li | + Then result addresses contain + | country_code | + | li | + # https://trac.openstreetmap.org/ticket/5094 Scenario: housenumbers are ordered by complete match first When sending json search query "Austrasse 11, Vaduz" with address @@ -182,3 +212,7 @@ Feature: Search queries Then result addresses contain | ID | town | | 0 | Vaduz | + + Scenario: Search can handle complex query word sets + When sending search query "aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus" + Then a HTTP 200 is returned diff --git a/test/bdd/api/search/simple.feature b/test/bdd/api/search/simple.feature index 603627b1..b9323c5a 100644 --- a/test/bdd/api/search/simple.feature +++ b/test/bdd/api/search/simple.feature @@ -30,15 +30,10 @@ Feature: Simple Tests Examples: | parameter | value | - | addressdetails | 1 | | addressdetails | 0 | - | polygon_text | 1 | | polygon_text | 0 | - | polygon_kml | 1 | | polygon_kml | 0 | - | polygon_geojson | 1 | | polygon_geojson | 0 | - | polygon_svg | 1 | | polygon_svg | 0 | | accept-language | de,en | | countrycodes | li | @@ -48,9 +43,7 @@ Feature: Simple Tests | limit | 1000 | | dedupe | 1 | | dedupe | 0 | - | extratags | 1 | | extratags | 0 | - | namedetails | 1 | | namedetails | 0 | Scenario: Search with invalid output format @@ -180,13 +173,16 @@ Feature: Simple Tests | 234 | Then the result is valid xml - Scenario: Empty JSON search - When sending json search query "YHlERzzx" + Scenario Outline: Empty search + When sending search query "YHlERzzx" Then exactly 0 results are returned - Scenario: Empty JSONv2 search - When sending jsonv2 search query "Flubb XdfESSaZx" - Then exactly 0 results are returned + Examples: + | format | + | json | + | jsonv2 | + | geojson | + | geocodejson | Scenario: Search for non-existing coordinates When sending json search query "-21.0,-33.0" @@ -199,3 +195,16 @@ Feature: Simple Tests Then result header contains | attr | value | | more_url | .*&countrycodes=pl%2Cbo&.* | + + Scenario Outline: Search debug output does not return errors + When sending debug search query "" + Then a HTTP 200 is returned + + Examples: + | query | + | Liechtenstein | + | Triesen | + | Pfarrkirche | + | Landstr 27 Steinort, Triesenberg, 9495 | + | 9497 | + | restaurant in triesen | diff --git a/test/bdd/steps/http_responses.py b/test/bdd/steps/http_responses.py index 1e7da93d..beafcd9e 100644 --- a/test/bdd/steps/http_responses.py +++ b/test/bdd/steps/http_responses.py @@ -50,7 +50,7 @@ class GenericResponse: self.result = [] self.header = dict() - if errorcode == 200: + if errorcode == 200 and fmt != 'debug': getattr(self, '_parse_' + fmt)() def _parse_json(self): diff --git a/test/bdd/steps/steps_api_queries.py b/test/bdd/steps/steps_api_queries.py index fd263af7..ad4a8515 100644 --- a/test/bdd/steps/steps_api_queries.py +++ b/test/bdd/steps/steps_api_queries.py @@ -12,6 +12,7 @@ from urllib.parse import urlencode from utils import run_script from http_responses import GenericResponse, SearchResponse, ReverseResponse, StatusResponse from check_functions import Bbox +from table_compare import NominatimID LOG = logging.getLogger(__name__) @@ -78,7 +79,7 @@ def query_cmd(context, query, dups): context.response = SearchResponse(outp, 'json') def send_api_query(endpoint, params, fmt, context): - if fmt is not None: + if fmt is not None and fmt.strip() != 'debug': params['format'] = fmt.strip() if context.table: if context.table.headings[0] == 'param': @@ -147,6 +148,8 @@ def website_search_request(context, fmt, query, addr): params['q'] = query if addr is not None: params['addressdetails'] = '1' + if fmt and fmt.strip() == 'debug': + params['debug'] = '1' outp, status = send_api_query('search', params, fmt, context) @@ -159,6 +162,8 @@ def website_reverse_request(context, fmt, lat, lon): params['lat'] = lat if lon is not None: params['lon'] = lon + if fmt and fmt.strip() == 'debug': + params['debug'] = '1' outp, status = send_api_query('reverse', params, fmt, context) @@ -168,8 +173,11 @@ def website_reverse_request(context, fmt, lat, lon): def website_details_request(context, fmt, query): params = {} if query[0] in 'NWR': - params['osmtype'] = query[0] - params['osmid'] = query[1:] + nid = NominatimID(query) + params['osmtype'] = nid.typ + params['osmid'] = nid.oid + if nid.cls: + params['class'] = nid.cls else: params['place_id'] = query outp, status = send_api_query('details', params, fmt, context) @@ -199,7 +207,8 @@ def validate_result_number(context, operator, number): @then(u'a HTTP (?P\d+) is returned') def check_http_return_status(context, status): - assert context.response.errorcode == int(status) + assert context.response.errorcode == int(status), \ + "Return HTTP status is {}.".format(context.response.errorcode) @then(u'the page contents equals "(?P.+)"') def check_page_content_equals(context, text): @@ -232,9 +241,13 @@ def check_header_attr(context): def check_header_no_attr(context, neg, attrs): for attr in attrs.split(','): if neg: - assert attr not in context.response.header + assert attr not in context.response.header, \ + "Unexpected attribute {}. Full response:\n{}".format( + attr, json.dumps(context.response.header, sort_keys=True, indent=2)) else: - assert attr in context.response.header + assert attr in context.response.header, \ + "No attribute {}. Full response:\n{}".format( + attr, json.dumps(context.response.header, sort_keys=True, indent=2)) @then(u'results contain') def step_impl(context): @@ -255,9 +268,13 @@ def validate_attributes(context, lid, neg, attrs): for i in idx: for attr in attrs.split(','): if neg: - assert attr not in context.response.result[i] + assert attr not in context.response.result[i],\ + "Unexpected attribute {}. Full response:\n{}".format( + attr, json.dumps(context.response.result[i], sort_keys=True, indent=2)) else: - assert attr in context.response.result[i] + assert attr in context.response.result[i], \ + "No attribute {}. Full response:\n{}".format( + attr, json.dumps(context.response.result[i], sort_keys=True, indent=2)) @then(u'result addresses contain') def step_impl(context): diff --git a/test/php/Nominatim/OutputTest.php b/test/php/Nominatim/OutputTest.php deleted file mode 100644 index cbfebb7d..00000000 --- a/test/php/Nominatim/OutputTest.php +++ /dev/null @@ -1,71 +0,0 @@ - 'N', 'osm_id'=> 38274, 'class' => 'place'); - $this->assertSame( - detailsPermaLink($aFeature), - 'node 38274' - ); - } - - public function testDetailsPermaLinkWay() - { - $aFeature = array('osm_type' => 'W', 'osm_id'=> 65, 'class' => 'highway'); - $this->assertSame( - detailsPermaLink($aFeature), - 'way 65' - ); - } - - public function testDetailsPermaLinkRelation() - { - $aFeature = array('osm_type' => 'R', 'osm_id'=> 9908, 'class' => 'waterway'); - $this->assertSame( - detailsPermaLink($aFeature), - 'relation 9908' - ); - } - - public function testDetailsPermaLinkTiger() - { - $aFeature = array('osm_type' => 'T', 'osm_id'=> 2, 'place_id' => 334); - $this->assertSame( - detailsPermaLink($aFeature, 'foo'), - 'foo' - ); - } - - public function testDetailsPermaLinkInterpolation() - { - $aFeature = array('osm_type' => 'I', 'osm_id'=> 400, 'place_id' => 3); - $this->assertSame( - detailsPermaLink($aFeature, 'foo'), - 'foo' - ); - } - - public function testDetailsPermaLinkWithExtraPropertiesNode() - { - $aFeature = array('osm_type' => 'N', 'osm_id'=> 2, 'class' => 'amenity'); - $this->assertSame( - detailsPermaLink($aFeature, 'something', 'class="xtype"'), - 'something' - ); - } - - public function testDetailsPermaLinkWithExtraPropertiesTiger() - { - $aFeature = array('osm_type' => 'T', 'osm_id'=> 5, 'place_id' => 46); - $this->assertSame( - detailsPermaLink($aFeature, 'something', 'class="xtype"'), - 'something' - ); - } -}