forked from DGNum/gestioCOF
Merge branch 'aureplop/cof-tests_survey' into 'master'
cof -- Add tests for survey views See merge request cof-geek/gestioCOF!285
This commit is contained in:
commit
8f0eec0e88
9 changed files with 551 additions and 3 deletions
|
@ -8,7 +8,7 @@
|
||||||
{% if survey.details %}
|
{% if survey.details %}
|
||||||
<p>{{ survey.details }}</p>
|
<p>{{ survey.details }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form class="form-horizontal" method="post" action="{% url 'gestioncof.views.survey' survey.id %}">
|
<form class="form-horizontal" method="post" action="{% url 'survey.details' survey.id %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form | bootstrap}}
|
{{ form | bootstrap}}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h3>Filtres</h3>
|
<h3>Filtres</h3>
|
||||||
{% include "tristate_js.html" %}
|
{% include "tristate_js.html" %}
|
||||||
<form method="post" action="{% url 'gestioncof.views.survey_status' survey.id %}">
|
<form method="post" action="{% url 'survey.details.status' survey.id %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
<input style="margin-top:10px;" type="submit" class="btn btn-primary" value="Filtrer" />
|
<input style="margin-top:10px;" type="submit" class="btn btn-primary" value="Filtrer" />
|
||||||
|
|
0
gestioncof/tests/__init__.py
Normal file
0
gestioncof/tests/__init__.py
Normal file
178
gestioncof/tests/test_views.py
Normal file
178
gestioncof/tests/test_views.py
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.messages import get_messages
|
||||||
|
from django.contrib.messages.storage.base import Message
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from gestioncof.models import Survey, SurveyAnswer
|
||||||
|
from gestioncof.tests.testcases import ViewTestCaseMixin
|
||||||
|
|
||||||
|
|
||||||
|
class SurveyViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
url_name = 'survey.details'
|
||||||
|
|
||||||
|
http_methods = ['GET', 'POST']
|
||||||
|
|
||||||
|
auth_user = 'user'
|
||||||
|
auth_forbidden = [None]
|
||||||
|
|
||||||
|
post_expected_message = Message(messages.SUCCESS, (
|
||||||
|
"Votre réponse a bien été enregistrée ! Vous pouvez cependant la "
|
||||||
|
"modifier jusqu'à la fin du sondage."
|
||||||
|
))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_kwargs(self):
|
||||||
|
return {'survey_id': self.s.pk}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_expected(self):
|
||||||
|
return '/survey/{}'.format(self.s.pk)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.s = Survey.objects.create(title='Title')
|
||||||
|
|
||||||
|
self.q1 = self.s.questions.create(question='Question 1 ?')
|
||||||
|
self.q2 = self.s.questions.create(
|
||||||
|
question='Question 2 ?',
|
||||||
|
multi_answers=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.qa1 = self.q1.answers.create(answer='Q1 - Answer 1')
|
||||||
|
self.qa2 = self.q1.answers.create(answer='Q1 - Answer 2')
|
||||||
|
self.qa3 = self.q2.answers.create(answer='Q2 - Answer 1')
|
||||||
|
self.qa4 = self.q2.answers.create(answer='Q2 - Answer 2')
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
def test_post_new(self):
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
'question_{}'.format(self.q1.pk): [str(self.qa1.pk)],
|
||||||
|
'question_{}'.format(self.q2.pk): [
|
||||||
|
str(self.qa3.pk), str(self.qa4.pk),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
a = self.s.surveyanswer_set.get(user=self.users['user'])
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
a.answers.all(), map(repr, [self.qa1, self.qa3, self.qa4]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_post_edit(self):
|
||||||
|
a = self.s.surveyanswer_set.create(user=self.users['user'])
|
||||||
|
a.answers.add(self.qa1, self.qa1, self.qa4)
|
||||||
|
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
'question_{}'.format(self.q1.pk): [],
|
||||||
|
'question_{}'.format(self.q2.pk): [str(self.qa3.pk)],
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertIn(self.post_expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
a.refresh_from_db()
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
a.answers.all(), map(repr, [self.qa3]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_post_delete(self):
|
||||||
|
a = self.s.surveyanswer_set.create(user=self.users['user'])
|
||||||
|
a.answers.add(self.qa1, self.qa4)
|
||||||
|
|
||||||
|
r = self.client.post(self.url, {'delete': '1'})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
expected_message = Message(
|
||||||
|
messages.SUCCESS, "Votre réponse a bien été supprimée")
|
||||||
|
self.assertIn(expected_message, get_messages(r.wsgi_request))
|
||||||
|
|
||||||
|
with self.assertRaises(SurveyAnswer.DoesNotExist):
|
||||||
|
a.refresh_from_db()
|
||||||
|
|
||||||
|
def test_forbidden_closed(self):
|
||||||
|
self.s.survey_open = False
|
||||||
|
self.s.save()
|
||||||
|
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
|
||||||
|
self.assertNotEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
def test_forbidden_old(self):
|
||||||
|
self.s.old = True
|
||||||
|
self.s.save()
|
||||||
|
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
|
||||||
|
self.assertNotEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
class SurveyStatusViewTests(ViewTestCaseMixin, TestCase):
|
||||||
|
url_name = 'survey.details.status'
|
||||||
|
|
||||||
|
http_methods = ['GET', 'POST']
|
||||||
|
|
||||||
|
auth_user = 'staff'
|
||||||
|
auth_forbidden = [None, 'user', 'member']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_kwargs(self):
|
||||||
|
return {'survey_id': self.s.pk}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url_expected(self):
|
||||||
|
return '/survey/{}/status'.format(self.s.pk)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.s = Survey.objects.create(title='Title')
|
||||||
|
|
||||||
|
self.q1 = self.s.questions.create(question='Question 1 ?')
|
||||||
|
self.q2 = self.s.questions.create(
|
||||||
|
question='Question 2 ?',
|
||||||
|
multi_answers=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.qa1 = self.q1.answers.create(answer='Q1 - Answer 1')
|
||||||
|
self.qa2 = self.q1.answers.create(answer='Q1 - Answer 2')
|
||||||
|
self.qa3 = self.q2.answers.create(answer='Q2 - Answer 1')
|
||||||
|
self.qa4 = self.q2.answers.create(answer='Q2 - Answer 2')
|
||||||
|
|
||||||
|
self.a1 = self.s.surveyanswer_set.create(user=self.users['user'])
|
||||||
|
self.a1.answers.add(self.qa1)
|
||||||
|
self.a2 = self.s.surveyanswer_set.create(user=self.users['member'])
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
r = self.client.get(self.url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
def _get_qa_filter_name(self, qa):
|
||||||
|
return 'question_{}_answer_{}'.format(qa.survey_question.pk, qa.pk)
|
||||||
|
|
||||||
|
def _test_filters(self, filters, expected):
|
||||||
|
r = self.client.post(self.url, {
|
||||||
|
self._get_qa_filter_name(qa): v for qa, v in filters
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
r.context['user_answers'], map(repr, expected),
|
||||||
|
ordered=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_filter_none(self):
|
||||||
|
self._test_filters([(self.qa1, 'none')], [self.a1, self.a2])
|
||||||
|
|
||||||
|
def test_filter_yes(self):
|
||||||
|
self._test_filters([(self.qa1, 'yes')], [self.a1])
|
||||||
|
|
||||||
|
def test_filter_no(self):
|
||||||
|
self._test_filters([(self.qa1, 'no')], [self.a2])
|
24
gestioncof/tests/testcases.py
Normal file
24
gestioncof/tests/testcases.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
from shared.tests.testcases import ViewTestCaseMixin as BaseViewTestCaseMixin
|
||||||
|
|
||||||
|
from .utils import create_user, create_member, create_staff
|
||||||
|
|
||||||
|
|
||||||
|
class ViewTestCaseMixin(BaseViewTestCaseMixin):
|
||||||
|
"""
|
||||||
|
TestCase extension to ease testing of cof views.
|
||||||
|
|
||||||
|
Most information can be found in the base parent class doc.
|
||||||
|
This class performs some changes to users management, detailed below.
|
||||||
|
|
||||||
|
During setup, three users are created:
|
||||||
|
- 'user': a basic user without any permission,
|
||||||
|
- 'member': (profile.is_cof is True),
|
||||||
|
- 'staff': (profile.is_cof is True) && (profile.is_buro is True).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_users_base(self):
|
||||||
|
return {
|
||||||
|
'user': create_user('user'),
|
||||||
|
'member': create_member('member'),
|
||||||
|
'staff': create_staff('staff'),
|
||||||
|
}
|
51
gestioncof/tests/utils.py
Normal file
51
gestioncof/tests/utils.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
def _create_user(username, is_cof=False, is_staff=False, attrs=None):
|
||||||
|
if attrs is None:
|
||||||
|
attrs = {}
|
||||||
|
|
||||||
|
password = attrs.pop('password', username)
|
||||||
|
|
||||||
|
user_keys = ['first_name', 'last_name', 'email', 'is_staff']
|
||||||
|
user_attrs = {k: v for k, v in attrs.items() if k in user_keys}
|
||||||
|
|
||||||
|
profile_keys = [
|
||||||
|
'is_cof', 'login_clipper', 'phone', 'occupation', 'departement',
|
||||||
|
'type_cotiz', 'mailing_cof', 'mailing_bda', 'mailing_bda_revente',
|
||||||
|
'comments', 'is_buro', 'petit_cours_accept',
|
||||||
|
'petit_cours_remarques',
|
||||||
|
]
|
||||||
|
profile_attrs = {k: v for k, v in attrs.items() if k in profile_keys}
|
||||||
|
|
||||||
|
if is_cof:
|
||||||
|
profile_attrs['is_cof'] = True
|
||||||
|
|
||||||
|
if is_staff:
|
||||||
|
# At the moment, admin is accessible by COF staff.
|
||||||
|
user_attrs['is_staff'] = True
|
||||||
|
profile_attrs['is_buro'] = True
|
||||||
|
|
||||||
|
user = User(username=username, **user_attrs)
|
||||||
|
user.set_password(password)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
for k, v in profile_attrs.items():
|
||||||
|
setattr(user.profile, k, v)
|
||||||
|
user.profile.save()
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(username, attrs=None):
|
||||||
|
return _create_user(username, attrs=attrs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_member(username, attrs=None):
|
||||||
|
return _create_user(username, is_cof=True, attrs=attrs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_staff(username, attrs=None):
|
||||||
|
return _create_user(username, is_cof=True, is_staff=True, attrs=attrs)
|
|
@ -20,6 +20,7 @@ class TriStateCheckbox(Widget):
|
||||||
def render(self, name, value, attrs=None, choices=()):
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
if value is None:
|
if value is None:
|
||||||
value = 'none'
|
value = 'none'
|
||||||
final_attrs = self.build_attrs(attrs, value=value)
|
attrs['value'] = value
|
||||||
|
final_attrs = self.build_attrs(self.attrs, attrs)
|
||||||
output = ["<span class=\"tristate\"%s></span>" % flatatt(final_attrs)]
|
output = ["<span class=\"tristate\"%s></span>" % flatatt(final_attrs)]
|
||||||
return mark_safe('\n'.join(output))
|
return mark_safe('\n'.join(output))
|
||||||
|
|
294
shared/tests/testcases.py
Normal file
294
shared/tests/testcases.py
Normal file
|
@ -0,0 +1,294 @@
|
||||||
|
from unittest import mock
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.http import QueryDict
|
||||||
|
from django.test import Client
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaseMixin:
|
||||||
|
|
||||||
|
def assertForbidden(self, response):
|
||||||
|
"""
|
||||||
|
Test that the response (retrieved with a Client) is a denial of access.
|
||||||
|
|
||||||
|
The response should verify one of the following:
|
||||||
|
- its HTTP response code is 403,
|
||||||
|
- it redirects to the login page with a GET parameter named 'next'
|
||||||
|
whose value is the url of the requested page.
|
||||||
|
|
||||||
|
"""
|
||||||
|
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 = '{}?{}'.format(
|
||||||
|
reverse('cof-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 assertUrlsEqual(self, actual, expected):
|
||||||
|
"""
|
||||||
|
Test that the url 'actual' is as 'expected'.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
actual (str): Url to verify.
|
||||||
|
expected: Two forms are accepted.
|
||||||
|
* (str): Expected url. Strings equality is checked.
|
||||||
|
* (dict): Its keys must be attributes of 'urlparse(actual)'.
|
||||||
|
Equality is checked for each present key, except for
|
||||||
|
'query' which must be a dict of the expected query string
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if type(expected) == dict:
|
||||||
|
parsed = urlparse(actual)
|
||||||
|
for part, expected_part in expected.items():
|
||||||
|
if part == 'query':
|
||||||
|
self.assertDictEqual(
|
||||||
|
parse_qs(parsed.query),
|
||||||
|
expected.get('query', {}),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.assertEqual(getattr(parsed, part), expected_part)
|
||||||
|
else:
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class ViewTestCaseMixin(TestCaseMixin):
|
||||||
|
"""
|
||||||
|
TestCase extension to ease tests of kfet views.
|
||||||
|
|
||||||
|
|
||||||
|
Urls concerns
|
||||||
|
-------------
|
||||||
|
|
||||||
|
# Basic usage
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
url_name (str): Name of view under test, as given to 'reverse'
|
||||||
|
function.
|
||||||
|
url_args (list, optional): Will be given to 'reverse' call.
|
||||||
|
url_kwargs (dict, optional): Same.
|
||||||
|
url_expcted (str): What 'reverse' should return given previous
|
||||||
|
attributes.
|
||||||
|
|
||||||
|
View url can then be accessed at the 'url' attribute.
|
||||||
|
|
||||||
|
# Advanced usage
|
||||||
|
|
||||||
|
If multiple combinations of url name, args, kwargs can be used for a view,
|
||||||
|
it is possible to define 'urls_conf' attribute. It must be a list whose
|
||||||
|
each item is a dict defining arguments for 'reverse' call ('name', 'args',
|
||||||
|
'kwargs' keys) and its expected result ('expected' key).
|
||||||
|
|
||||||
|
The reversed urls can be accessed at the 't_urls' attribute.
|
||||||
|
|
||||||
|
|
||||||
|
Users concerns
|
||||||
|
--------------
|
||||||
|
|
||||||
|
During setup, the following users are created:
|
||||||
|
- 'user': a basic user without any permission,
|
||||||
|
- 'root': a superuser, account trigramme: 200.
|
||||||
|
Their password is their username.
|
||||||
|
|
||||||
|
One can create additionnal users with 'get_users_extra' method, or prevent
|
||||||
|
these users to be created with 'get_users_base' method. See these two
|
||||||
|
methods for further informations.
|
||||||
|
|
||||||
|
By using 'register_user' method, these users can then be accessed at
|
||||||
|
'users' attribute by their label.
|
||||||
|
|
||||||
|
A user label can be given to 'auth_user' attribute. The related user is
|
||||||
|
then authenticated on self.client during test setup. Its value defaults to
|
||||||
|
'None', meaning no user is authenticated.
|
||||||
|
|
||||||
|
|
||||||
|
Automated tests
|
||||||
|
---------------
|
||||||
|
|
||||||
|
# Url reverse
|
||||||
|
|
||||||
|
Based on url-related attributes/properties, the test 'test_urls' checks
|
||||||
|
that expected url is returned by 'reverse' (once with basic url usage and
|
||||||
|
each for advanced usage).
|
||||||
|
|
||||||
|
# Forbidden responses
|
||||||
|
|
||||||
|
The 'test_forbidden' test verifies that each user, from labels of
|
||||||
|
'auth_forbidden' attribute, can't access the url(s), i.e. response should
|
||||||
|
be a 403, or a redirect to login view.
|
||||||
|
|
||||||
|
Tested HTTP requests are given by 'http_methods' attribute. Additional data
|
||||||
|
can be given by defining an attribute '<method(lowercase)>_data'.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url_name = None
|
||||||
|
url_expected = None
|
||||||
|
|
||||||
|
http_methods = ['GET']
|
||||||
|
|
||||||
|
auth_user = None
|
||||||
|
auth_forbidden = []
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Warning: Do not forget to call super().setUp() in subclasses.
|
||||||
|
"""
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
# Register of User instances.
|
||||||
|
self.users = {}
|
||||||
|
|
||||||
|
for label, user in dict(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,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
del self.users_base
|
||||||
|
del self.users_extra
|
||||||
|
|
||||||
|
def get_users_base(self):
|
||||||
|
"""
|
||||||
|
Dict of <label: user instance>.
|
||||||
|
|
||||||
|
Note: Don't access yourself this property. Use 'users_base' attribute
|
||||||
|
which cache the returned value from here.
|
||||||
|
It allows to give functions calls, which creates users instances, as
|
||||||
|
values here.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'user': User.objects.create_user('user', '', 'user'),
|
||||||
|
'root': User.objects.create_superuser('root', '', 'root'),
|
||||||
|
}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def users_base(self):
|
||||||
|
return self.get_users_base()
|
||||||
|
|
||||||
|
def get_users_extra(self):
|
||||||
|
"""
|
||||||
|
Dict of <label: user instance>.
|
||||||
|
|
||||||
|
Note: Don't access yourself this property. Use 'users_base' attribute
|
||||||
|
which cache the returned value from here.
|
||||||
|
It allows to give functions calls, which create users instances, as
|
||||||
|
values here.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def users_extra(self):
|
||||||
|
return self.get_users_extra()
|
||||||
|
|
||||||
|
def register_user(self, label, user):
|
||||||
|
self.users[label] = user
|
||||||
|
|
||||||
|
def get_user(self, label):
|
||||||
|
if self.auth_user is not None:
|
||||||
|
return self.auth_user
|
||||||
|
return self.auth_user_mapping.get(label)
|
||||||
|
|
||||||
|
@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):
|
||||||
|
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)
|
Loading…
Add table
Reference in a new issue