44import shutil
55import sys
66import tempfile
7+ import json
8+ from urllib .parse import parse_qs
9+ from urllib .parse import urlparse
710import pytest
811
912# Add the parent directories to sys.path so we can import elodie modules and test helpers
1518from elodie .external .pyexiftool import ExifTool
1619from elodie .dependencies import get_exiftool
1720from elodie import constants
21+ from elodie import geolocation
1822
1923
2024@pytest .fixture (scope = "session" , autouse = True )
@@ -80,4 +84,120 @@ def setup_test_environment():
8084 try :
8185 shutil .rmtree (temporary_application_directory )
8286 except OSError :
83- pass # Directory might already be cleaned up
87+ pass # Directory might already be cleaned up
88+
89+
90+ @pytest .fixture (scope = "function" , autouse = True )
91+ def offline_mapquest (request , monkeypatch ):
92+ """Provide deterministic geocoding for suites that historically depended on live MapQuest."""
93+ if request .module .__name__ not in (
94+ 'elodie.tests.elodie_test' ,
95+ 'elodie.tests.filesystem_test' ,
96+ ):
97+ yield
98+ return
99+
100+ address_lookup = {
101+ 'new york, ny' : {'lat' : 40.7128 , 'lng' : - 74.0060 , 'city' : 'New York' , 'state' : 'NY' , 'country' : 'US' },
102+ 'san francisco, ca' : {'lat' : 37.7749 , 'lng' : - 122.4194 , 'city' : 'San Francisco' , 'state' : 'CA' , 'country' : 'US' },
103+ 'sunnyvale, ca' : {'lat' : 37.37188 , 'lng' : - 122.03751 , 'city' : 'Sunnyvale' , 'state' : 'CA' , 'country' : 'US' },
104+ 'sunnyvale, california' : {'lat' : 37.37188 , 'lng' : - 122.03751 , 'city' : 'Sunnyvale' , 'state' : 'CA' , 'country' : 'US' },
105+ }
106+ reverse_lookup = {
107+ '51.521435,0.162714' : {'city' : 'Rainham' , 'state' : 'England' , 'country' : 'GB' },
108+ '29.758938,-95.3677' : {'city' : 'Houston' , 'state' : 'TX' , 'country' : 'US' },
109+ '38.1893,-119.9558' : {'city' : 'Pinecrest' , 'state' : 'CA' , 'country' : 'US' },
110+ '37.366703,-122.033384' : {'city' : 'Sunnyvale' , 'state' : 'CA' , 'country' : 'US' },
111+ '37.37188,-122.03751' : {'city' : 'Sunnyvale' , 'state' : 'CA' , 'country' : 'US' },
112+ '40.7128,-74.006' : {'city' : 'New York' , 'state' : 'NY' , 'country' : 'US' },
113+ '37.7749,-122.4194' : {'city' : 'San Francisco' , 'state' : 'CA' , 'country' : 'US' },
114+ }
115+
116+ def canonical_key (lat , lon ):
117+ return '{},{}' .format (round (float (lat ), 6 ), round (float (lon ), 6 ))
118+
119+ def make_response (payload ):
120+ class FakeResponse (object ):
121+ def __init__ (self , body ):
122+ self ._body = body
123+ self .text = json .dumps (body )
124+
125+ def json (self ):
126+ return self ._body
127+
128+ return FakeResponse (payload )
129+
130+ def make_address_payload (location ):
131+ match = address_lookup .get (location .lower ())
132+ if match is None :
133+ return {
134+ 'info' : {'statuscode' : 0 },
135+ 'results' : [{
136+ 'providedLocation' : {'location' : location },
137+ 'locations' : [{
138+ 'source' : 'FALLBACK' ,
139+ 'latLng' : {'lat' : 0 , 'lng' : 0 },
140+ }]
141+ }]
142+ }
143+
144+ return {
145+ 'info' : {'statuscode' : 0 },
146+ 'results' : [{
147+ 'providedLocation' : {'location' : location },
148+ 'locations' : [{
149+ 'adminArea5' : match ['city' ],
150+ 'adminArea5Type' : 'City' ,
151+ 'adminArea3' : match ['state' ],
152+ 'adminArea3Type' : 'State' ,
153+ 'adminArea1' : match ['country' ],
154+ 'adminArea1Type' : 'Country' ,
155+ 'geocodeQuality' : 'CITY' ,
156+ 'latLng' : {'lat' : match ['lat' ], 'lng' : match ['lng' ]},
157+ }]
158+ }]
159+ }
160+
161+ def make_reverse_payload (lat , lon ):
162+ match = reverse_lookup .get (canonical_key (lat , lon ))
163+ if match is None :
164+ return {'info' : {'statuscode' : 400 }, 'results' : []}
165+
166+ return {
167+ 'info' : {'statuscode' : 0 },
168+ 'results' : [{
169+ 'providedLocation' : {'latLng' : {'lat' : float (lat ), 'lng' : float (lon )}},
170+ 'locations' : [{
171+ 'adminArea5' : match ['city' ],
172+ 'adminArea5Type' : 'City' ,
173+ 'adminArea3' : match ['state' ],
174+ 'adminArea3Type' : 'State' ,
175+ 'adminArea1' : match ['country' ],
176+ 'adminArea1Type' : 'Country' ,
177+ 'latLng' : {'lat' : float (lat ), 'lng' : float (lon )},
178+ }]
179+ }]
180+ }
181+
182+ def fake_get (url , headers = None ):
183+ parsed = urlparse (url )
184+ params = parse_qs (parsed .query )
185+ path = parsed .path
186+ if path .endswith ('/address' ):
187+ location = params .get ('location' , ['' ])[0 ]
188+ return make_response (make_address_payload (location ))
189+
190+ if path .endswith ('/reverse' ):
191+ lat = params .get ('lat' , [None ])[0 ]
192+ lon = params .get ('lon' , [None ])[0 ]
193+ if lat is None or lon is None :
194+ location = params .get ('location' , ['' ])[0 ]
195+ if ',' in location :
196+ lat , lon = location .split (',' , 1 )
197+ return make_response (make_reverse_payload (lat , lon ))
198+
199+ return make_response ({'info' : {'statuscode' : 400 }, 'results' : []})
200+
201+ monkeypatch .setattr (geolocation .requests , 'get' , fake_get )
202+
203+ yield
0 commit comments