Skip to content

Commit 57a6de2

Browse files
committed
test(geocoding): add hard regression tests for enable_address_descriptor (#540)
1 parent 9ec69cb commit 57a6de2

1 file changed

Lines changed: 309 additions & 0 deletions

File tree

tests/test_geocoding_advanced.py

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
#
2+
# Copyright 2024 Google Inc. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
# use this file except in compliance with the License. You may obtain a copy of
6+
# the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
11+
"""Advanced / regression tests for the geocoding module.
12+
13+
These tests are specifically aimed at issue #540 ("unexpected keyword argument
14+
``enable_address_descriptor``"). They exhaustively pin down the public
15+
signature of :func:`googlemaps.Client.reverse_geocode` and the on-the-wire
16+
behaviour of the ``enable_address_descriptor`` flag so the regression cannot
17+
silently come back in a future release.
18+
"""
19+
20+
import inspect
21+
from urllib.parse import parse_qs, urlparse
22+
23+
import responses
24+
25+
import googlemaps
26+
from googlemaps import geocoding
27+
28+
from . import TestCase
29+
30+
31+
_GEOCODE_URL = "https://maps.googleapis.com/maps/api/geocode/json"
32+
33+
_DESCRIPTOR_BODY = (
34+
'{"status":"OK","results":[],'
35+
'"address_descriptor":{'
36+
'"landmarks":[{"placeId":"id","display_name":{"text":"Opera House"}}],'
37+
'"areas":[{"placeId":"area1","display_name":{"text":"Sydney"}}]'
38+
'}}'
39+
)
40+
_PLAIN_BODY = '{"status":"OK","results":[]}'
41+
42+
43+
def _query(call):
44+
"""Return the parsed query string for a captured ``responses`` call."""
45+
return parse_qs(urlparse(call.request.url).query)
46+
47+
48+
class ReverseGeocodeSignatureTest(TestCase):
49+
"""Pin the public signature so issue #540 cannot regress."""
50+
51+
def test_module_function_accepts_enable_address_descriptor(self):
52+
sig = inspect.signature(geocoding.reverse_geocode)
53+
self.assertIn("enable_address_descriptor", sig.parameters)
54+
self.assertFalse(
55+
sig.parameters["enable_address_descriptor"].default,
56+
"enable_address_descriptor must default to a falsy value",
57+
)
58+
59+
def test_client_method_accepts_enable_address_descriptor(self):
60+
client = googlemaps.Client(key="AIzaasdf")
61+
sig = inspect.signature(client.reverse_geocode)
62+
self.assertIn(
63+
"enable_address_descriptor",
64+
sig.parameters,
65+
"Client.reverse_geocode must expose enable_address_descriptor "
66+
"(regression for issue #540).",
67+
)
68+
69+
def test_client_method_call_does_not_raise_typeerror(self):
70+
"""The exact failure mode from issue #540 must no longer occur."""
71+
client = googlemaps.Client(key="AIzaasdf")
72+
with responses.RequestsMock() as rsps:
73+
rsps.add(
74+
responses.GET,
75+
_GEOCODE_URL,
76+
body=_DESCRIPTOR_BODY,
77+
status=200,
78+
content_type="application/json",
79+
)
80+
try:
81+
client.reverse_geocode(
82+
(-33.8674869, 151.2069902),
83+
enable_address_descriptor=True,
84+
)
85+
except TypeError as exc: # pragma: no cover - regression guard
86+
self.fail(
87+
"reverse_geocode raised TypeError for "
88+
"enable_address_descriptor (issue #540): %s" % exc
89+
)
90+
91+
92+
class ReverseGeocodeAddressDescriptorTest(TestCase):
93+
def setUp(self):
94+
self.key = "AIzaasdf"
95+
self.client = googlemaps.Client(self.key)
96+
97+
@responses.activate
98+
def test_flag_true_sends_lowercase_true(self):
99+
responses.add(
100+
responses.GET, _GEOCODE_URL,
101+
body=_DESCRIPTOR_BODY, status=200,
102+
content_type="application/json",
103+
)
104+
self.client.reverse_geocode(
105+
(-33.8674869, 151.2069902), enable_address_descriptor=True,
106+
)
107+
q = _query(responses.calls[0])
108+
self.assertEqual(q["enable_address_descriptor"], ["true"])
109+
self.assertEqual(q["latlng"], ["-33.8674869,151.2069902"])
110+
111+
@responses.activate
112+
def test_flag_default_omits_parameter(self):
113+
responses.add(
114+
responses.GET, _GEOCODE_URL,
115+
body=_PLAIN_BODY, status=200,
116+
content_type="application/json",
117+
)
118+
self.client.reverse_geocode((-33.8674869, 151.2069902))
119+
q = _query(responses.calls[0])
120+
self.assertNotIn("enable_address_descriptor", q)
121+
122+
@responses.activate
123+
def test_flag_false_omits_parameter(self):
124+
responses.add(
125+
responses.GET, _GEOCODE_URL,
126+
body=_PLAIN_BODY, status=200,
127+
content_type="application/json",
128+
)
129+
self.client.reverse_geocode(
130+
(-33.8674869, 151.2069902), enable_address_descriptor=False,
131+
)
132+
q = _query(responses.calls[0])
133+
self.assertNotIn("enable_address_descriptor", q)
134+
135+
@responses.activate
136+
def test_falsy_values_omit_parameter(self):
137+
for falsy in (None, 0, "", [], {}):
138+
with self.subTest(value=falsy):
139+
with responses.RequestsMock() as rsps:
140+
rsps.add(
141+
responses.GET, _GEOCODE_URL,
142+
body=_PLAIN_BODY, status=200,
143+
content_type="application/json",
144+
)
145+
self.client.reverse_geocode(
146+
(40.0, -73.0), enable_address_descriptor=falsy,
147+
)
148+
q = _query(rsps.calls[0])
149+
self.assertNotIn("enable_address_descriptor", q)
150+
151+
@responses.activate
152+
def test_truthy_non_bool_values_send_true(self):
153+
for truthy in (1, "yes", ["x"], {"a": 1}):
154+
with self.subTest(value=truthy):
155+
with responses.RequestsMock() as rsps:
156+
rsps.add(
157+
responses.GET, _GEOCODE_URL,
158+
body=_DESCRIPTOR_BODY, status=200,
159+
content_type="application/json",
160+
)
161+
self.client.reverse_geocode(
162+
(40.0, -73.0), enable_address_descriptor=truthy,
163+
)
164+
q = _query(rsps.calls[0])
165+
self.assertEqual(q["enable_address_descriptor"], ["true"])
166+
167+
@responses.activate
168+
def test_combined_with_result_and_location_type(self):
169+
responses.add(
170+
responses.GET, _GEOCODE_URL,
171+
body=_DESCRIPTOR_BODY, status=200,
172+
content_type="application/json",
173+
)
174+
self.client.reverse_geocode(
175+
(40.714224, -73.961452),
176+
result_type=["street_address", "route"],
177+
location_type="ROOFTOP",
178+
language="en",
179+
enable_address_descriptor=True,
180+
)
181+
q = _query(responses.calls[0])
182+
self.assertEqual(q["enable_address_descriptor"], ["true"])
183+
self.assertEqual(q["result_type"], ["street_address|route"])
184+
self.assertEqual(q["location_type"], ["ROOFTOP"])
185+
self.assertEqual(q["language"], ["en"])
186+
self.assertEqual(q["latlng"], ["40.714224,-73.961452"])
187+
188+
@responses.activate
189+
def test_works_with_place_id_string(self):
190+
place_id = "ChIJN1t_tDeuEmsRUsoyG83frY4"
191+
responses.add(
192+
responses.GET, _GEOCODE_URL,
193+
body=_DESCRIPTOR_BODY, status=200,
194+
content_type="application/json",
195+
)
196+
self.client.reverse_geocode(place_id, enable_address_descriptor=True)
197+
q = _query(responses.calls[0])
198+
self.assertEqual(q["place_id"], [place_id])
199+
self.assertNotIn("latlng", q)
200+
self.assertEqual(q["enable_address_descriptor"], ["true"])
201+
202+
@responses.activate
203+
def test_works_with_dict_latlng(self):
204+
responses.add(
205+
responses.GET, _GEOCODE_URL,
206+
body=_DESCRIPTOR_BODY, status=200,
207+
content_type="application/json",
208+
)
209+
self.client.reverse_geocode(
210+
{"lat": -33.8674869, "lng": 151.2069902},
211+
enable_address_descriptor=True,
212+
)
213+
q = _query(responses.calls[0])
214+
self.assertEqual(q["latlng"], ["-33.8674869,151.2069902"])
215+
self.assertEqual(q["enable_address_descriptor"], ["true"])
216+
217+
@responses.activate
218+
def test_works_with_list_latlng(self):
219+
responses.add(
220+
responses.GET, _GEOCODE_URL,
221+
body=_DESCRIPTOR_BODY, status=200,
222+
content_type="application/json",
223+
)
224+
self.client.reverse_geocode(
225+
[-33.8674869, 151.2069902], enable_address_descriptor=True,
226+
)
227+
q = _query(responses.calls[0])
228+
self.assertEqual(q["latlng"], ["-33.8674869,151.2069902"])
229+
230+
@responses.activate
231+
def test_works_with_string_latlng(self):
232+
responses.add(
233+
responses.GET, _GEOCODE_URL,
234+
body=_DESCRIPTOR_BODY, status=200,
235+
content_type="application/json",
236+
)
237+
self.client.reverse_geocode(
238+
"-33.8674869,151.2069902", enable_address_descriptor=True,
239+
)
240+
q = _query(responses.calls[0])
241+
self.assertEqual(q["latlng"], ["-33.8674869,151.2069902"])
242+
self.assertEqual(q["enable_address_descriptor"], ["true"])
243+
244+
@responses.activate
245+
def test_response_exposes_address_descriptor(self):
246+
responses.add(
247+
responses.GET, _GEOCODE_URL,
248+
body=_DESCRIPTOR_BODY, status=200,
249+
content_type="application/json",
250+
)
251+
response = self.client.reverse_geocode(
252+
(-33.8674869, 151.2069902), enable_address_descriptor=True,
253+
)
254+
descriptor = response.get("address_descriptor")
255+
self.assertIsNotNone(descriptor)
256+
self.assertEqual(len(descriptor["landmarks"]), 1)
257+
self.assertEqual(descriptor["landmarks"][0]["placeId"], "id")
258+
self.assertEqual(len(descriptor["areas"]), 1)
259+
260+
@responses.activate
261+
def test_repeated_calls_are_independent(self):
262+
"""A previous call with the flag must not leak into the next."""
263+
responses.add(
264+
responses.GET, _GEOCODE_URL,
265+
body=_DESCRIPTOR_BODY, status=200,
266+
content_type="application/json",
267+
)
268+
responses.add(
269+
responses.GET, _GEOCODE_URL,
270+
body=_PLAIN_BODY, status=200,
271+
content_type="application/json",
272+
)
273+
274+
self.client.reverse_geocode(
275+
(40.0, -73.0), enable_address_descriptor=True,
276+
)
277+
self.client.reverse_geocode((40.0, -73.0))
278+
279+
self.assertEqual(
280+
_query(responses.calls[0])["enable_address_descriptor"], ["true"],
281+
)
282+
self.assertNotIn(
283+
"enable_address_descriptor", _query(responses.calls[1]),
284+
)
285+
286+
@responses.activate
287+
def test_keyword_only_call_via_module_function(self):
288+
"""Calling the underlying module function directly must also work."""
289+
responses.add(
290+
responses.GET, _GEOCODE_URL,
291+
body=_DESCRIPTOR_BODY, status=200,
292+
content_type="application/json",
293+
)
294+
geocoding.reverse_geocode(
295+
self.client,
296+
(-33.8674869, 151.2069902),
297+
enable_address_descriptor=True,
298+
)
299+
q = _query(responses.calls[0])
300+
self.assertEqual(q["enable_address_descriptor"], ["true"])
301+
302+
def test_unknown_kwarg_still_raises_typeerror(self):
303+
"""Sanity check: only the documented kwarg is accepted."""
304+
client = googlemaps.Client(self.key)
305+
with self.assertRaises(TypeError):
306+
client.reverse_geocode(
307+
(-33.8674869, 151.2069902),
308+
enable_address_descriptors=True, # note the typo / plural
309+
)

0 commit comments

Comments
 (0)