2017-08-10 15:02:08 +02:00
|
|
|
from unittest import mock
|
2017-08-16 17:45:59 +02:00
|
|
|
from urllib.parse import parse_qs, urlparse
|
2017-08-10 15:02:08 +02:00
|
|
|
|
|
|
|
from django.core.urlresolvers import reverse
|
|
|
|
from django.http import QueryDict
|
|
|
|
from django.test import Client
|
2017-08-16 17:45:59 +02:00
|
|
|
from django.utils import timezone
|
2017-08-10 15:02:08 +02:00
|
|
|
|
|
|
|
from .utils import create_root, create_team, create_user
|
|
|
|
|
|
|
|
|
2017-08-16 17:45:59 +02:00
|
|
|
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):
|
2017-08-10 15:02:08 +02:00
|
|
|
url_name = None
|
|
|
|
url_expected = None
|
|
|
|
|
2017-08-16 17:45:59 +02:00
|
|
|
http_methods = ['GET']
|
|
|
|
|
2017-08-10 15:02:08 +02:00
|
|
|
auth_user = None
|
|
|
|
auth_forbidden = []
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
# Signals handlers on login/logout send messages.
|
|
|
|
# Due to the way the Django' test Client performs login, this raise an
|
|
|
|
# error. As workaround, we mock the Django' messages module.
|
|
|
|
patcher_messages = mock.patch('gestioncof.signals.messages')
|
|
|
|
patcher_messages.start()
|
|
|
|
self.addCleanup(patcher_messages.stop)
|
|
|
|
|
2017-08-16 17:45:59 +02:00
|
|
|
# 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()
|
|
|
|
|
2017-08-10 15:02:08 +02:00
|
|
|
self.users = {}
|
|
|
|
self.accounts = {}
|
|
|
|
|
|
|
|
for label, user in {**self.users_base, **self.users_extra}.items():
|
|
|
|
self.register_user(label, user)
|
|
|
|
|
|
|
|
if self.auth_user:
|
|
|
|
# The wrapper is a sanity check.
|
|
|
|
self.assertTrue(
|
|
|
|
self.client.login(
|
|
|
|
username=self.auth_user,
|
|
|
|
password=self.auth_user,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def users_base(self):
|
|
|
|
# Format desc: username, password, trigramme
|
|
|
|
return {
|
|
|
|
# user, user, 000
|
|
|
|
'user': create_user(),
|
|
|
|
# team, team, 100
|
|
|
|
'team': create_team(),
|
|
|
|
# root, root, 200
|
|
|
|
'root': create_root(),
|
|
|
|
}
|
|
|
|
|
|
|
|
@property
|
|
|
|
def users_extra(self):
|
|
|
|
return {}
|
|
|
|
|
|
|
|
def register_user(self, label, user):
|
|
|
|
self.users[label] = user
|
|
|
|
if hasattr(user.profile, 'account_kfet'):
|
|
|
|
self.accounts[label] = user.profile.account_kfet
|
|
|
|
|
2017-08-16 17:45:59 +02:00
|
|
|
def get_user(self, label):
|
|
|
|
if self.auth_user is not None:
|
|
|
|
return self.auth_user
|
|
|
|
return self.auth_user_mapping.get(label)
|
|
|
|
|
2017-08-10 15:02:08 +02:00
|
|
|
@property
|
|
|
|
def urls_conf(self):
|
|
|
|
return [{
|
|
|
|
'name': self.url_name,
|
|
|
|
'args': getattr(self, 'url_args', []),
|
|
|
|
'kwargs': getattr(self, 'url_kwargs', {}),
|
|
|
|
'expected': self.url_expected,
|
|
|
|
}]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def t_urls(self):
|
|
|
|
return [
|
|
|
|
reverse(
|
|
|
|
url_conf['name'],
|
|
|
|
args=url_conf.get('args', []),
|
|
|
|
kwargs=url_conf.get('kwargs', {}),
|
|
|
|
)
|
|
|
|
for url_conf in self.urls_conf]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def url(self):
|
|
|
|
return self.t_urls[0]
|
|
|
|
|
|
|
|
def test_urls(self):
|
|
|
|
for url, conf in zip(self.t_urls, self.urls_conf):
|
|
|
|
self.assertEqual(url, conf['expected'])
|
|
|
|
|
|
|
|
def test_forbidden(self):
|
2017-08-16 17:45:59 +02:00
|
|
|
for method in self.http_methods:
|
|
|
|
for user in self.auth_forbidden:
|
|
|
|
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()
|
|
|
|
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)
|