Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes and improvements #166

Merged
merged 15 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,31 @@ jobs:
max-parallel: 5
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12-dev']
django-version: ['3.2', '4.0', 'main']
django-version: ['3.2', '4.0', '4.1', '4.2', 'main']
include:
- python-version: '3.7'
django-version: '3.2'
exclude:
- 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'

- python-version: '3.8'
django-version: 'main'
- python-version: '3.9'
django-version: 'main'
- python-version: '3.10'
django-version: 'main'

blag marked this conversation as resolved.
Show resolved Hide resolved
steps:
- uses: actions/checkout@v3

Expand All @@ -31,6 +52,7 @@ jobs:
python -m pip install --upgrade tox tox-gh-actions

- name: Tox tests
continue-on-error: ${{ endsWith(matrix.python-version, '-dev') || matrix.django-version == 'main' }}
run: |
tox -v
env:
Expand Down
12 changes: 7 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/example/database.sqlite3
/example/GeoLiteCity.dat
/django_user_sessions.egg-info/
/tests/test_city.mmdb
/tests/test_country.mmdb

/htmlcov/

Expand All @@ -10,8 +12,8 @@ coverage.xml

/docs/_build/
/GeoLite2-City.mmdb
__pycache__
/venv/
/.eggs/
/build/

__pycache__
/venv/
/.eggs/
/build/
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ check:
DJANGO_SETTINGS_MODULE=example.settings PYTHONPATH=. \
python -Wd example/manage.py check

test:
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

test: generate-mmdb-fixtures
DJANGO_SETTINGS_MODULE=tests.settings PYTHONPATH=. \
django-admin.py test ${TARGET}

Expand Down
4 changes: 0 additions & 4 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,3 @@ Views
-----
.. autoclass:: user_sessions.views.SessionListView
.. autoclass:: user_sessions.views.SessionDeleteView

Unit tests
----------
.. autoclass:: user_sessions.utils.tests.Client
9 changes: 9 additions & 0 deletions tests/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM perl:latest

RUN cpanm MaxMind::DB::Writer

COPY generate_mmdb.pl /

VOLUME ["/data"]

CMD ["perl", "/generate_mmdb.pl"]
89 changes: 89 additions & 0 deletions tests/generate_mmdb.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!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);
14 changes: 9 additions & 5 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
from pathlib import Path

BASE_DIR = os.path.dirname(__file__)
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'

Expand Down Expand Up @@ -38,7 +39,7 @@
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
BASE_DIR / 'templates',
],
'APP_DIRS': True,
'OPTIONS': {
Expand All @@ -55,10 +56,13 @@
},
]

GEOIP_PATH = os.path.join(os.path.dirname(BASE_DIR), 'GeoLite2-City.mmdb')
GEOIP_PATH = BASE_DIR / 'tests'
GEOIP_CITY = 'test_city.mmdb'
GEOIP_COUNTRY = 'test_country.mmdb'

SESSION_ENGINE = 'user_sessions.backends.db'

LOGIN_URL = '/admin/'
LOGOUT_REDIRECT_URL = '/'

SILENCED_SYSTEM_CHECKS = ['admin.E406', 'admin.E409', 'admin.E410']
SILENCED_SYSTEM_CHECKS = ['admin.E406', 'admin.E409', 'admin.E410', 'admin.W411']
64 changes: 44 additions & 20 deletions tests/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta
from datetime import timedelta
from unittest import skipUnless
from unittest.mock import patch
from urllib.parse import urlencode
Expand All @@ -15,14 +15,17 @@

from user_sessions.backends.db import SessionStore
from user_sessions.models import Session
from user_sessions.templatetags.user_sessions import device, location
from user_sessions.utils.tests import Client
from user_sessions.templatetags.user_sessions import (
city, country, device, location,
)

from .utils import Client

try:
from django.contrib.gis.geoip2 import GeoIP2
geoip = GeoIP2()
geoip_msg = None
except Exception as error_geoip2:
except Exception as error_geoip2: # pragma: no cover
try:
from django.contrib.gis.geoip import GeoIP
geoip = GeoIP()
Expand Down Expand Up @@ -80,9 +83,10 @@ def setUp(self):

def test_list(self):
self.user.session_set.create(session_key='ABC123', ip='127.0.0.1',
expire_date=datetime.now() + timedelta(days=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')
Expand All @@ -94,19 +98,21 @@ def test_delete(self):
self.assertRedirects(response, '/')

def test_delete_all_other(self):
self.user.session_set.create(ip='127.0.0.1', expire_date=datetime.now() + timedelta(days=1))
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):
other = self.user.session_set.create(session_key='OTHER', ip='127.0.0.1',
expire_date=datetime.now() + timedelta(days=1))
expire_date=now() + timedelta(days=1))
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)


Expand All @@ -126,33 +132,38 @@ 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 = '{}?{}'.format(self.admin_url, urlencode({'owner': 'my'}))
response = self.client.get(my_sessions)
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)
self.assertContains(response, '127.0.0.1')
self.assertNotContains(response, '1.1.1.1')

def test_expired(self):
expired = '{}?{}'.format(self.admin_url, urlencode({'active': '0'}))
response = self.client.get(expired)
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)
self.assertContains(response, '20.13.1.1')
self.assertNotContains(response, '1.1.1.1')

def test_unexpired(self):
unexpired = '{}?{}'.format(self.admin_url, urlencode({'active': '1'}))
response = self.client.get(unexpired)
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)
self.assertContains(response, '1.1.1.1')
self.assertNotContains(response, '20.13.1.1')

Expand Down Expand Up @@ -314,7 +325,20 @@ def test_no_session(self):
class LocationTemplateFilterTest(TestCase):
@override_settings(GEOIP_PATH=None)
def test_no_location(self):
self.assertEqual(location('127.0.0.1'), None)
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)

@skipUnless(geoip, geoip_msg)
def test_city(self):
self.assertEqual('San Diego', city('44.55.66.77'))

@skipUnless(geoip, geoip_msg)
def test_country(self):
self.assertEqual('United States', country('8.8.8.8'))

@skipUnless(geoip, geoip_msg)
def test_locations(self):
Expand Down Expand Up @@ -479,7 +503,7 @@ def test_windows_only(self):

class ClearsessionsCommandTest(TestCase):
def test_can_call(self):
Session.objects.create(expire_date=datetime.now() - timedelta(days=1),
Session.objects.create(expire_date=now() - timedelta(days=1),
ip='127.0.0.1')
call_command('clearsessions')
self.assertEqual(Session.objects.count(), 0)
Expand Down
2 changes: 1 addition & 1 deletion user_sessions/utils/tests.py → tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.http import HttpRequest
from django.test import Client as BaseClient

from ..backends.db import SessionStore
from user_sessions.backends.db import SessionStore


class Client(BaseClient):
Expand Down
Loading
Loading