From ccef26b2ec769a3fddbe41223a2d550a32283d36 Mon Sep 17 00:00:00 2001 From: blag Date: Sat, 14 Mar 2026 22:01:08 -0700 Subject: [PATCH 1/8] Fix tests for modern Django and Python --- Makefile | 10 +++--- pyproject.toml | 62 +++++++++++++++++----------------- tests/test_admin.py | 15 +++----- tests/test_template_filters.py | 7 +--- tests/test_views.py | 9 ++--- 5 files changed, 45 insertions(+), 58 deletions(-) diff --git a/Makefile b/Makefile index 5a87979..8ca01cb 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ ruff: example: DJANGO_SETTINGS_MODULE=example.settings PYTHONPATH=. \ - django-admin.py runserver + django-admin runserver check: DJANGO_SETTINGS_MODULE=example.settings PYTHONPATH=. \ @@ -19,11 +19,11 @@ generate-mmdb-fixtures: test: generate-mmdb-fixtures DJANGO_SETTINGS_MODULE=tests.settings PYTHONPATH=. \ - django-admin.py test ${TARGET} + django-admin test ${TARGET} migrations: DJANGO_SETTINGS_MODULE=tests.settings PYTHONPATH=. \ - django-admin.py makemigrations user_sessions + django-admin makemigrations user_sessions coverage: coverage erase @@ -34,8 +34,8 @@ coverage: tx-pull: tx pull -a - cd user_sessions; django-admin.py compilemessages + cd user_sessions; django-admin compilemessages tx-push: - cd user_sessions; django-admin.py makemessages -l en + cd user_sessions; django-admin makemessages -l en tx push -s diff --git a/pyproject.toml b/pyproject.toml index 4aa149f..84d7ec6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,22 +12,22 @@ requires-python = ">=3.8" keywords = ["django", "sessions"] license = {text = "MIT"} classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Web Environment", - "Framework :: Django", - "Framework :: Django :: 3.2", - "Framework :: Django :: 4.2", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Security", + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Django :: 3.2", + "Framework :: Django :: 4.2", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Security", ] dependencies = [ "Django>=3.2", @@ -43,21 +43,21 @@ issues = "https://github.com/jazzband/django-user-sessions/issues" [project.optional-dependencies] dev = [ - # Example app - "django-debug-toolbar", - # Testing - "coverage", - "tox", - "tox-pyenv", - "detox", - # Transifex - "transifex-client", - # Documentation - "Sphinx", - "sphinx_rtd_theme", - # Build - "bumpversion", - "twine", + # Example app + "django-debug-toolbar", + # Testing + "coverage", + "tox", + "tox-pyenv", + "detox", + # Transifex + "transifex-client", + # Documentation + "Sphinx", + "sphinx_rtd_theme", + # Build + "bumpversion", + "twine", ] [tool.ruff] diff --git a/tests/test_admin.py b/tests/test_admin.py index e9238f9..f1dd685 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -25,37 +25,32 @@ def setUp(self): self.admin_url = reverse('admin:user_sessions_session_changelist') def test_list(self): - with self.assertWarnsRegex(UserWarning, r"The address 1\.1\.1\.1 is not in the database"): - response = self.client.get(self.admin_url) + response = self.client.get(self.admin_url) self.assertContains(response, 'Select session to change') self.assertContains(response, '127.0.0.1') self.assertContains(response, '20.13.1.1') self.assertContains(response, '1.1.1.1') def test_search(self): - with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): - response = self.client.get(self.admin_url, {'q': 'bouke'}) + response = self.client.get(self.admin_url, {'q': 'bouke'}) self.assertContains(response, '127.0.0.1') self.assertNotContains(response, '20.13.1.1') self.assertNotContains(response, '1.1.1.1') def test_mine(self): my_sessions = f"{self.admin_url}?{urlencode({'owner': 'my'})}" - with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): - response = self.client.get(my_sessions) + response = self.client.get(my_sessions) self.assertContains(response, '127.0.0.1') self.assertNotContains(response, '1.1.1.1') def test_expired(self): expired = f"{self.admin_url}?{urlencode({'active': '0'})}" - with self.assertWarnsRegex(UserWarning, r"The address 20\.13\.1\.1 is not in the database"): - response = self.client.get(expired) + response = self.client.get(expired) self.assertContains(response, '20.13.1.1') self.assertNotContains(response, '1.1.1.1') def test_unexpired(self): unexpired = f"{self.admin_url}?{urlencode({'active': '1'})}" - with self.assertWarnsRegex(UserWarning, r"The address 1\.1\.1\.1 is not in the database"): - response = self.client.get(unexpired) + response = self.client.get(unexpired) self.assertContains(response, '1.1.1.1') self.assertNotContains(response, '20.13.1.1') diff --git a/tests/test_template_filters.py b/tests/test_template_filters.py index 4589596..a56f2be 100644 --- a/tests/test_template_filters.py +++ b/tests/test_template_filters.py @@ -25,12 +25,7 @@ class LocationTemplateFilterTest(TestCase): @override_settings(GEOIP_PATH=None) def test_no_location(self): - with self.assertWarnsRegex( - UserWarning, - r"The address 127\.0\.0\.1 is not in the database", - ): - loc = location('127.0.0.1') - self.assertEqual(loc, None) + self.assertIsNone(location('127.0.0.1')) @skipUnless(geoip, geoip_msg) def test_city(self): diff --git a/tests/test_views.py b/tests/test_views.py index 53435f5..fce2921 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -20,8 +20,7 @@ def test_list(self): self.user.session_set.create(session_key='ABC123', ip='127.0.0.1', expire_date=now() + timedelta(days=1), user_agent='Firefox') - with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): - response = self.client.get(reverse('user_sessions:session_list')) + response = self.client.get(reverse('user_sessions:session_list')) self.assertContains(response, 'Active Sessions') self.assertContains(response, 'Firefox') self.assertNotContains(response, 'ABC123') @@ -36,8 +35,7 @@ def test_delete_all_other(self): self.user.session_set.create(ip='127.0.0.1', expire_date=now() + timedelta(days=1)) self.assertEqual(self.user.session_set.count(), 2) response = self.client.post(reverse('user_sessions:session_delete_other')) - with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): - self.assertRedirects(response, reverse('user_sessions:session_list')) + self.assertRedirects(response, reverse('user_sessions:session_list')) self.assertEqual(self.user.session_set.count(), 1) def test_delete_some_other(self): @@ -46,6 +44,5 @@ def test_delete_some_other(self): self.assertEqual(self.user.session_set.count(), 2) response = self.client.post(reverse('user_sessions:session_delete', args=[other.session_key])) - with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): - self.assertRedirects(response, reverse('user_sessions:session_list')) + self.assertRedirects(response, reverse('user_sessions:session_list')) self.assertEqual(self.user.session_set.count(), 1) From df77e48f199c776bdfc6d679ad27e5313cb6bb39 Mon Sep 17 00:00:00 2001 From: blag Date: Sun, 15 Mar 2026 03:18:00 -0700 Subject: [PATCH 2/8] Modernize test configs and write MMDB files with a Python script --- .github/workflows/test.yml | 24 +++------- Makefile | 3 +- pyproject.toml | 1 + tests/Dockerfile | 9 ---- tests/generate_mmdb.pl | 89 -------------------------------------- tests/generate_mmdb.py | 63 +++++++++++++++++++++++++++ tox.ini | 24 +++++----- 7 files changed, 84 insertions(+), 129 deletions(-) delete mode 100644 tests/Dockerfile delete mode 100755 tests/generate_mmdb.pl create mode 100644 tests/generate_mmdb.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9a7bd99..01287e1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,28 +10,18 @@ jobs: fail-fast: false max-parallel: 5 matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12-dev'] - django-version: ['3.2', '4.2', 'main'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.15-dev'] + django-version: ['5.2', '6.0', 'main'] exclude: + - python-version: '3.10' + django-version: '6.0' - python-version: '3.11' - django-version: '3.2' - - python-version: '3.12-dev' - django-version: '3.2' - - - python-version: '3.11' - django-version: '4.0' - - python-version: '3.12-dev' - django-version: '4.0' - - - python-version: '3.12-dev' - django-version: '4.1' + django-version: '6.0' - - python-version: '3.8' - django-version: 'main' - - python-version: '3.9' - django-version: 'main' - python-version: '3.10' django-version: 'main' + - python-version: '3.11' + django-version: 'main' steps: - uses: actions/checkout@v3 diff --git a/Makefile b/Makefile index 8ca01cb..c04d6fe 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,7 @@ check: python -Wd example/manage.py check generate-mmdb-fixtures: - docker --context=default buildx build -f tests/Dockerfile --tag test-mmdb-maker tests - docker run --rm --volume $$(pwd)/tests:/data test-mmdb-maker + [ -e tests/test_city.mmdb ] || python3 generate_mmdb.py test: generate-mmdb-fixtures DJANGO_SETTINGS_MODULE=tests.settings PYTHONPATH=. \ diff --git a/pyproject.toml b/pyproject.toml index 84d7ec6..46bd448 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ dev = [ # Build "bumpversion", "twine", + "mmdb-writer", ] [tool.ruff] diff --git a/tests/Dockerfile b/tests/Dockerfile deleted file mode 100644 index d363dc6..0000000 --- a/tests/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM perl:latest - -RUN cpanm MaxMind::DB::Writer - -COPY generate_mmdb.pl / - -VOLUME ["/data"] - -CMD ["perl", "/generate_mmdb.pl"] diff --git a/tests/generate_mmdb.pl b/tests/generate_mmdb.pl deleted file mode 100755 index 2b2f5b6..0000000 --- a/tests/generate_mmdb.pl +++ /dev/null @@ -1,89 +0,0 @@ -#!perl - -use MaxMind::DB::Writer::Tree; - -my %city_types = ( - city => 'map', - code => 'utf8_string', - continent => 'map', - country => 'map', - en => 'utf8_string', - is_in_european_union => 'boolean', - iso_code => 'utf8_string', - latitude => 'double', - location => 'map', - longitude => 'double', - metro_code => 'utf8_string', - names => 'map', - postal => 'map', - subdivisions => ['array', 'map'], - region => 'utf8_string', - time_zone => 'utf8_string', -); - -my $city_tree = MaxMind::DB::Writer::Tree->new( - ip_version => 6, - record_size => 24, - database_type => 'GeoLite2-City', - languages => ['en'], - description => { en => 'Test database of IP city data' }, - map_key_type_callback => sub { $city_types{ $_[0] } }, -); - -$city_tree->insert_network( - '44.55.66.77/32', - { - city => { names => {en => 'San Diego'} }, - continent => { code => 'NA', names => {en => 'North America'} }, - country => { iso_code => 'US', names => {en => 'United States'} }, - is_in_european_union => false, - location => { - latitude => 37.751, - longitude => -97.822, - metro_code => 'custom metro code', - time_zone => 'America/Los Angeles', - }, - postal => { code => 'custom postal code' }, - subdivisions => [ - { iso_code => 'ABC', names => {en => 'Absolute Basic Class'} }, - ], - }, -); - -my $outfile = ($ENV{'DATA_DIR'} || '/data/') . ($ENV{'CITY_FILENAME'} || 'test_city.mmdb'); -open my $fh, '>:raw', $outfile; -$city_tree->write_tree($fh); - - - -my %country_types = ( - country => 'map', - iso_code => 'utf8_string', - names => 'map', - en => 'utf8_string', -); - -my $country_tree = MaxMind::DB::Writer::Tree->new( - ip_version => 6, - record_size => 24, - database_type => 'GeoLite2-Country', - languages => ['en'], - description => { en => 'Test database of IP country data' }, - map_key_type_callback => sub { $country_types{ $_[0] } }, -); - -$country_tree->insert_network( - '8.8.8.8/32', - { - country => { - iso_code => 'US', - names => { - en => 'United States', - }, - }, - }, -); - -my $outfile = ($ENV{'DATA_DIR'} || '/data/') . ($ENV{'COUNTRY_FILENAME'} || 'test_country.mmdb'); -open my $fh, '>:raw', $outfile; -$country_tree->write_tree($fh); diff --git a/tests/generate_mmdb.py b/tests/generate_mmdb.py new file mode 100644 index 0000000..91a54a8 --- /dev/null +++ b/tests/generate_mmdb.py @@ -0,0 +1,63 @@ +from netaddr import IPSet + +from mmdb_writer import MMDBWriter + + +city_writer = MMDBWriter() + +city_writer.insert_network( + IPSet(["44.55.66.77/32"]), + { + "city": { + "names": { + "en": "San Diego", + }, + }, + "continent": { + "code": "NA", + "names": { + "en": "North America", + }, + }, + "country": { + "iso_code": "US", + "names": { + "en": "United States", + }, + }, + "is_in_european_union": False, + "location": { + "latitude": 37.751, + "longitude": -97.822, + "metro_code": "custom metro code", + "time_zone": "America/Los Angeles", + }, + "postal": { + "code": "custom postal code", + }, + "subdivisions": [ + { + "iso_code": "ABC", + "names": { + "en": 'Absolute Basic Class', + }, + }, + ], + }, +) + +city_writer.to_db_file("tests/test_city.mmdb") + +# country_writer = MMDBWriter( +# IPSet(["8.8.8.8/32"]), +# { +# "country": { +# "iso_code": "US", +# "names": { +# "en": "United States", +# }, +# }, +# }, +# ) + +# country_writer.to_db_file("tests/test_country.mmdb") diff --git a/tox.ini b/tox.ini index 406b88a..312701d 100644 --- a/tox.ini +++ b/tox.ini @@ -2,37 +2,37 @@ ; Minimum version of Tox minversion = 1.8 envlist = - ; https://docs.djangoproject.com/en/4.2/faq/install/#what-python-version-can-i-use-with-django - py{37}-dj32 - py{38,39,310}-dj32 - py{311,312}-dj{42,main} + ; https://docs.djangoproject.com/en/6.0/faq/install/#what-python-version-can-i-use-with-django + py3{10,11,12,13,14}-dj52 + py3{12,13,14}-dj{60,main} [gh-actions] python = - 3.8: py38 - 3.9: py39 3.10: py310 3.11: py311 3.12: py312 + 3.13: py313 + 3.14: py314 [gh-actions:env] DJANGO = - 3.2: dj32 - 4.2: dj42 + 5.2: dj52 + 6.0: dj60 main: djmain [testenv] commands = - make generate-mmdb-fixtures + python3 tests/generate_mmdb.py coverage run {envbindir}/django-admin test -v 2 --pythonpath=./ --settings=tests.settings coverage report coverage xml deps = coverage - dj32: Django>=3.2,<4.0 - dj42: Django>=4.2,<4.3 + dj52: Django>=5.2,<6.0 + dj60: Django>=6.0,<6.1 djmain: https://github.com/django/django/archive/main.tar.gz geoip2 + mmdb-writer + netaddr ignore_outcome = djmain: True -allowlist_externals = make From 8a0e81ef495d00e01e738a357fee23a6b9c0c474 Mon Sep 17 00:00:00 2001 From: blag Date: Sun, 15 Mar 2026 16:09:34 -0700 Subject: [PATCH 3/8] Remove commented-out code to generate tests/test_country.mmdb --- tests/generate_mmdb.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/generate_mmdb.py b/tests/generate_mmdb.py index 91a54a8..7b5a797 100644 --- a/tests/generate_mmdb.py +++ b/tests/generate_mmdb.py @@ -47,17 +47,3 @@ ) city_writer.to_db_file("tests/test_city.mmdb") - -# country_writer = MMDBWriter( -# IPSet(["8.8.8.8/32"]), -# { -# "country": { -# "iso_code": "US", -# "names": { -# "en": "United States", -# }, -# }, -# }, -# ) - -# country_writer.to_db_file("tests/test_country.mmdb") From 20773780aaac4bea9ff54e1b36aee4bdd8448930 Mon Sep 17 00:00:00 2001 From: blag Date: Sun, 15 Mar 2026 16:10:30 -0700 Subject: [PATCH 4/8] Make indentation in pyproject.toml consistent to 2 spaces --- pyproject.toml | 68 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 46bd448..120e530 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"] [project] name = "django-user-sessions" authors = [ - {name = "Bouke Haarsma", email = "bouke@haarsma.eu"}, + {name = "Bouke Haarsma", email = "bouke@haarsma.eu"}, ] description = "Django sessions with a foreign key to the user" readme = "README.rst" @@ -12,25 +12,25 @@ requires-python = ">=3.8" keywords = ["django", "sessions"] license = {text = "MIT"} classifiers = [ - "Development Status :: 5 - Production/Stable", - "Environment :: Web Environment", - "Framework :: Django", - "Framework :: Django :: 3.2", - "Framework :: Django :: 4.2", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Security", + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Django :: 3.2", + "Framework :: Django :: 4.2", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Security", ] dependencies = [ - "Django>=3.2", + "Django>=3.2", ] dynamic = ["version"] @@ -43,22 +43,22 @@ issues = "https://github.com/jazzband/django-user-sessions/issues" [project.optional-dependencies] dev = [ - # Example app - "django-debug-toolbar", - # Testing - "coverage", - "tox", - "tox-pyenv", - "detox", - # Transifex - "transifex-client", - # Documentation - "Sphinx", - "sphinx_rtd_theme", - # Build - "bumpversion", - "twine", - "mmdb-writer", + # Example app + "django-debug-toolbar", + # Testing + "coverage", + "tox", + "tox-pyenv", + "detox", + # Transifex + "transifex-client", + # Documentation + "Sphinx", + "sphinx_rtd_theme", + # Build + "bumpversion", + "twine", + "mmdb-writer", ] [tool.ruff] From 55165f1b7fc0bd906b5c27735e44fac1e7ca0f93 Mon Sep 17 00:00:00 2001 From: blag Date: Sun, 15 Mar 2026 16:11:03 -0700 Subject: [PATCH 5/8] Don't explicitly install transitive netaddr dependency in tox environments --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index 312701d..e746c39 100644 --- a/tox.ini +++ b/tox.ini @@ -33,6 +33,5 @@ deps = djmain: https://github.com/django/django/archive/main.tar.gz geoip2 mmdb-writer - netaddr ignore_outcome = djmain: True From 617727c2fe0db0d1c039fbcdeec4a36094284113 Mon Sep 17 00:00:00 2001 From: blag Date: Sun, 15 Mar 2026 16:40:37 -0700 Subject: [PATCH 6/8] Revert "Remove commented-out code to generate tests/test_country.mmdb" This reverts commit 8a0e81ef495d00e01e738a357fee23a6b9c0c474. --- tests/generate_mmdb.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/generate_mmdb.py b/tests/generate_mmdb.py index 7b5a797..91a54a8 100644 --- a/tests/generate_mmdb.py +++ b/tests/generate_mmdb.py @@ -47,3 +47,17 @@ ) city_writer.to_db_file("tests/test_city.mmdb") + +# country_writer = MMDBWriter( +# IPSet(["8.8.8.8/32"]), +# { +# "country": { +# "iso_code": "US", +# "names": { +# "en": "United States", +# }, +# }, +# }, +# ) + +# country_writer.to_db_file("tests/test_country.mmdb") From 416e054f1df304aac86e4dd53e501883f641bec1 Mon Sep 17 00:00:00 2001 From: blag Date: Sun, 19 Apr 2026 07:09:52 -0700 Subject: [PATCH 7/8] Enable using MMDB files containing only countries --- user_sessions/templatetags/user_sessions.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/user_sessions/templatetags/user_sessions.py b/user_sessions/templatetags/user_sessions.py index a7b5249..cb2b8d7 100644 --- a/user_sessions/templatetags/user_sessions.py +++ b/user_sessions/templatetags/user_sessions.py @@ -107,7 +107,10 @@ def device(value): @register.filter def city(value): - location = geoip() and geoip().city(value) + try: + location = geoip() and geoip().city(value) + except Exception: + location = None if location and location['city']: return location['city'] return None @@ -115,7 +118,11 @@ def city(value): @register.filter def country(value): - location = geoip() and geoip().country(value) + try: + location = geoip() and geoip().country(value) + except Exception as e: + warnings.warn(str(e), stacklevel=2) + location = geoip() and geoip().city(value) if location and location['country_name']: return location['country_name'] return None From 1b127ec5d8c77f2cc6706a5f1ad5346f142bdcca Mon Sep 17 00:00:00 2001 From: blag Date: Sun, 19 Apr 2026 07:10:37 -0700 Subject: [PATCH 8/8] Update fixture generation and tests for country MMDB files --- Makefile | 9 ++++-- tests/generate_mmdb.py | 50 ++++++++++++++++++++--------- tests/test_admin.py | 15 ++++++--- tests/test_template_filters.py | 57 ++++++++++++++++++++++++++++------ tests/test_views.py | 9 ++++-- 5 files changed, 106 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index c04d6fe..ea31c3f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TARGET?=tests -.PHONY: ruff example test coverage +.PHONY: ruff example test coverage generate-mmdb-fixtures ruff: ruff user_sessions example tests @@ -13,8 +13,11 @@ check: DJANGO_SETTINGS_MODULE=example.settings PYTHONPATH=. \ python -Wd example/manage.py check -generate-mmdb-fixtures: - [ -e tests/test_city.mmdb ] || python3 generate_mmdb.py +$(TARGET)/test_city.mmdb $(TARGET)/test_country.mmdb: $(TARGET)/generate_mmdb.py + python3 $(TARGET)/generate_mmdb.py + stat $@ + +generate-mmdb-fixtures: $(TARGET)/test_city.mmdb $(TARGET)/test_country.mmdb test: generate-mmdb-fixtures DJANGO_SETTINGS_MODULE=tests.settings PYTHONPATH=. \ diff --git a/tests/generate_mmdb.py b/tests/generate_mmdb.py index 91a54a8..b902a6c 100644 --- a/tests/generate_mmdb.py +++ b/tests/generate_mmdb.py @@ -3,7 +3,7 @@ from mmdb_writer import MMDBWriter -city_writer = MMDBWriter() +city_writer = MMDBWriter(database_type="GeoIP2-City") city_writer.insert_network( IPSet(["44.55.66.77/32"]), @@ -45,19 +45,41 @@ ], }, ) - +city_writer.insert_network( + IPSet(["8.8.8.8/32"]), + { + "country": { + "iso_code": "US", + "names": { + "en": "United States", + }, + }, + }, +) city_writer.to_db_file("tests/test_city.mmdb") -# country_writer = MMDBWriter( -# IPSet(["8.8.8.8/32"]), -# { -# "country": { -# "iso_code": "US", -# "names": { -# "en": "United States", -# }, -# }, -# }, -# ) +country_writer = MMDBWriter(database_type="GeoIP2-Country") +country_writer.insert_network( + IPSet(["55.66.77.88/32"]), + { + "country": { + "iso_code": "US", + "names": { + "en": "United States", + }, + }, + }, +) +country_writer.insert_network( + IPSet(["8.8.4.4/32"]), + { + "country": { + "iso_code": "US", + "names": { + "en": "United States", + }, + }, + }, +) -# country_writer.to_db_file("tests/test_country.mmdb") +country_writer.to_db_file("tests/test_country.mmdb") diff --git a/tests/test_admin.py b/tests/test_admin.py index f1dd685..e9238f9 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -25,32 +25,37 @@ def setUp(self): self.admin_url = reverse('admin:user_sessions_session_changelist') def test_list(self): - response = self.client.get(self.admin_url) + with self.assertWarnsRegex(UserWarning, r"The address 1\.1\.1\.1 is not in the database"): + response = self.client.get(self.admin_url) self.assertContains(response, 'Select session to change') self.assertContains(response, '127.0.0.1') self.assertContains(response, '20.13.1.1') self.assertContains(response, '1.1.1.1') def test_search(self): - response = self.client.get(self.admin_url, {'q': 'bouke'}) + with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): + response = self.client.get(self.admin_url, {'q': 'bouke'}) self.assertContains(response, '127.0.0.1') self.assertNotContains(response, '20.13.1.1') self.assertNotContains(response, '1.1.1.1') def test_mine(self): my_sessions = f"{self.admin_url}?{urlencode({'owner': 'my'})}" - response = self.client.get(my_sessions) + with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): + response = self.client.get(my_sessions) self.assertContains(response, '127.0.0.1') self.assertNotContains(response, '1.1.1.1') def test_expired(self): expired = f"{self.admin_url}?{urlencode({'active': '0'})}" - response = self.client.get(expired) + with self.assertWarnsRegex(UserWarning, r"The address 20\.13\.1\.1 is not in the database"): + response = self.client.get(expired) self.assertContains(response, '20.13.1.1') self.assertNotContains(response, '1.1.1.1') def test_unexpired(self): unexpired = f"{self.admin_url}?{urlencode({'active': '1'})}" - response = self.client.get(unexpired) + with self.assertWarnsRegex(UserWarning, r"The address 1\.1\.1\.1 is not in the database"): + response = self.client.get(unexpired) self.assertContains(response, '1.1.1.1') self.assertNotContains(response, '20.13.1.1') diff --git a/tests/test_template_filters.py b/tests/test_template_filters.py index a56f2be..2c79a44 100644 --- a/tests/test_template_filters.py +++ b/tests/test_template_filters.py @@ -3,6 +3,7 @@ from django.test import TestCase from django.test.utils import override_settings +from user_sessions.templatetags import user_sessions from user_sessions.templatetags.user_sessions import ( browser, city, country, device, location, platform, ) @@ -13,19 +14,23 @@ geoip = GeoIP2() geoip_msg = None except Exception as error_geoip2: # pragma: no cover - try: - from django.contrib.gis.geoip import GeoIP - geoip = GeoIP() - geoip_msg = None - except Exception as error_geoip: - geoip = None - geoip_msg = str(error_geoip2) + " and " + str(error_geoip) + geoip = None + geoip_msg = str(error_geoip2) class LocationTemplateFilterTest(TestCase): - @override_settings(GEOIP_PATH=None) + def setUp(self): + # Remove the cached GeoIP object, since it caches the database type and + # this fails when we switch between city and country MMDB files + user_sessions._geoip = None + + @override_settings(GEOIP_CITY="") def test_no_location(self): - self.assertIsNone(location('127.0.0.1')) + with self.assertWarnsRegex( + UserWarning, + r"The address 127\.0\.0\.1 is not in the database", + ): + self.assertIsNone(location('127.0.0.1')) @skipUnless(geoip, geoip_msg) def test_city(self): @@ -41,6 +46,40 @@ def test_locations(self): self.assertEqual('San Diego, United States', location('44.55.66.77')) +@override_settings(GEOIP_CITY="doesnt_exist") +class CountryLocationTemplateFilterTest(TestCase): + def setUp(self): + # Remove the cached GeoIP object, since it caches the database type and + # this fails when we switch between city and country MMDB files + user_sessions._geoip = None + + def test_no_location(self): + with self.assertWarnsRegex( + UserWarning, + r"The address 127\.0\.0\.1 is not in the database", + ): + self.assertIsNone(location('127.0.0.1')) + + @skipUnless(geoip, geoip_msg) + def test_city(self): + self.assertIsNone(city('55.66.77.88')) + self.assertIsNone(city('8.8.4.4')) + # Make sure it isn't somehow pulling any IP addresses from the city + # database + self.assertIsNone(city('44.55.66.77')) + self.assertIsNone(city('8.8.8.8')) + + @skipUnless(geoip, geoip_msg) + def test_country(self): + self.assertEqual('United States', country('55.66.77.88')) + self.assertEqual('United States', country('8.8.4.4')) + + @skipUnless(geoip, geoip_msg) + def test_locations(self): + self.assertEqual('United States', location('55.66.77.88')) + self.assertEqual('United States', location('8.8.4.4')) + + class PlatformTemplateFilterTest(TestCase): def test_windows(self): # Generic Windows diff --git a/tests/test_views.py b/tests/test_views.py index fce2921..53435f5 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -20,7 +20,8 @@ def test_list(self): self.user.session_set.create(session_key='ABC123', ip='127.0.0.1', expire_date=now() + timedelta(days=1), user_agent='Firefox') - response = self.client.get(reverse('user_sessions:session_list')) + with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): + response = self.client.get(reverse('user_sessions:session_list')) self.assertContains(response, 'Active Sessions') self.assertContains(response, 'Firefox') self.assertNotContains(response, 'ABC123') @@ -35,7 +36,8 @@ def test_delete_all_other(self): self.user.session_set.create(ip='127.0.0.1', expire_date=now() + timedelta(days=1)) self.assertEqual(self.user.session_set.count(), 2) response = self.client.post(reverse('user_sessions:session_delete_other')) - self.assertRedirects(response, reverse('user_sessions:session_list')) + with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): + self.assertRedirects(response, reverse('user_sessions:session_list')) self.assertEqual(self.user.session_set.count(), 1) def test_delete_some_other(self): @@ -44,5 +46,6 @@ def test_delete_some_other(self): self.assertEqual(self.user.session_set.count(), 2) response = self.client.post(reverse('user_sessions:session_delete', args=[other.session_key])) - self.assertRedirects(response, reverse('user_sessions:session_list')) + with self.assertWarnsRegex(UserWarning, r"The address 127\.0\.0\.1 is not in the database"): + self.assertRedirects(response, reverse('user_sessions:session_list')) self.assertEqual(self.user.session_set.count(), 1)