diff --git a/backend/services/geocode.py b/backend/services/geocode.py index 204fdb0..234db64 100644 --- a/backend/services/geocode.py +++ b/backend/services/geocode.py @@ -171,7 +171,7 @@ def search_geocode(query: str, limit: int = 5, local_only: bool = False) -> List if not q: return [] limit = max(1, min(int(limit or 5), 10)) - key = f"search:{q.lower()}:{limit}:{int(local_only)}" + key = f"search2:{q.lower()}:{limit}:{int(local_only)}" cached = _get_cache(key) if cached is not None: return cached @@ -180,7 +180,7 @@ def search_geocode(query: str, limit: int = 5, local_only: bool = False) -> List _set_cache(key, results) return results - params = urlencode({"q": q, "format": "json", "limit": str(limit)}) + params = urlencode({"q": q, "format": "jsonv2", "limit": str(limit)}) url = f"https://nominatim.openstreetmap.org/search?{params}" try: res = fetch_with_curl( @@ -207,15 +207,23 @@ def search_geocode(query: str, limit: int = 5, local_only: bool = False) -> List data = res.json() or [] for item in data: try: - results.append( - { - "label": item.get("display_name"), - "lat": float(item.get("lat")), - "lng": float(item.get("lon")), - } - ) + entry = { + "label": item.get("display_name"), + "lat": float(item.get("lat")), + "lng": float(item.get("lon")), + } except (TypeError, ValueError): continue + # Extent metadata the frontend uses to size the camera. Absent + # for local_only results, so every field stays optional. + bbox = item.get("boundingbox") + if isinstance(bbox, list) and len(bbox) == 4: + entry["bbox"] = [str(value) for value in bbox] + if isinstance(item.get("place_rank"), int): + entry["place_rank"] = item["place_rank"] + if item.get("addresstype"): + entry["addresstype"] = str(item["addresstype"]) + results.append(entry) except Exception: results = [] diff --git a/backend/tests/test_geocode_api.py b/backend/tests/test_geocode_api.py index 7781f13..0461a04 100644 --- a/backend/tests/test_geocode_api.py +++ b/backend/tests/test_geocode_api.py @@ -2,7 +2,7 @@ from unittest.mock import patch def test_geocode_search_proxy(client): - with patch("main.search_geocode") as mock_search: + with patch("services.geocode.search_geocode") as mock_search: mock_search.return_value = [{"label": "Denver, CO, USA", "lat": 39.7392, "lng": -104.9903}] r = client.get("/api/geocode/search?q=denver&limit=1") assert r.status_code == 200 @@ -12,9 +12,42 @@ def test_geocode_search_proxy(client): def test_geocode_reverse_proxy(client): - with patch("main.reverse_geocode") as mock_reverse: + with patch("services.geocode.reverse_geocode") as mock_reverse: mock_reverse.return_value = {"label": "Boulder, CO, USA"} r = client.get("/api/geocode/reverse?lat=40.01499&lng=-105.27055") assert r.status_code == 200 data = r.json() assert data["label"] == "Boulder, CO, USA" + + +def test_geocode_search_passes_through_extent_fields(client): + with patch("services.geocode.search_geocode") as mock_search: + mock_search.return_value = [ + { + "label": "Monaco", + "lat": 43.7311, + "lng": 7.4197, + "bbox": ["43.5165358", "43.7519173", "7.4090279", "7.5329917"], + "place_rank": 4, + "addresstype": "country", + } + ] + r = client.get("/api/geocode/search?q=monaco&limit=1") + assert r.status_code == 200 + result = r.json()["results"][0] + assert result["bbox"] == [ + "43.5165358", + "43.7519173", + "7.4090279", + "7.5329917", + ] + assert result["place_rank"] == 4 + + +def test_geocode_search_tolerates_missing_extent_fields(client): + """local_only results and older cache entries carry no extent data.""" + with patch("services.geocode.search_geocode") as mock_search: + mock_search.return_value = [{"label": "Denver", "lat": 39.7392, "lng": -104.9903}] + r = client.get("/api/geocode/search?q=denver&limit=1") + assert r.status_code == 200 + assert "bbox" not in r.json()["results"][0]