Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/geocodio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ def _parse_geocoding_response(self, response_json: dict) -> GeocodingResponse:
source=top.get("source", ""),
query=query,
fields=self._parse_fields(top.get("fields")),
stable_address_key=top.get("stable_address_key"),
)
)
return GeocodingResponse(results=results)
Expand All @@ -430,6 +431,7 @@ def _parse_geocoding_response(self, response_json: dict) -> GeocodingResponse:
accuracy_type=res.get("accuracy_type", ""),
source=res.get("source", ""),
fields=self._parse_fields(res.get("fields")),
stable_address_key=res.get("stable_address_key"),
)
for res in response_json.get("results", [])
]
Expand Down
1 change: 1 addition & 0 deletions src/geocodio/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,7 @@ class GeocodingResult:
source: str
fields: Optional[GeocodioFields] = None
query: str = ""
stable_address_key: Optional[str] = None

@property
def matched(self) -> bool:
Expand Down
26 changes: 26 additions & 0 deletions tests/e2e/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,32 @@ def test_integration_geocode(client):
assert components.postal_code is not None


def test_integration_stable_address_key(client):
"""Verify stable_address_key is exposed on real single, reverse, and batch calls."""
# Single forward geocode
single = client.geocode("1109 N Highland St, Arlington, VA").results[0]
assert single.stable_address_key is not None
assert isinstance(single.stable_address_key, str)
assert single.stable_address_key != ""

# Reverse geocode (White House)
reverse = client.reverse((38.897699, -77.036547)).results[0]
assert reverse.stable_address_key is not None
assert isinstance(reverse.stable_address_key, str)

# Batch geocode (nested response format)
batch = client.geocode(
["3730 N Clark St, Chicago, IL", "638 E 13th Ave, Denver, CO"]
)
for result in batch.results:
assert result.stable_address_key is not None
assert isinstance(result.stable_address_key, str)

# The key should be stable: the same address returns the same key.
again = client.geocode("1109 N Highland St, Arlington, VA").results[0]
assert again.stable_address_key == single.stable_address_key


def test_integration_reverse(client):
"""Test real reverse geocoding API call."""
# Test coordinates (White House)
Expand Down
92 changes: 92 additions & 0 deletions tests/unit/test_geocode.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def sample_payload() -> dict:
"observes_dst": True,
}
},
"stable_address_key": "abc123stablekey",
}
],
}
Expand Down Expand Up @@ -67,6 +68,8 @@ def response_callback(request):
tz = resp.results[0].fields.timezone
assert tz.name == "America/New_York"
assert tz.observes_dst is True
# stable address key (v2 returns this on every result)
assert resp.results[0].stable_address_key == "abc123stablekey"


def test_geocode_batch(client, httpx_mock):
Expand Down Expand Up @@ -820,3 +823,92 @@ def batch_response_callback(request):
assert unmatched.location is None
assert unmatched.query == "qwertyuiop asdfghjkl zxcvbnm"
assert unmatched.formatted_address == ""

def test_geocode_batch_exposes_stable_address_key(client, httpx_mock):
"""stable_address_key is parsed from the nested batch response format."""
addresses = ["1109 N Highland St, Arlington, VA"]

def batch_response_callback(request):
return httpx.Response(
200,
json={
"results": [
{
"query": "1109 N Highland St, Arlington, VA",
"response": {
"results": [
{
"address_components": {
"number": "1109",
"street": "Highland",
"suffix": "St",
"city": "Arlington",
"state_province": "VA",
"postal_code": "22201",
"country": "US",
},
"formatted_address": "1109 N Highland St, Arlington, VA 22201",
"location": {"lat": 38.886672, "lng": -77.094735},
"accuracy": 1,
"accuracy_type": "rooftop",
"source": "Arlington",
"stable_address_key": "arlington-stable-key",
}
]
},
},
]
},
)

httpx_mock.add_callback(
callback=batch_response_callback,
url=httpx.URL("https://api.test/v2/geocode"),
match_headers={"Authorization": "Bearer TEST_KEY"},
)

resp = client.geocode(addresses)

assert resp.results[0].stable_address_key == "arlington-stable-key"


def test_geocode_stable_address_key_absent_is_none(client, httpx_mock):
"""stable_address_key defaults to None when the API omits it."""

def response_callback(request):
return httpx.Response(
200,
json={
"results": [
{
"address_components": {
"number": "1109",
"street": "Highland",
"suffix": "St",
"city": "Arlington",
"state_province": "VA",
"postal_code": "22201",
"country": "US",
},
"formatted_address": "1109 N Highland St, Arlington, VA 22201",
"location": {"lat": 38.886672, "lng": -77.094735},
"accuracy": 1,
"accuracy_type": "rooftop",
"source": "Arlington",
}
],
},
)

httpx_mock.add_callback(
callback=response_callback,
url=httpx.URL(
"https://api.test/v2/geocode",
params={"q": "1109 N Highland St, Arlington, VA"},
),
match_headers={"Authorization": "Bearer TEST_KEY"},
)

resp = client.geocode("1109 N Highland St, Arlington, VA")

assert resp.results[0].stable_address_key is None
Loading