1010
1111namespace Geocoder \IntegrationTest ;
1212
13+ use Geocoder \Collection ;
14+ use Geocoder \Location ;
15+ use Geocoder \Model \AdminLevelCollection ;
16+ use Geocoder \Model \Bounds ;
17+ use Geocoder \Model \Coordinates ;
18+ use Geocoder \Model \Country ;
1319use Geocoder \Model \Query \GeocodeQuery ;
1420use Geocoder \Model \Query \ReverseQuery ;
1521use Geocoder \Provider \Provider ;
1622use GuzzleHttp \Psr7 \Response ;
1723use Http \Client \HttpClient ;
24+ use Http \Discovery \HttpClientDiscovery ;
1825use PHPUnit \Framework \TestCase ;
1926use Psr \Http \Message \ResponseInterface ;
2027
@@ -29,10 +36,20 @@ abstract class ProviderIntegrationTest extends TestCase
2936 protected $ skippedTests = [];
3037
3138 /**
32- * @return Provider that is used in the tests
39+ * @return Provider that is used in the tests.
3340 */
3441 abstract protected function createProvider (HttpClient $ httpClient );
3542
43+ /**
44+ * @return string the directory where cached responses are stored
45+ */
46+ abstract protected function getCacheDir ();
47+
48+ /**
49+ * @return string the API key or substring to be removed from cache.
50+ */
51+ abstract protected function getApiKey ();
52+
3653 /**
3754 * @param ResponseInterface $response
3855 *
@@ -50,6 +67,88 @@ private function getHttpClient(ResponseInterface $response)
5067 return $ client ;
5168 }
5269
70+ /**
71+ * This client will make real request if cache was not found.
72+ *
73+ * @return CachedResponseClient
74+ */
75+ private function getCachedHttpClient ()
76+ {
77+ $ client = HttpClientDiscovery::find ();
78+
79+ return new CachedResponseClient ($ client , $ this ->getCacheDir (), $ this ->getApiKey ());
80+ }
81+
82+ public function testGeocodeQuery ()
83+ {
84+ if (isset ($ this ->skippedTests [__FUNCTION__ ])) {
85+ $ this ->markTestSkipped ($ this ->skippedTests [__FUNCTION__ ]);
86+ }
87+
88+ $ provider = $ this ->createProvider ($ this ->getCachedHttpClient ());
89+ $ query = GeocodeQuery::create ('10 Downing St, London, UK ' )->withLocale ('en ' );
90+ $ result = $ provider ->geocodeQuery ($ query );
91+ $ this ->assertWellFormattedResult ($ result );
92+
93+ // Check Downing Street
94+ $ location = $ result ->first ();
95+ $ this ->assertEquals (51.5033 , $ location ->getCoordinates ()->getLatitude (), 'Latitude should be in London ' , 0.1 );
96+ $ this ->assertEquals (-0.1276 , $ location ->getCoordinates ()->getLongitude (), 'Longitude should be in London ' , 0.1 );
97+ $ this ->assertContains ('Downing ' , $ location ->getStreetName (), 'Street name should contain "Downing St" ' );
98+ $ this ->assertContains ('10 ' , $ location ->getStreetNumber (), 'Street number should contain "10" ' );
99+ }
100+
101+ public function testReverseQuery ()
102+ {
103+ if (isset ($ this ->skippedTests [__FUNCTION__ ])) {
104+ $ this ->markTestSkipped ($ this ->skippedTests [__FUNCTION__ ]);
105+ }
106+
107+ $ provider = $ this ->createProvider ($ this ->getCachedHttpClient ());
108+
109+ // Close to the white house
110+ $ result = $ provider ->reverseQuery (ReverseQuery::fromCoordinates (38.900206 , -77.036991 )->withLocale ('en ' ));
111+ $ this ->assertWellFormattedResult ($ result );
112+ }
113+
114+ /**
115+ * @expectedException \Geocoder\Exception\InvalidArgument
116+ */
117+ public function testEmptyQuery ()
118+ {
119+ if (isset ($ this ->skippedTests [__FUNCTION__ ])) {
120+ $ this ->markTestSkipped ($ this ->skippedTests [__FUNCTION__ ]);
121+ }
122+
123+ $ provider = $ this ->createProvider ($ this ->getCachedHttpClient ());
124+ $ provider ->geocodeQuery (GeocodeQuery::create ('' ));
125+ }
126+
127+ /**
128+ * @expectedException \Geocoder\Exception\InvalidArgument
129+ */
130+ public function testNullQuery ()
131+ {
132+ if (isset ($ this ->skippedTests [__FUNCTION__ ])) {
133+ $ this ->markTestSkipped ($ this ->skippedTests [__FUNCTION__ ]);
134+ }
135+
136+ $ provider = $ this ->createProvider ($ this ->getCachedHttpClient ());
137+ $ provider ->geocodeQuery (GeocodeQuery::create (null ));
138+ }
139+
140+ public function testEmptyReverseQuery ()
141+ {
142+ if (isset ($ this ->skippedTests [__FUNCTION__ ])) {
143+ $ this ->markTestSkipped ($ this ->skippedTests [__FUNCTION__ ]);
144+ }
145+
146+ $ provider = $ this ->createProvider ($ this ->getCachedHttpClient ());
147+
148+ $ result = $ provider ->reverseQuery (ReverseQuery::fromCoordinates (0 , 0 ));
149+ $ this ->assertEquals (0 , $ result ->count ());
150+ }
151+
53152 /**
54153 * @expectedException \Geocoder\Exception\InvalidServerResponse
55154 */
@@ -179,4 +278,91 @@ public function testQuotaExceededResponseReverse()
179278 $ provider = $ this ->createProvider ($ this ->getHttpClient (new Response (429 )));
180279 $ provider ->reverseQuery (ReverseQuery::fromCoordinates (0 , 0 ));
181280 }
281+
282+ /**
283+ * Make sure that a result for a Geocoder is well formatted. Be aware that even
284+ * a Location with no data may be well formatted.
285+ *
286+ * @param $result
287+ */
288+ private function assertWellFormattedResult (Collection $ result )
289+ {
290+ $ this ->assertInstanceOf (
291+ Collection::class,
292+ $ result ,
293+ 'The result must be an instance of a Geocoder\Collection '
294+ );
295+
296+ $ this ->assertNotEmpty ($ result , 'Geocoder\Exception should never be empty. A NoResult exception should be thrown. ' );
297+
298+ /** @var Location $location */
299+ foreach ($ result as $ location ) {
300+ $ this ->assertInstanceOf (
301+ Location::class,
302+ $ location ,
303+ 'All items in Geocoder\Collection must implement Geocoder\Location '
304+ );
305+
306+ $ this ->assertInstanceOf (
307+ AdminLevelCollection::class,
308+ $ location ->getAdminLevels (),
309+ 'Location::getAdminLevels MUST always return a AdminLevelCollection '
310+ );
311+ $ arrayData = $ location ->toArray ();
312+ $ this ->assertTrue (is_array ($ arrayData ), 'Location::toArray MUST return an array. ' );
313+ $ this ->assertNotEmpty ($ arrayData , 'Location::toArray cannot be empty. ' );
314+
315+ // Verify coordinates
316+ if (null !== $ coords = $ location ->getCoordinates ()) {
317+ $ this ->assertInstanceOf (
318+ Coordinates::class,
319+ $ coords ,
320+ 'Location::getCoordinates MUST always return a Coordinates or null '
321+ );
322+
323+ // Using "assertNotEmpty" means that we can not have test code where coordinates is on equator or long = 0
324+ $ this ->assertNotEmpty ($ coords ->getLatitude (), 'If coordinate object exists it cannot have an empty latitude. ' );
325+ $ this ->assertNotEmpty ($ coords ->getLongitude (), 'If coordinate object exists it cannot have an empty longitude. ' );
326+ }
327+
328+ // Verify bounds
329+ if (null !== $ bounds = $ location ->getBounds ()) {
330+ $ this ->assertInstanceOf (
331+ Bounds::class,
332+ $ bounds ,
333+ 'Location::getBounds MUST always return a Bounds or null '
334+ );
335+
336+ // Using "assertNotEmpty" means that we can not have test code where coordinates is on equator or long = 0
337+ $ this ->assertNotEmpty ($ bounds ->getSouth (), 'If bounds object exists it cannot have an empty values. ' );
338+ $ this ->assertNotEmpty ($ bounds ->getWest (), 'If bounds object exists it cannot have an empty values. ' );
339+ $ this ->assertNotEmpty ($ bounds ->getNorth (), 'If bounds object exists it cannot have an empty values. ' );
340+ $ this ->assertNotEmpty ($ bounds ->getEast (), 'If bounds object exists it cannot have an empty values. ' );
341+ }
342+
343+ // Check country
344+ if (null !== $ country = $ location ->getCountry ()) {
345+ $ this ->assertInstanceOf (
346+ Country::class,
347+ $ country ,
348+ 'Location::getCountry MUST always return a Country or null '
349+ );
350+ $ this ->assertFalse (null === $ country ->getCode () && null === $ country ->getName (), 'Both code and name cannot be empty ' );
351+
352+ if (null !== $ country ->getCode ()) {
353+ $ this ->assertNotEmpty (
354+ $ location ->getCountry ()->getCode (),
355+ 'The Country should not have an empty code. '
356+ );
357+ }
358+
359+ if (null !== $ country ->getName ()) {
360+ $ this ->assertNotEmpty (
361+ $ location ->getCountry ()->getName (),
362+ 'The Country should not have an empty name. '
363+ );
364+ }
365+ }
366+ }
367+ }
182368}
0 commit comments