package com.maxmind.geoip2; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.maxmind.geoip2.exception.*; import com.maxmind.geoip2.model.InsightsResponse; import com.maxmind.geoip2.record.*; import junitparams.JUnitParamsRunner; import junitparams.Parameters; import org.hamcrest.CoreMatchers; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.util.List; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.jcabi.matchers.RegexMatchers.matchesPattern; import static org.hamcrest.core.StringStartsWith.startsWith; import static org.junit.Assert.*; @RunWith(JUnitParamsRunner.class) public class WebServiceClientTest { @Rule public final ExpectedException thrown = ExpectedException.none(); @Rule public final WireMockRule wireMockRule = new WireMockRule(0); // 0 picks random port @Test public void test200WithNoBody() throws Exception { WebServiceClient client = createSuccessClient("insights", "me", ""); thrown.expect(GeoIp2Exception.class); thrown.expectMessage("Received a 200 response but could not decode it as JSON"); client.insights(); } @Test public void test200WithInvalidJson() throws Exception { WebServiceClient client = createSuccessClient("insights", "me", "{"); thrown.expect(GeoIp2Exception.class); thrown.expectMessage("Received a 200 response but could not decode it as JSON"); client.insights(); } @Test public void test200WithDefaultValues() throws Exception { WebServiceClient client = createSuccessClient("insights", "1.2.3.13", "{\"traits\":{\"ip_address\":\"1.2.3.13\"}}"); InsightsResponse insights = client.insights(InetAddress .getByName("1.2.3.13")); assertThat(insights.toString(), CoreMatchers.startsWith("com.maxmind.geoip2.model.InsightsResponse [ {")); City city = insights.getCity(); assertNotNull(city); assertNull(city.getConfidence()); Continent continent = insights.getContinent(); assertNotNull(continent); assertNull(continent.getCode()); Country country = insights.getCountry(); assertNotNull(country); Location location = insights.getLocation(); assertNotNull(location); assertNull(location.getAccuracyRadius()); assertNull(location.getLatitude()); assertNull(location.getLongitude()); assertNull(location.getMetroCode()); assertNull(location.getTimeZone()); assertThat(location.toString(), CoreMatchers.equalTo("com.maxmind.geoip2.record.Location [ {} ]")); MaxMind maxmind = insights.getMaxMind(); assertNotNull(maxmind); assertNull(maxmind.getQueriesRemaining()); assertNotNull(insights.getPostal()); Country registeredCountry = insights.getRegisteredCountry(); assertNotNull(registeredCountry); RepresentedCountry representedCountry = insights .getRepresentedCountry(); assertNotNull(representedCountry); assertNull(representedCountry.getType()); List<Subdivision> subdivisions = insights.getSubdivisions(); assertNotNull(subdivisions); assertTrue(subdivisions.isEmpty()); Subdivision subdiv = insights.getMostSpecificSubdivision(); assertNotNull(subdiv); assertNull(subdiv.getIsoCode()); assertNull(subdiv.getConfidence()); Subdivision leastSpecificSubdiv = insights.getLeastSpecificSubdivision(); assertNotNull(leastSpecificSubdiv); assertNull(leastSpecificSubdiv.getIsoCode()); assertNull(leastSpecificSubdiv.getConfidence()); Traits traits = insights.getTraits(); assertNotNull(traits); assertNull(traits.getAutonomousSystemNumber()); assertNull(traits.getAutonomousSystemOrganization()); assertNull(traits.getDomain()); assertEquals("1.2.3.13", traits.getIpAddress()); assertNull(traits.getIsp()); assertNull(traits.getOrganization()); assertNull(traits.getUserType()); assertFalse(traits.isAnonymousProxy()); assertFalse(traits.isSatelliteProvider()); for (Country c : new Country[]{country, registeredCountry, representedCountry}) { assertNull(c.getConfidence()); assertNull(c.getIsoCode()); } for (AbstractNamedRecord r : new AbstractNamedRecord[]{city, continent, country, registeredCountry, representedCountry, subdiv}) { assertNull(r.getGeoNameId()); assertNull(r.getName()); assertTrue(r.getNames().isEmpty()); assertEquals(r.getClass().getName() + " [ {} ]", r.toString()); } } @Test public void test200OnInsightsAsMe() throws Exception { WebServiceClient client = createSuccessClient("insights", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", client.insights().getTraits().getIpAddress()); } @Test public void test200OnCityAsMe() throws Exception { WebServiceClient client = createSuccessClient("city", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", client.city().getTraits().getIpAddress()); } @Test public void test200OnCountryAsMe() throws Exception { WebServiceClient client = createSuccessClient("country", "me", "{\"traits\":{\"ip_address\":\"24.24.24.24\"}}"); assertEquals("24.24.24.24", client.country().getTraits().getIpAddress()); } @Test public void testAddressNotFound() throws Exception { thrown.expect(AddressNotFoundException.class); thrown.expectMessage("not found"); createInsightsError( "1.2.3.16", 404, "application/json", "{\"code\":\"IP_ADDRESS_NOT_FOUND\",\"error\":\"not found\"}" ); } @Test public void testAddressReserved() throws Exception { thrown.expect(AddressNotFoundException.class); thrown.expectMessage("reserved"); createInsightsError( "1.2.3.17", 400, "application/json", "{\"code\":\"IP_ADDRESS_RESERVED\",\"error\":\"reserved\"}" ); } @Test public void testAddressInvalid() throws Exception { thrown.expect(InvalidRequestException.class); thrown.expectMessage("invalid"); createInsightsError( "1.2.3.17", 400, "application/json", "{\"code\":\"IP_ADDRESS_INVALID\",\"error\":\"invalid\"}" ); } @Test @Parameters({"INSUFFICIENT_FUNDS", "OUT_OF_QUERIES"}) public void testInsufficientCredit(String code) throws Exception { thrown.expect(OutOfQueriesException.class); thrown.expectMessage("out of credit"); createInsightsMeError( 402, "application/json", "{\"code\":\"" + code + "\",\"error\":\"out of credit\"}" ); } @Test @Parameters({"AUTHORIZATION_INVALID", "LICENSE_KEY_REQUIRED", "USER_ID_REQUIRED", "USER_ID_UNKNOWN"}) public void testInvalidAuth(String code) throws Exception { thrown.expect(AuthenticationException.class); thrown.expectMessage("Invalid auth"); createInsightsMeError( 401, "application/json", "{\"code\":\"" + code + "\",\"error\":\"Invalid auth\"}" ); } @Test public void testPermissionRequired() throws Exception { thrown.expect(PermissionRequiredException.class); thrown.expectMessage("Permission required"); createInsightsMeError( 403, "application/json", "{\"code\":\"PERMISSION_REQUIRED\",\"error\":\"Permission required\"}" ); } @Test public void testInvalidRequest() throws Exception { thrown.expect(InvalidRequestException.class); thrown.expectMessage("IP invalid"); createInsightsMeError( 400, "application/json", "{\"code\":\"IP_ADDRESS_INVALID\",\"error\":\"IP invalid\"}" ); } @Test public void test400WithInvalidJson() throws Exception { thrown.expect(HttpException.class); thrown.expectMessage(matchesPattern("Received a 400 error for .*/geoip/v2.1/insights/me but it did not include the expected JSON body: \\{blah\\}")); createInsightsMeError( 400, "application/json", "{blah}" ); } @Test public void test400WithNoBody() throws Exception { thrown.expect(HttpException.class); thrown.expectMessage(matchesPattern("Received a 400 error for .*/geoip/v2.1/insights/me but it did not include the expected JSON body: ")); createInsightsMeError( 400, "application/json", "" ); } @Test public void test400WithUnexpectedContentType() throws Exception { thrown.expect(HttpException.class); thrown.expectMessage(matchesPattern("Received a 400 error for .*/geoip/v2.1/insights/me but it did not include the expected JSON body: text")); createInsightsMeError( 400, "text/plain", "text" ); } @Test public void test400WithUnexpectedJson() throws Exception { thrown.expect(HttpException.class); thrown.expectMessage("Error response contains JSON but it does not specify code or error keys: {\"not\":\"expected\"}"); createInsightsMeError( 400, "application/json", "{\"not\":\"expected\"}" ); } @Test public void test300() throws Exception { thrown.expect(HttpException.class); thrown.expectMessage(startsWith("Received an unexpected HTTP status (300)")); createInsightsMeError( 300, "application/json", "" ); } @Test public void test500() throws Exception { thrown.expect(HttpException.class); thrown.expectMessage(startsWith("Received a server error (500)")); createInsightsMeError( 500, "application/json", "" ); } private void createInsightsError(String ip, int status, String contentType, String responseContent) throws Exception { WebServiceClient client = createClient( "insights", ip, status, contentType, responseContent ); client.insights(InetAddress.getByName(ip)); } private void createInsightsMeError(int status, String contentType, String responseContent) throws Exception { WebServiceClient client = createClient( "insights", "me", status, contentType, responseContent ); client.insights(); } private WebServiceClient createSuccessClient(String service, String ip, String responseContent) throws UnsupportedEncodingException { return createClient( service, ip, 200, "application/vnd.maxmind.com-" + service + "+json; charset=UTF-8; version=2.1\n", responseContent ); } private WebServiceClient createClient(String service, String ip, int status, String contentType, String responseContent) throws UnsupportedEncodingException { byte[] body = responseContent.getBytes("UTF-8"); stubFor(get(urlEqualTo("/geoip/v2.1/" + service + "/" + ip)) .withHeader("Accept", equalTo("application/json")) .willReturn(aResponse() .withStatus(status) // This is wrong if we use non-ASCII characters, but we don't currently .withHeader("Content-Length", Integer.toString(body.length)) .withHeader("Content-Type", contentType) .withBody(body))); return new WebServiceClient.Builder(6, "0123456789") .host("localhost") .port(this.wireMockRule.port()) .disableHttps() .build(); } }