Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/tests/gis_tests/test_geoip2.py
blob: 150ca2cd4ecd95b5e5779d26483400a18e1cd561 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import os
import unittest
from unittest import skipUnless

from django.conf import settings
from django.contrib.gis.geoip2 import HAS_GEOIP2
from django.contrib.gis.geos import HAS_GEOS, GEOSGeometry
from django.test import mock
from django.utils import six

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(unittest.TestCase):
    addr = '128.249.1.1'
    fqdn = 'tmc.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.

        for g in (g1, g2, g3):
            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:
            self.assertRaises(GeoIP2Exception, GeoIP2, cache=bad)
            if isinstance(bad, six.string_types):
                e = GeoIP2Exception
            else:
                e = TypeError
            self.assertRaises(e, GeoIP2, bad, 0)

    def test02_bad_query(self):
        "GeoIP query parameter checking."
        cntry_g = GeoIP2(city='<foo>')
        # No city database available, these calls should fail.
        self.assertRaises(GeoIP2Exception, cntry_g.city, 'tmc.edu')
        self.assertRaises(GeoIP2Exception, cntry_g.coords, 'tmc.edu')

        # Non-string query should raise TypeError
        self.assertRaises(TypeError, cntry_g.country_code, 17)
        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='<foo>')

        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)
            )

    @skipUnless(HAS_GEOS, "Geos is required")
    @mock.patch('socket.gethostbyname')
    def test04_city(self, gethostbyname):
        "GeoIP city querying methods."
        gethostbyname.return_value = '128.249.1.1'
        g = GeoIP2(country='<foo>')

        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('US', d['country_code'])
            self.assertEqual('Houston', d['city'])
            self.assertEqual('TX', d['region'])

            geom = g.geos(query)
            self.assertIsInstance(geom, GEOSGeometry)
            lon, lat = (-95.4010, 29.7079)
            lat_lon = g.lat_lon(query)
            lat_lon = (lat_lon[1], lat_lon[0])
            for tup in (geom.tuple, g.coords(query), g.lon_lat(query), lat_lon):
                self.assertAlmostEqual(lon, tup[0], 4)
                self.assertAlmostEqual(lat, tup[1], 4)

    @mock.patch('socket.gethostbyname')
    def test05_unicode_response(self, gethostbyname):
        "GeoIP strings should be properly encoded (#16553)."
        gethostbyname.return_value = '194.27.42.76'
        g = GeoIP2()
        d = g.city("nigde.edu.tr")
        self.assertEqual('Niğde', d['city'])
        d = g.country('200.26.205.1')
        # Some databases have only unaccented countries
        self.assertIn(d['country_name'], ('Curaçao', 'Curacao'))

    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 = '<GeoIP2 [v%(version)s] _country_file="%(country)s", _city_file="%(city)s">' % {
            'version': version,
            'country': country_path,
            'city': city_path,
        }
        self.assertEqual(repr(g), expected)