import os import pathlib from unittest import mock, skipUnless from django.conf import settings from django.contrib.gis.geoip2 import HAS_GEOIP2 from django.contrib.gis.geos import GEOSGeometry from django.test import SimpleTestCase if HAS_GEOIP2: from django.contrib.gis.geoip2 import GeoIP2, GeoIP2Exception # Note: Requires both the GeoIP country and city datasets. # The GEOIP_DATA path should be the only setting set (the directory # should contain links or the actual database files 'GeoLite2-City.mmdb' and # 'GeoLite2-City.mmdb'. @skipUnless( HAS_GEOIP2 and getattr(settings, "GEOIP_PATH", None), "GeoIP is required along with the GEOIP_PATH setting.", ) class GeoIPTest(SimpleTestCase): addr = "129.237.192.1" fqdn = "ku.edu" def test01_init(self): "GeoIP initialization." g1 = GeoIP2() # Everything inferred from GeoIP path path = settings.GEOIP_PATH g2 = GeoIP2(path, 0) # Passing in data path explicitly. g3 = GeoIP2.open(path, 0) # MaxMind Python API syntax. # path accepts str and pathlib.Path. if isinstance(path, str): g4 = GeoIP2(pathlib.Path(path)) else: g4 = GeoIP2(str(path)) for g in (g1, g2, g3, g4): self.assertTrue(g._country) self.assertTrue(g._city) # Only passing in the location of one database. city = os.path.join(path, "GeoLite2-City.mmdb") cntry = os.path.join(path, "GeoLite2-Country.mmdb") g4 = GeoIP2(city, country="") self.assertIsNone(g4._country) g5 = GeoIP2(cntry, city="") self.assertIsNone(g5._city) # Improper parameters. bad_params = (23, "foo", 15.23) for bad in bad_params: with self.assertRaises(GeoIP2Exception): GeoIP2(cache=bad) if isinstance(bad, str): e = GeoIP2Exception else: e = TypeError with self.assertRaises(e): GeoIP2(bad, 0) def test_no_database_file(self): invalid_path = os.path.join(os.path.dirname(__file__), "data") msg = "Could not load a database from %s." % invalid_path with self.assertRaisesMessage(GeoIP2Exception, msg): GeoIP2(invalid_path) def test02_bad_query(self): "GeoIP query parameter checking." cntry_g = GeoIP2(city="") # No city database available, these calls should fail. with self.assertRaises(GeoIP2Exception): cntry_g.city("tmc.edu") with self.assertRaises(GeoIP2Exception): cntry_g.coords("tmc.edu") # Non-string query should raise TypeError with self.assertRaises(TypeError): cntry_g.country_code(17) with self.assertRaises(TypeError): cntry_g.country_name(GeoIP2) @mock.patch("socket.gethostbyname") def test03_country(self, gethostbyname): "GeoIP country querying methods." gethostbyname.return_value = "128.249.1.1" g = GeoIP2(city="") for query in (self.fqdn, self.addr): self.assertEqual( "US", g.country_code(query), "Failed for func country_code and query %s" % query, ) self.assertEqual( "United States", g.country_name(query), "Failed for func country_name and query %s" % query, ) self.assertEqual( {"country_code": "US", "country_name": "United States"}, g.country(query), ) @mock.patch("socket.gethostbyname") def test04_city(self, gethostbyname): "GeoIP city querying methods." gethostbyname.return_value = "129.237.192.1" g = GeoIP2(country="") for query in (self.fqdn, self.addr): # Country queries should still work. self.assertEqual( "US", g.country_code(query), "Failed for func country_code and query %s" % query, ) self.assertEqual( "United States", g.country_name(query), "Failed for func country_name and query %s" % query, ) self.assertEqual( {"country_code": "US", "country_name": "United States"}, g.country(query), ) # City information dictionary. d = g.city(query) self.assertEqual("NA", d["continent_code"]) self.assertEqual("North America", d["continent_name"]) self.assertEqual("US", d["country_code"]) self.assertEqual("Lawrence", d["city"]) self.assertEqual("KS", d["region"]) self.assertEqual("America/Chicago", d["time_zone"]) self.assertFalse(d["is_in_european_union"]) geom = g.geos(query) self.assertIsInstance(geom, GEOSGeometry) for e1, e2 in ( geom.tuple, g.coords(query), g.lon_lat(query), g.lat_lon(query), ): self.assertIsInstance(e1, float) self.assertIsInstance(e2, float) def test06_ipv6_query(self): "GeoIP can lookup IPv6 addresses." g = GeoIP2() d = g.city("2002:81ed:c9a5::81ed:c9a5") # IPv6 address for www.nhm.ku.edu self.assertEqual("US", d["country_code"]) self.assertEqual("Lawrence", d["city"]) self.assertEqual("KS", d["region"]) def test_repr(self): path = settings.GEOIP_PATH g = GeoIP2(path=path) meta = g._reader.metadata() version = "%s.%s" % ( meta.binary_format_major_version, meta.binary_format_minor_version, ) country_path = g._country_file city_path = g._city_file expected = ( '' % { "version": version, "country": country_path, "city": city_path, } ) self.assertEqual(repr(g), expected) @mock.patch("socket.gethostbyname", return_value="expected") def test_check_query(self, gethostbyname): g = GeoIP2() self.assertEqual(g._check_query("127.0.0.1"), "127.0.0.1") self.assertEqual( g._check_query("2002:81ed:c9a5::81ed:c9a5"), "2002:81ed:c9a5::81ed:c9a5" ) self.assertEqual(g._check_query("invalid-ip-address"), "expected")