Add tests for kfet views.
kfet.tests.testcases embed mixins for TestCase: - TestCaseMixin provides assertion helpers, - ViewTestCaseMixin provides a few basic tests, which are common to every view. kfet.tests.utils provides helpers for users and permissions management. Each kfet view get a testcase (at least very basic) in kfet.tests.test_views.
This commit is contained in:
parent
343b52f986
commit
2cfce1c921
95
kfet/tests/test_tests_utils.py
Normal file
95
kfet/tests/test_tests_utils.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from gestioncof.models import CofProfile
|
||||||
|
|
||||||
|
from ..models import Account
|
||||||
|
from .testcases import TestCaseMixin
|
||||||
|
from .utils import (
|
||||||
|
create_user, create_team, create_root, get_perms, user_add_perms,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class UserHelpersTests(TestCaseMixin, TestCase):
|
||||||
|
|
||||||
|
def test_create_user(self):
|
||||||
|
"""create_user creates a basic user and its account."""
|
||||||
|
u = create_user()
|
||||||
|
a = u.profile.account_kfet
|
||||||
|
|
||||||
|
self.assertInstanceExpected(u, {
|
||||||
|
'get_full_name': 'first last',
|
||||||
|
'username': 'user',
|
||||||
|
})
|
||||||
|
self.assertFalse(u.user_permissions.exists())
|
||||||
|
|
||||||
|
self.assertEqual('000', a.trigramme)
|
||||||
|
|
||||||
|
def test_create_team(self):
|
||||||
|
u = create_team()
|
||||||
|
a = u.profile.account_kfet
|
||||||
|
|
||||||
|
self.assertInstanceExpected(u, {
|
||||||
|
'get_full_name': 'team member',
|
||||||
|
'username': 'team',
|
||||||
|
})
|
||||||
|
self.assertTrue(u.has_perm('kfet.is_team'))
|
||||||
|
|
||||||
|
self.assertEqual('100', a.trigramme)
|
||||||
|
|
||||||
|
def test_create_root(self):
|
||||||
|
u = create_root()
|
||||||
|
a = u.profile.account_kfet
|
||||||
|
|
||||||
|
self.assertInstanceExpected(u, {
|
||||||
|
'get_full_name': 'super user',
|
||||||
|
'username': 'root',
|
||||||
|
'is_superuser': True,
|
||||||
|
'is_staff': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual('200', a.trigramme)
|
||||||
|
|
||||||
|
|
||||||
|
class PermHelpersTest(TestCaseMixin, TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
cts = ContentType.objects.get_for_models(Account, CofProfile)
|
||||||
|
self.perm1 = Permission.objects.create(
|
||||||
|
content_type=cts[Account],
|
||||||
|
codename='test_perm',
|
||||||
|
name='Perm for test',
|
||||||
|
)
|
||||||
|
self.perm2 = Permission.objects.create(
|
||||||
|
content_type=cts[CofProfile],
|
||||||
|
codename='another_test_perm',
|
||||||
|
name='Another one',
|
||||||
|
)
|
||||||
|
self.perm_team = Permission.objects.get(
|
||||||
|
content_type__app_label='kfet',
|
||||||
|
codename='is_team',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_perms(self):
|
||||||
|
perms = get_perms('kfet.test_perm', 'gestioncof.another_test_perm')
|
||||||
|
self.assertDictEqual(perms, {
|
||||||
|
'kfet.test_perm': self.perm1,
|
||||||
|
'gestioncof.another_test_perm': self.perm2,
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_user_add_perms(self):
|
||||||
|
user = User.objects.create_user(username='user', password='user')
|
||||||
|
user.user_permissions.add(self.perm1)
|
||||||
|
|
||||||
|
user_add_perms(user, ['kfet.is_team', 'gestioncof.another_test_perm'])
|
||||||
|
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
user.user_permissions.all(),
|
||||||
|
map(repr, [self.perm1, self.perm2, self.perm_team]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
File diff suppressed because it is too large
Load diff
|
@ -1,16 +1,114 @@
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import QueryDict
|
from django.http import QueryDict
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from .utils import create_root, create_team, create_user
|
from .utils import create_root, create_team, create_user
|
||||||
|
|
||||||
|
|
||||||
class ViewTestCaseMixin:
|
class TestCaseMixin:
|
||||||
|
def assertForbidden(self, response):
|
||||||
|
request = response.wsgi_request
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
# Is this an HTTP Forbidden response ?
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
except AssertionError:
|
||||||
|
# A redirection to the login view is fine too.
|
||||||
|
|
||||||
|
# Let's build the login url with the 'next' param on current
|
||||||
|
# page.
|
||||||
|
full_path = request.get_full_path()
|
||||||
|
|
||||||
|
querystring = QueryDict(mutable=True)
|
||||||
|
querystring['next'] = full_path
|
||||||
|
|
||||||
|
login_url = '/login?' + querystring.urlencode(safe='/')
|
||||||
|
|
||||||
|
# We don't focus on what the login view does.
|
||||||
|
# So don't fetch the redirect.
|
||||||
|
self.assertRedirects(
|
||||||
|
response, login_url,
|
||||||
|
fetch_redirect_response=False,
|
||||||
|
)
|
||||||
|
except AssertionError:
|
||||||
|
raise AssertionError(
|
||||||
|
"%(http_method)s request at %(path)s should be forbidden for "
|
||||||
|
"%(username)s user.\n"
|
||||||
|
"Response isn't 403, nor a redirect to login view. Instead, "
|
||||||
|
"response code is %(code)d." % {
|
||||||
|
'http_method': request.method,
|
||||||
|
'path': request.get_full_path(),
|
||||||
|
'username': (
|
||||||
|
"'{}'".format(request.user)
|
||||||
|
if request.user.is_authenticated()
|
||||||
|
else 'anonymous'
|
||||||
|
),
|
||||||
|
'code': response.status_code,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def assertForbiddenKfet(self, response, form_ctx='form'):
|
||||||
|
try:
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
try:
|
||||||
|
form = response.context[form_ctx]
|
||||||
|
self.assertIn("Permission refusée", form.non_field_errors())
|
||||||
|
except (AssertionError, AttributeError, KeyError):
|
||||||
|
messages = [str(msg) for msg in response.context['messages']]
|
||||||
|
self.assertIn("Permission refusée", messages)
|
||||||
|
except AssertionError:
|
||||||
|
request = response.wsgi_request
|
||||||
|
raise AssertionError(
|
||||||
|
"%(http_method)s request at %(path)s should raise an error "
|
||||||
|
"for %(username)s user.\n"
|
||||||
|
"Cannot find any errors in non-field errors of form "
|
||||||
|
"'%(form_ctx)s', nor in messages." % {
|
||||||
|
'http_method': request.method,
|
||||||
|
'path': request.get_full_path(),
|
||||||
|
'username': (
|
||||||
|
"'%s'" % request.user
|
||||||
|
if request.user.is_authenticated()
|
||||||
|
else 'anonymous'
|
||||||
|
),
|
||||||
|
'form_ctx': form_ctx,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def assertInstanceExpected(self, instance, expected):
|
||||||
|
for attr, expected_value in expected.items():
|
||||||
|
value = getattr(instance, attr)
|
||||||
|
if callable(value):
|
||||||
|
value = value()
|
||||||
|
self.assertEqual(value, expected_value)
|
||||||
|
|
||||||
|
def assertUrlsEqual(self, actual, expected):
|
||||||
|
if type(expected) == dict:
|
||||||
|
parsed = urlparse(actual)
|
||||||
|
checks = ['scheme', 'netloc', 'path', 'params']
|
||||||
|
for check in checks:
|
||||||
|
self.assertEqual(
|
||||||
|
getattr(parsed, check),
|
||||||
|
expected.get(check, ''),
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
parse_qs(parsed.query),
|
||||||
|
expected.get('query', {}),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class ViewTestCaseMixin(TestCaseMixin):
|
||||||
url_name = None
|
url_name = None
|
||||||
url_expected = None
|
url_expected = None
|
||||||
|
|
||||||
|
http_methods = ['GET']
|
||||||
|
|
||||||
auth_user = None
|
auth_user = None
|
||||||
auth_forbidden = []
|
auth_forbidden = []
|
||||||
|
|
||||||
|
@ -22,6 +120,11 @@ class ViewTestCaseMixin:
|
||||||
patcher_messages.start()
|
patcher_messages.start()
|
||||||
self.addCleanup(patcher_messages.stop)
|
self.addCleanup(patcher_messages.stop)
|
||||||
|
|
||||||
|
# A test can mock 'django.utils.timezone.now' and give this as return
|
||||||
|
# value. E.g. it is useful if the test checks values of 'auto_now' or
|
||||||
|
# 'auto_now_add' fields.
|
||||||
|
self.now = timezone.now()
|
||||||
|
|
||||||
self.users = {}
|
self.users = {}
|
||||||
self.accounts = {}
|
self.accounts = {}
|
||||||
|
|
||||||
|
@ -58,6 +161,11 @@ class ViewTestCaseMixin:
|
||||||
if hasattr(user.profile, 'account_kfet'):
|
if hasattr(user.profile, 'account_kfet'):
|
||||||
self.accounts[label] = user.profile.account_kfet
|
self.accounts[label] = user.profile.account_kfet
|
||||||
|
|
||||||
|
def get_user(self, label):
|
||||||
|
if self.auth_user is not None:
|
||||||
|
return self.auth_user
|
||||||
|
return self.auth_user_mapping.get(label)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def urls_conf(self):
|
def urls_conf(self):
|
||||||
return [{
|
return [{
|
||||||
|
@ -81,62 +189,24 @@ class ViewTestCaseMixin:
|
||||||
def url(self):
|
def url(self):
|
||||||
return self.t_urls[0]
|
return self.t_urls[0]
|
||||||
|
|
||||||
def assertForbidden(self, response):
|
|
||||||
request = response.wsgi_request
|
|
||||||
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
# Is this an HTTP Forbidden response ?
|
|
||||||
self.assertEqual(response.status_code, 403)
|
|
||||||
except AssertionError:
|
|
||||||
# A redirection to the login view is fine too.
|
|
||||||
|
|
||||||
# Let's build the login url with the 'next' param on current
|
|
||||||
# page.
|
|
||||||
full_path = request.get_full_path()
|
|
||||||
|
|
||||||
querystring = QueryDict(mutable=True)
|
|
||||||
querystring['next'] = full_path
|
|
||||||
|
|
||||||
login_url = '/login?' + querystring.urlencode(safe='/')
|
|
||||||
|
|
||||||
# We don't focus on what the login view does.
|
|
||||||
# So don't fetch the redirect.
|
|
||||||
self.assertRedirects(
|
|
||||||
response, login_url,
|
|
||||||
fetch_redirect_response=False,
|
|
||||||
)
|
|
||||||
except AssertionError:
|
|
||||||
raise AssertionError(
|
|
||||||
"%(http_method)s request at %(path)s should be forbidden for "
|
|
||||||
"%(username)s user.\n"
|
|
||||||
"Response isn't 403, nor a redirect to login view. Instead, "
|
|
||||||
"response code is %(code)d." % {
|
|
||||||
'http_method': request.method,
|
|
||||||
'path': request.get_full_path(),
|
|
||||||
'username': (
|
|
||||||
"'{}'".format(request.user.username)
|
|
||||||
if request.user.username
|
|
||||||
else 'anonymous'
|
|
||||||
),
|
|
||||||
'code': response.status_code,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def assertForbiddenKfet(self, response):
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
form = response.context['form']
|
|
||||||
self.assertIn("Permission refusée", form.non_field_errors)
|
|
||||||
|
|
||||||
def test_urls(self):
|
def test_urls(self):
|
||||||
for url, conf in zip(self.t_urls, self.urls_conf):
|
for url, conf in zip(self.t_urls, self.urls_conf):
|
||||||
self.assertEqual(url, conf['expected'])
|
self.assertEqual(url, conf['expected'])
|
||||||
|
|
||||||
def test_forbidden(self):
|
def test_forbidden(self):
|
||||||
for creds in self.auth_forbidden:
|
for method in self.http_methods:
|
||||||
|
for user in self.auth_forbidden:
|
||||||
for url in self.t_urls:
|
for url in self.t_urls:
|
||||||
|
self.check_forbidden(method, url, user)
|
||||||
|
|
||||||
|
def check_forbidden(self, method, url, user=None):
|
||||||
|
method = method.lower()
|
||||||
client = Client()
|
client = Client()
|
||||||
if creds is not None:
|
if user is not None:
|
||||||
client.login(username=creds, password=creds)
|
client.login(username=user, password=user)
|
||||||
r = client.get(url)
|
|
||||||
|
send_request = getattr(client, method)
|
||||||
|
data = getattr(self, '{}_data'.format(method), {})
|
||||||
|
|
||||||
|
r = send_request(url, data)
|
||||||
self.assertForbidden(r)
|
self.assertForbidden(r)
|
||||||
|
|
|
@ -7,44 +7,14 @@ from ..models import Account
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
def user_add_perms(user, perms_labels):
|
|
||||||
"""
|
|
||||||
Add perms to a user.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user (User instance)
|
|
||||||
perms (list of str 'app.perm_name')
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The same user (refetched from DB to avoid missing perms)
|
|
||||||
|
|
||||||
"""
|
|
||||||
u_labels = set(perms_labels)
|
|
||||||
|
|
||||||
perms = []
|
|
||||||
for label in u_labels:
|
|
||||||
app_label, codename = label.split('.', 1)
|
|
||||||
perms.append(
|
|
||||||
Permission.objects.get(
|
|
||||||
content_type__app_label=app_label,
|
|
||||||
codename=codename,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
user.user_permissions.add(*perms)
|
|
||||||
|
|
||||||
# If permissions have already been fetched for this user, we need to reload
|
|
||||||
# it to avoid using of the previous permissions cache.
|
|
||||||
# https://docs.djangoproject.com/en/1.11/topics/auth/default/#permission-caching
|
|
||||||
return User.objects.get(pk=user.pk)
|
|
||||||
|
|
||||||
|
|
||||||
def _create_user_and_account(user_attrs, account_attrs, perms=None):
|
def _create_user_and_account(user_attrs, account_attrs, perms=None):
|
||||||
user_attrs.setdefault('password', user_attrs['username'])
|
user_pwd = user_attrs.pop('password', user_attrs['username'])
|
||||||
user = User.objects.create_user(**user_attrs)
|
user = User.objects.create(**user_attrs)
|
||||||
|
user.set_password(user_pwd)
|
||||||
|
user.save()
|
||||||
|
|
||||||
account_attrs['cofprofile'] = user.profile
|
account_attrs['cofprofile'] = user.profile
|
||||||
kfet_pwd = account_attrs.pop('password', None)
|
kfet_pwd = account_attrs.pop('password', 'kfetpwd_{}'.format(user_pwd))
|
||||||
|
|
||||||
account = Account.objects.create(**account_attrs)
|
account = Account.objects.create(**account_attrs)
|
||||||
|
|
||||||
|
@ -52,8 +22,6 @@ def _create_user_and_account(user_attrs, account_attrs, perms=None):
|
||||||
user = user_add_perms(user, perms)
|
user = user_add_perms(user, perms)
|
||||||
|
|
||||||
if 'kfet.is_team' in perms:
|
if 'kfet.is_team' in perms:
|
||||||
if kfet_pwd is None:
|
|
||||||
kfet_pwd = 'kfetpwd_{}'.format(user_attrs['password'])
|
|
||||||
account.change_pwd(kfet_pwd)
|
account.change_pwd(kfet_pwd)
|
||||||
account.save()
|
account.save()
|
||||||
|
|
||||||
|
@ -98,8 +66,41 @@ def create_root(username='root', trigramme='200', **kwargs):
|
||||||
user_attrs.setdefault('first_name', 'super')
|
user_attrs.setdefault('first_name', 'super')
|
||||||
user_attrs.setdefault('last_name', 'user')
|
user_attrs.setdefault('last_name', 'user')
|
||||||
user_attrs.setdefault('email', 'mail@root.net')
|
user_attrs.setdefault('email', 'mail@root.net')
|
||||||
|
user_attrs['is_superuser'] = user_attrs['is_staff'] = True
|
||||||
|
|
||||||
account_attrs = kwargs.setdefault('account_attrs', {})
|
account_attrs = kwargs.setdefault('account_attrs', {})
|
||||||
account_attrs.setdefault('trigramme', trigramme)
|
account_attrs.setdefault('trigramme', trigramme)
|
||||||
|
|
||||||
return _create_user_and_account(**kwargs)
|
return _create_user_and_account(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_perms(*labels):
|
||||||
|
perms = {}
|
||||||
|
for label in set(labels):
|
||||||
|
app_label, codename = label.split('.', 1)
|
||||||
|
perms[label] = Permission.objects.get(
|
||||||
|
content_type__app_label=app_label,
|
||||||
|
codename=codename,
|
||||||
|
)
|
||||||
|
return perms
|
||||||
|
|
||||||
|
|
||||||
|
def user_add_perms(user, perms_labels):
|
||||||
|
"""
|
||||||
|
Add perms to a user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User instance)
|
||||||
|
perms (list of str 'app.perm_name')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The same user (refetched from DB to avoid missing perms)
|
||||||
|
|
||||||
|
"""
|
||||||
|
perms = get_perms(*perms_labels)
|
||||||
|
user.user_permissions.add(*perms.values())
|
||||||
|
|
||||||
|
# If permissions have already been fetched for this user, we need to reload
|
||||||
|
# it to avoid using of the previous permissions cache.
|
||||||
|
# https://docs.djangoproject.com/en/1.11/topics/auth/default/#permission-caching
|
||||||
|
return User.objects.get(pk=user.pk)
|
||||||
|
|
Loading…
Reference in a new issue