test_geoip.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import os
  4. import socket
  5. import unittest
  6. import warnings
  7. from unittest import skipUnless
  8. from django.conf import settings
  9. from django.contrib.gis.geoip import HAS_GEOIP
  10. from django.contrib.gis.geos import HAS_GEOS, GEOSGeometry
  11. from django.test import ignore_warnings
  12. from django.utils import six
  13. from django.utils.deprecation import RemovedInDjango20Warning
  14. from django.utils.encoding import force_text
  15. if HAS_GEOIP:
  16. from django.contrib.gis.geoip import GeoIP, GeoIPException
  17. from django.contrib.gis.geoip.prototypes import GeoIP_lib_version
  18. # Note: Requires use of both the GeoIP country and city datasets.
  19. # The GEOIP_DATA path should be the only setting set (the directory
  20. # should contain links or the actual database files 'GeoIP.dat' and
  21. # 'GeoLiteCity.dat'.
  22. @skipUnless(
  23. HAS_GEOIP and getattr(settings, "GEOIP_PATH", None),
  24. "GeoIP is required along with the GEOIP_PATH setting."
  25. )
  26. @ignore_warnings(category=RemovedInDjango20Warning)
  27. class GeoIPTest(unittest.TestCase):
  28. addr = '162.242.220.127'
  29. fqdn = 'www.djangoproject.com'
  30. def _is_dns_available(self, domain):
  31. # Naive check to see if there is DNS available to use.
  32. # Used to conditionally skip fqdn geoip checks.
  33. # See #25407 for details.
  34. ErrClass = socket.error if six.PY2 else OSError
  35. try:
  36. socket.gethostbyname(domain)
  37. return True
  38. except ErrClass:
  39. return False
  40. def test01_init(self):
  41. "Testing GeoIP initialization."
  42. g1 = GeoIP() # Everything inferred from GeoIP path
  43. path = settings.GEOIP_PATH
  44. g2 = GeoIP(path, 0) # Passing in data path explicitly.
  45. g3 = GeoIP.open(path, 0) # MaxMind Python API syntax.
  46. for g in (g1, g2, g3):
  47. self.assertTrue(g._country)
  48. self.assertTrue(g._city)
  49. # Only passing in the location of one database.
  50. city = os.path.join(path, 'GeoLiteCity.dat')
  51. cntry = os.path.join(path, 'GeoIP.dat')
  52. g4 = GeoIP(city, country='')
  53. self.assertIsNone(g4._country)
  54. g5 = GeoIP(cntry, city='')
  55. self.assertIsNone(g5._city)
  56. # Improper parameters.
  57. bad_params = (23, 'foo', 15.23)
  58. for bad in bad_params:
  59. with self.assertRaises(GeoIPException):
  60. GeoIP(cache=bad)
  61. if isinstance(bad, six.string_types):
  62. e = GeoIPException
  63. else:
  64. e = TypeError
  65. with self.assertRaises(e):
  66. GeoIP(bad, 0)
  67. def test02_bad_query(self):
  68. "Testing GeoIP query parameter checking."
  69. cntry_g = GeoIP(city='<foo>')
  70. # No city database available, these calls should fail.
  71. with self.assertRaises(GeoIPException):
  72. cntry_g.city('google.com')
  73. with self.assertRaises(GeoIPException):
  74. cntry_g.coords('yahoo.com')
  75. # Non-string query should raise TypeError
  76. with self.assertRaises(TypeError):
  77. cntry_g.country_code(17)
  78. with self.assertRaises(TypeError):
  79. cntry_g.country_name(GeoIP)
  80. def test03_country(self):
  81. "Testing GeoIP country querying methods."
  82. g = GeoIP(city='<foo>')
  83. queries = [self.addr]
  84. if self._is_dns_available(self.fqdn):
  85. queries.append(self.fqdn)
  86. for query in queries:
  87. for func in (g.country_code, g.country_code_by_addr, g.country_code_by_name):
  88. self.assertEqual('US', func(query), 'Failed for func %s and query %s' % (func, query))
  89. for func in (g.country_name, g.country_name_by_addr, g.country_name_by_name):
  90. self.assertEqual('United States', func(query), 'Failed for func %s and query %s' % (func, query))
  91. self.assertEqual({'country_code': 'US', 'country_name': 'United States'},
  92. g.country(query))
  93. @skipUnless(HAS_GEOS, "Geos is required")
  94. def test04_city(self):
  95. "Testing GeoIP city querying methods."
  96. g = GeoIP(country='<foo>')
  97. queries = [self.addr]
  98. if self._is_dns_available(self.fqdn):
  99. queries.append(self.fqdn)
  100. for query in queries:
  101. # Country queries should still work.
  102. for func in (g.country_code, g.country_code_by_addr, g.country_code_by_name):
  103. self.assertEqual('US', func(query))
  104. for func in (g.country_name, g.country_name_by_addr, g.country_name_by_name):
  105. self.assertEqual('United States', func(query))
  106. self.assertEqual({'country_code': 'US', 'country_name': 'United States'},
  107. g.country(query))
  108. # City information dictionary.
  109. d = g.city(query)
  110. self.assertEqual('USA', d['country_code3'])
  111. self.assertEqual('San Antonio', d['city'])
  112. self.assertEqual('TX', d['region'])
  113. self.assertEqual(210, d['area_code'])
  114. geom = g.geos(query)
  115. self.assertIsInstance(geom, GEOSGeometry)
  116. lon, lat = (-98, 29)
  117. lat_lon = g.lat_lon(query)
  118. lat_lon = (lat_lon[1], lat_lon[0])
  119. for tup in (geom.tuple, g.coords(query), g.lon_lat(query), lat_lon):
  120. self.assertAlmostEqual(lon, tup[0], 0)
  121. self.assertAlmostEqual(lat, tup[1], 0)
  122. def test05_unicode_response(self):
  123. "Testing that GeoIP strings are properly encoded, see #16553."
  124. g = GeoIP()
  125. fqdn = "hs-duesseldorf.de"
  126. if self._is_dns_available(fqdn):
  127. d = g.city(fqdn)
  128. self.assertEqual('Düsseldorf', d['city'])
  129. d = g.country('200.26.205.1')
  130. # Some databases have only unaccented countries
  131. self.assertIn(d['country_name'], ('Curaçao', 'Curacao'))
  132. def test_deprecation_warning(self):
  133. with warnings.catch_warnings(record=True) as warns:
  134. warnings.simplefilter('always')
  135. GeoIP()
  136. self.assertEqual(len(warns), 1)
  137. msg = str(warns[0].message)
  138. self.assertIn('django.contrib.gis.geoip is deprecated', msg)
  139. def test_repr(self):
  140. path = settings.GEOIP_PATH
  141. g = GeoIP(path=path)
  142. country_path = g._country_file
  143. city_path = g._city_file
  144. if GeoIP_lib_version:
  145. expected = '<GeoIP [v%(version)s] _country_file="%(country)s", _city_file="%(city)s">' % {
  146. 'version': force_text(GeoIP_lib_version()),
  147. 'country': country_path,
  148. 'city': city_path,
  149. }
  150. else:
  151. expected = '<GeoIP _country_file="%(country)s", _city_file="%(city)s">' % {
  152. 'country': country_path,
  153. 'city': city_path,
  154. }
  155. self.assertEqual(repr(g), expected)