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:
Aurélien Delobelle 2017-08-16 17:45:59 +02:00
parent 343b52f986
commit 2cfce1c921
4 changed files with 2265 additions and 206 deletions

View 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

View file

@ -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 url in self.t_urls: for user in self.auth_forbidden:
client = Client() for url in self.t_urls:
if creds is not None: self.check_forbidden(method, url, user)
client.login(username=creds, password=creds)
r = client.get(url) def check_forbidden(self, method, url, user=None):
self.assertForbidden(r) method = method.lower()
client = Client()
if user is not None:
client.login(username=user, password=user)
send_request = getattr(client, method)
data = getattr(self, '{}_data'.format(method), {})
r = send_request(url, data)
self.assertForbidden(r)

View file

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