Backport test case improvements
Add random primary key mixin. Split test case code into mixins. Make the view test case and the API test cases part of the same class hierachy. Update tests that failed due to the new import locations. Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
37
mayan/apps/acls/tests/mixins.py
Normal file
37
mayan/apps/acls/tests/mixins.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from acls.models import AccessControlList
|
||||
from permissions.models import Role
|
||||
from permissions.tests.literals import TEST_ROLE_LABEL
|
||||
from permissions.tests.mixins import RoleTestCaseMixin
|
||||
from user_management.tests.literals import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_GROUP_NAME, TEST_USER_EMAIL, TEST_USER_USERNAME, TEST_USER_PASSWORD
|
||||
)
|
||||
|
||||
|
||||
class ACLTestCaseMixin(RoleTestCaseMixin):
|
||||
def setUp(self):
|
||||
super(ACLTestCaseMixin, self).setUp()
|
||||
self.admin_user = get_user_model().objects.create_superuser(
|
||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
||||
password=TEST_ADMIN_PASSWORD
|
||||
)
|
||||
|
||||
self.user = get_user_model().objects.create_user(
|
||||
username=TEST_USER_USERNAME, email=TEST_USER_EMAIL,
|
||||
password=TEST_USER_PASSWORD
|
||||
)
|
||||
|
||||
self.group = Group.objects.create(name=TEST_GROUP_NAME)
|
||||
self.role = Role.objects.create(label=TEST_ROLE_LABEL)
|
||||
self.group.user_set.add(self.user)
|
||||
self.role.groups.add(self.group)
|
||||
|
||||
def grant_access(self, permission, obj):
|
||||
AccessControlList.objects.grant(
|
||||
permission=permission, role=self.role, obj=obj
|
||||
)
|
||||
@@ -53,6 +53,7 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
)
|
||||
|
||||
def test_object_acl_delete_view(self):
|
||||
self.expected_content_type = None
|
||||
self._create_acl()
|
||||
|
||||
response = self.delete(
|
||||
@@ -87,6 +88,7 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
)
|
||||
|
||||
def test_object_acl_permission_delete_view(self):
|
||||
self.expected_content_type = None
|
||||
self._create_acl()
|
||||
permission = self.acl.permissions.first()
|
||||
|
||||
|
||||
@@ -35,21 +35,6 @@ class DocumentCheckoutTestCase(DocumentTestMixin, BaseTestCase):
|
||||
)
|
||||
)
|
||||
|
||||
def test_version_creation_blocking(self):
|
||||
# Silence unrelated logging
|
||||
logging.getLogger('documents.models').setLevel(logging.CRITICAL)
|
||||
|
||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||
|
||||
DocumentCheckout.objects.checkout_document(
|
||||
document=self.document, expiration_datetime=expiration_datetime,
|
||||
user=self.admin_user, block_new_version=True
|
||||
)
|
||||
|
||||
with self.assertRaises(NewDocumentVersionNotAllowed):
|
||||
with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object:
|
||||
self.document.new_version(file_object=file_object)
|
||||
|
||||
def test_checkin_in(self):
|
||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||
|
||||
@@ -100,16 +85,6 @@ class DocumentCheckoutTestCase(DocumentTestMixin, BaseTestCase):
|
||||
|
||||
self.assertFalse(self.document.is_checked_out())
|
||||
|
||||
def test_blocking_new_versions(self):
|
||||
# Silence unrelated logging
|
||||
logging.getLogger('documents.models').setLevel(logging.CRITICAL)
|
||||
|
||||
NewVersionBlock.objects.block(document=self.document)
|
||||
|
||||
with self.assertRaises(NewDocumentVersionNotAllowed):
|
||||
with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object:
|
||||
self.document.new_version(file_object=file_object)
|
||||
|
||||
|
||||
@override_settings(OCR_AUTO_OCR=False)
|
||||
class NewVersionBlockTestCase(DocumentTestMixin, BaseTestCase):
|
||||
@@ -121,6 +96,16 @@ class NewVersionBlockTestCase(DocumentTestMixin, BaseTestCase):
|
||||
NewVersionBlock.objects.first().document, self.document
|
||||
)
|
||||
|
||||
def test_blocking_new_versions(self):
|
||||
# Silence unrelated logging
|
||||
logging.getLogger('documents.models').setLevel(logging.CRITICAL)
|
||||
|
||||
NewVersionBlock.objects.block(document=self.document)
|
||||
|
||||
with self.assertRaises(NewDocumentVersionNotAllowed):
|
||||
with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object:
|
||||
self.document.new_version(file_object=file_object)
|
||||
|
||||
def test_unblocking(self):
|
||||
NewVersionBlock.objects.create(document=self.document)
|
||||
|
||||
@@ -140,3 +125,18 @@ class NewVersionBlockTestCase(DocumentTestMixin, BaseTestCase):
|
||||
self.assertFalse(
|
||||
NewVersionBlock.objects.is_blocked(document=self.document)
|
||||
)
|
||||
|
||||
def test_version_creation_blocking(self):
|
||||
# Silence unrelated logging
|
||||
logging.getLogger('documents.models').setLevel(logging.CRITICAL)
|
||||
|
||||
expiration_datetime = now() + datetime.timedelta(days=1)
|
||||
|
||||
DocumentCheckout.objects.checkout_document(
|
||||
document=self.document, expiration_datetime=expiration_datetime,
|
||||
user=self.admin_user, block_new_version=True
|
||||
)
|
||||
|
||||
with self.assertRaises(NewDocumentVersionNotAllowed):
|
||||
with open(TEST_SMALL_DOCUMENT_PATH, mode='rb') as file_object:
|
||||
self.document.new_version(file_object=file_object)
|
||||
|
||||
@@ -8,7 +8,7 @@ from django.utils.timezone import now
|
||||
from common.literals import TIME_DELTA_UNIT_DAYS
|
||||
from documents.tests import GenericDocumentViewTestCase
|
||||
from sources.links import link_upload_version
|
||||
from user_management.tests import (
|
||||
from user_management.tests.literals import (
|
||||
TEST_USER_PASSWORD, TEST_USER_USERNAME, TEST_ADMIN_PASSWORD,
|
||||
TEST_ADMIN_USERNAME,
|
||||
)
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.http import HttpResponse
|
||||
from django.template import Context, Template
|
||||
from django.test import TestCase
|
||||
from django.test.utils import ContextList
|
||||
from django.urls import clear_url_caches, reverse
|
||||
|
||||
from django_downloadview import assert_download_response
|
||||
|
||||
from acls.tests.mixins import ACLTestCaseMixin
|
||||
from permissions.classes import Permission
|
||||
from smart_settings.classes import Namespace
|
||||
from user_management.tests import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_USER_USERNAME,
|
||||
TEST_USER_PASSWORD
|
||||
)
|
||||
from user_management.tests.mixins import UserTestCaseMixin
|
||||
|
||||
from .literals import TEST_VIEW_NAME, TEST_VIEW_URL
|
||||
from .mixins import (
|
||||
ContentTypeCheckMixin, DatabaseConversionMixin, OpenFileCheckMixin,
|
||||
TempfileCheckMixin, UserMixin
|
||||
ClientMethodsTestCaseMixin, ContentTypeCheckTestCaseMixin,
|
||||
DatabaseConversionMixin, OpenFileCheckTestCaseMixin,
|
||||
RandomPrimaryKeyModelMonkeyPatchMixin, TempfileCheckTestCasekMixin,
|
||||
TestViewTestCaseMixin
|
||||
)
|
||||
|
||||
|
||||
class BaseTestCase(DatabaseConversionMixin, UserMixin, ContentTypeCheckMixin, OpenFileCheckMixin, TempfileCheckMixin, TestCase):
|
||||
class BaseTestCase(RandomPrimaryKeyModelMonkeyPatchMixin, DatabaseConversionMixin, ACLTestCaseMixin, OpenFileCheckTestCaseMixin, TempfileCheckTestCasekMixin, TestCase):
|
||||
"""
|
||||
This is the most basic test case class any test in the project should use.
|
||||
"""
|
||||
@@ -36,81 +29,9 @@ class BaseTestCase(DatabaseConversionMixin, UserMixin, ContentTypeCheckMixin, Op
|
||||
Permission.invalidate_cache()
|
||||
|
||||
|
||||
class GenericViewTestCase(BaseTestCase):
|
||||
def setUp(self):
|
||||
super(GenericViewTestCase, self).setUp()
|
||||
self.has_test_view = False
|
||||
|
||||
def tearDown(self):
|
||||
from mayan.urls import urlpatterns
|
||||
|
||||
self.client.logout()
|
||||
if self.has_test_view:
|
||||
urlpatterns.pop(0)
|
||||
super(GenericViewTestCase, self).tearDown()
|
||||
|
||||
def add_test_view(self, test_object):
|
||||
from mayan.urls import urlpatterns
|
||||
|
||||
def test_view(request):
|
||||
template = Template('{{ object }}')
|
||||
context = Context(
|
||||
{'object': test_object, 'resolved_object': test_object}
|
||||
)
|
||||
return HttpResponse(template.render(context=context))
|
||||
|
||||
urlpatterns.insert(0, url(TEST_VIEW_URL, test_view, name=TEST_VIEW_NAME))
|
||||
clear_url_caches()
|
||||
self.has_test_view = True
|
||||
|
||||
def get_test_view(self):
|
||||
response = self.get(TEST_VIEW_NAME)
|
||||
if isinstance(response.context, ContextList):
|
||||
# template widget rendering causes test client response to be
|
||||
# ContextList rather than RequestContext. Typecast to dictionary
|
||||
# before updating.
|
||||
result = dict(response.context).copy()
|
||||
result.update({'request': response.wsgi_request})
|
||||
return Context(result)
|
||||
else:
|
||||
response.context.update({'request': response.wsgi_request})
|
||||
return Context(response.context)
|
||||
|
||||
def get(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
return self.client.get(
|
||||
path=path, data=data, follow=follow
|
||||
)
|
||||
|
||||
def login(self, username, password):
|
||||
logged_in = self.client.login(username=username, password=password)
|
||||
|
||||
user = get_user_model().objects.get(username=username)
|
||||
|
||||
self.assertTrue(logged_in)
|
||||
self.assertTrue(user.is_authenticated)
|
||||
|
||||
def login_user(self):
|
||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||
|
||||
def login_admin_user(self):
|
||||
self.login(username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD)
|
||||
|
||||
def logout(self):
|
||||
self.client.logout()
|
||||
|
||||
def post(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
return self.client.post(
|
||||
path=path, data=data, follow=follow
|
||||
)
|
||||
class GenericViewTestCase(ClientMethodsTestCaseMixin, ContentTypeCheckTestCaseMixin, TestViewTestCaseMixin, UserTestCaseMixin, BaseTestCase):
|
||||
"""
|
||||
A generic view test case built on top of the base test case providing
|
||||
a single, user customizable view to test object resolution and shorthand
|
||||
HTTP method functions.
|
||||
"""
|
||||
|
||||
@@ -2,19 +2,21 @@ from __future__ import unicode_literals
|
||||
|
||||
import glob
|
||||
import os
|
||||
import random
|
||||
|
||||
from furl import furl
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.conf.urls import url
|
||||
from django.core import management
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save, pre_save
|
||||
from django.http import HttpResponse
|
||||
from django.template import Context, Template
|
||||
from django.test.utils import ContextList
|
||||
from django.urls import clear_url_caches, reverse
|
||||
|
||||
from acls.models import AccessControlList
|
||||
from permissions.models import Role
|
||||
from permissions.tests.literals import TEST_ROLE_LABEL
|
||||
from user_management.tests import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_ADMIN_EMAIL,
|
||||
TEST_GROUP_NAME, TEST_USER_EMAIL, TEST_USER_USERNAME, TEST_USER_PASSWORD
|
||||
)
|
||||
from .literals import TEST_VIEW_NAME, TEST_VIEW_URL
|
||||
|
||||
from ..settings import setting_temporary_directory
|
||||
|
||||
@@ -22,18 +24,69 @@ if getattr(settings, 'COMMON_TEST_FILE_HANDLES', False):
|
||||
import psutil
|
||||
|
||||
|
||||
class ContentTypeCheckMixin(object):
|
||||
class ClientMethodsTestCaseMixin(object):
|
||||
def _build_verb_kwargs(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
query = kwargs.pop('query', {})
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
path = furl(url=path)
|
||||
path.args.update(query)
|
||||
|
||||
return {'follow': follow, 'data': data, 'path': path.tostr()}
|
||||
|
||||
def delete(self, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.delete(
|
||||
**self._build_verb_kwargs(
|
||||
path=path, viewname=viewname, *args, **kwargs
|
||||
)
|
||||
)
|
||||
|
||||
def get(self, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.get(
|
||||
**self._build_verb_kwargs(
|
||||
path=path, viewname=viewname, *args, **kwargs
|
||||
)
|
||||
)
|
||||
|
||||
def patch(self, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.patch(
|
||||
**self._build_verb_kwargs(
|
||||
path=path, viewname=viewname, *args, **kwargs
|
||||
)
|
||||
)
|
||||
|
||||
def post(self, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.post(
|
||||
**self._build_verb_kwargs(
|
||||
path=path, viewname=viewname, *args, **kwargs
|
||||
)
|
||||
)
|
||||
|
||||
def put(self, viewname=None, path=None, *args, **kwargs):
|
||||
return self.client.put(
|
||||
**self._build_verb_kwargs(
|
||||
path=path, viewname=viewname, *args, **kwargs
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class ContentTypeCheckTestCaseMixin(object):
|
||||
expected_content_type = 'text/html; charset=utf-8'
|
||||
|
||||
def _pre_setup(self):
|
||||
super(ContentTypeCheckMixin, self)._pre_setup()
|
||||
super(ContentTypeCheckTestCaseMixin, self)._pre_setup()
|
||||
test_instance = self
|
||||
|
||||
class CustomClient(self.client_class):
|
||||
def request(self, *args, **kwargs):
|
||||
response = super(CustomClient, self).request(*args, **kwargs)
|
||||
|
||||
content_type = response._headers['content-type'][1]
|
||||
content_type = response._headers.get('content-type', [None, ''])[1]
|
||||
if test_instance.expected_content_type:
|
||||
test_instance.assertEqual(
|
||||
content_type, test_instance.expected_content_type,
|
||||
msg='Unexpected response content type: {}, expected: {}.'.format(
|
||||
@@ -53,7 +106,7 @@ class DatabaseConversionMixin(object):
|
||||
)
|
||||
|
||||
|
||||
class OpenFileCheckMixin(object):
|
||||
class OpenFileCheckTestCaseMixin(object):
|
||||
def _get_descriptor_count(self):
|
||||
process = psutil.Process()
|
||||
return process.num_fds()
|
||||
@@ -63,7 +116,7 @@ class OpenFileCheckMixin(object):
|
||||
return process.open_files()
|
||||
|
||||
def setUp(self):
|
||||
super(OpenFileCheckMixin, self).setUp()
|
||||
super(OpenFileCheckTestCaseMixin, self).setUp()
|
||||
if getattr(settings, 'COMMON_TEST_FILE_HANDLES', False):
|
||||
self._open_files = self._get_open_files()
|
||||
|
||||
@@ -78,10 +131,84 @@ class OpenFileCheckMixin(object):
|
||||
|
||||
self._skip_file_descriptor_test = False
|
||||
|
||||
super(OpenFileCheckMixin, self).tearDown()
|
||||
super(OpenFileCheckTestCaseMixin, self).tearDown()
|
||||
|
||||
|
||||
class TempfileCheckMixin(object):
|
||||
class RandomPrimaryKeyModelMonkeyPatchMixin(object):
|
||||
random_primary_key_random_floor = 100
|
||||
random_primary_key_random_ceiling = 10000
|
||||
random_primary_key_maximum_attempts = 100
|
||||
|
||||
@staticmethod
|
||||
def get_unique_primary_key(model):
|
||||
pk_list = model._meta.default_manager.values_list('pk', flat=True)
|
||||
|
||||
attempts = 0
|
||||
while True:
|
||||
primary_key = random.randint(
|
||||
RandomPrimaryKeyModelMonkeyPatchMixin.random_primary_key_random_floor,
|
||||
RandomPrimaryKeyModelMonkeyPatchMixin.random_primary_key_random_ceiling
|
||||
)
|
||||
|
||||
if primary_key not in pk_list:
|
||||
break
|
||||
|
||||
attempts = attempts + 1
|
||||
|
||||
if attempts > RandomPrimaryKeyModelMonkeyPatchMixin.random_primary_key_maximum_attempts:
|
||||
raise Exception(
|
||||
'Maximum number of retries for an unique random primary '
|
||||
'key reached.'
|
||||
)
|
||||
|
||||
return primary_key
|
||||
|
||||
def setUp(self):
|
||||
self.method_save_original = models.Model.save
|
||||
|
||||
def method_save_new(instance, *args, **kwargs):
|
||||
if instance.pk:
|
||||
return self.method_save_original(instance, *args, **kwargs)
|
||||
else:
|
||||
# Set meta.auto_created to True to have the original save_base
|
||||
# not send the pre_save signal which would normally send
|
||||
# the instance without a primary key. Since we assign a random
|
||||
# primary key any pre_save signal handler that relies on an
|
||||
# empty primary key will fail.
|
||||
# The meta.auto_created and manual pre_save sending emulates
|
||||
# the original behavior. Since meta.auto_created also disables
|
||||
# the post_save signal we must also send it ourselves.
|
||||
# This hack work with Django 1.11 .save_base() but can break
|
||||
# in future versions if that method is updated.
|
||||
pre_save.send(
|
||||
sender=instance.__class__, instance=instance, raw=False,
|
||||
update_fields=None,
|
||||
)
|
||||
instance._meta.auto_created = True
|
||||
instance.pk = RandomPrimaryKeyModelMonkeyPatchMixin.get_unique_primary_key(
|
||||
model=instance._meta.model
|
||||
)
|
||||
instance.id = instance.pk
|
||||
|
||||
result = instance.save_base(force_insert=True)
|
||||
instance._meta.auto_created = False
|
||||
|
||||
post_save.send(
|
||||
sender=instance.__class__, instance=instance, created=True,
|
||||
update_fields=None, raw=False
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
setattr(models.Model, 'save', method_save_new)
|
||||
super(RandomPrimaryKeyModelMonkeyPatchMixin, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
models.Model.save = self.method_save_original
|
||||
super(RandomPrimaryKeyModelMonkeyPatchMixin, self).tearDown()
|
||||
|
||||
|
||||
class TempfileCheckTestCasekMixin(object):
|
||||
# Ignore the jvmstat instrumentation and GitLab's CI .config files
|
||||
# Ignore LibreOffice fontconfig cache dir
|
||||
ignore_globs = ('hsperfdata_*', '.config', '.cache')
|
||||
@@ -106,7 +233,7 @@ class TempfileCheckMixin(object):
|
||||
) - set(ignored_result)
|
||||
|
||||
def setUp(self):
|
||||
super(TempfileCheckMixin, self).setUp()
|
||||
super(TempfileCheckTestCasekMixin, self).setUp()
|
||||
if getattr(settings, 'COMMON_TEST_TEMP_FILES', False):
|
||||
self._temporary_items = self._get_temporary_entries()
|
||||
|
||||
@@ -121,33 +248,43 @@ class TempfileCheckMixin(object):
|
||||
','.join(final_temporary_items - self._temporary_items)
|
||||
)
|
||||
)
|
||||
super(TempfileCheckMixin, self).tearDown()
|
||||
super(TempfileCheckTestCasekMixin, self).tearDown()
|
||||
|
||||
|
||||
class UserMixin(object):
|
||||
def setUp(self):
|
||||
super(UserMixin, self).setUp()
|
||||
self.admin_user = get_user_model().objects.create_superuser(
|
||||
username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL,
|
||||
password=TEST_ADMIN_PASSWORD
|
||||
class TestViewTestCaseMixin(object):
|
||||
has_test_view = False
|
||||
|
||||
def tearDown(self):
|
||||
from mayan.urls import urlpatterns
|
||||
|
||||
self.client.logout()
|
||||
if self.has_test_view:
|
||||
urlpatterns.pop(0)
|
||||
super(TestViewTestCaseMixin, self).tearDown()
|
||||
|
||||
def add_test_view(self, test_object):
|
||||
from mayan.urls import urlpatterns
|
||||
|
||||
def test_view(request):
|
||||
template = Template('{{ object }}')
|
||||
context = Context(
|
||||
{'object': test_object, 'resolved_object': test_object}
|
||||
)
|
||||
return HttpResponse(template.render(context=context))
|
||||
|
||||
self.user = get_user_model().objects.create_user(
|
||||
username=TEST_USER_USERNAME, email=TEST_USER_EMAIL,
|
||||
password=TEST_USER_PASSWORD
|
||||
)
|
||||
urlpatterns.insert(0, url(TEST_VIEW_URL, test_view, name=TEST_VIEW_NAME))
|
||||
clear_url_caches()
|
||||
self.has_test_view = True
|
||||
|
||||
self.group = Group.objects.create(name=TEST_GROUP_NAME)
|
||||
self.role = Role.objects.create(label=TEST_ROLE_LABEL)
|
||||
self.group.user_set.add(self.user)
|
||||
self.role.groups.add(self.group)
|
||||
|
||||
def grant_access(self, permission, obj):
|
||||
AccessControlList.objects.grant(
|
||||
permission=permission, role=self.role, obj=obj
|
||||
)
|
||||
|
||||
def grant_permission(self, permission):
|
||||
self.role.permissions.add(
|
||||
permission.stored_permission
|
||||
)
|
||||
def get_test_view(self):
|
||||
response = self.get(TEST_VIEW_NAME)
|
||||
if isinstance(response.context, ContextList):
|
||||
# template widget rendering causes test client response to be
|
||||
# ContextList rather than RequestContext. Typecast to dictionary
|
||||
# before updating.
|
||||
result = dict(response.context).copy()
|
||||
result.update({'request': response.wsgi_request})
|
||||
return Context(result)
|
||||
else:
|
||||
response.context.update({'request': response.wsgi_request})
|
||||
return Context(response.context)
|
||||
|
||||
@@ -135,6 +135,7 @@ class DocumentTypeAPITestCase(BaseAPITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
def test_document_type_delete_with_access(self):
|
||||
self.expected_content_type = None
|
||||
self.document_type = DocumentType.objects.create(
|
||||
label=TEST_DOCUMENT_TYPE_LABEL
|
||||
)
|
||||
@@ -522,6 +523,8 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
def test_document_move_to_trash_with_access(self):
|
||||
self.expected_content_type = None
|
||||
|
||||
self.document = self.upload_document()
|
||||
self.grant_access(
|
||||
permission=permission_document_trash, obj=self.document
|
||||
@@ -546,6 +549,8 @@ class TrashedDocumentAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
self.assertEqual(Document.trash.count(), 1)
|
||||
|
||||
def test_trashed_document_delete_from_trash_with_access(self):
|
||||
self.expected_content_type = None
|
||||
|
||||
self.document = self.upload_document()
|
||||
self.document.delete()
|
||||
self.grant_access(permission=permission_document_delete, obj=self.document)
|
||||
|
||||
8
mayan/apps/permissions/tests/mixins.py
Normal file
8
mayan/apps/permissions/tests/mixins.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
class RoleTestCaseMixin(object):
|
||||
def grant_permission(self, permission):
|
||||
self.role.permissions.add(
|
||||
permission.stored_permission
|
||||
)
|
||||
@@ -1,101 +1,19 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from common.tests.mixins import UserMixin
|
||||
from common.tests import GenericViewTestCase
|
||||
from permissions.classes import Permission
|
||||
from smart_settings.classes import Namespace
|
||||
from user_management.tests import (
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_USER_USERNAME,
|
||||
TEST_USER_PASSWORD
|
||||
)
|
||||
|
||||
|
||||
class BaseAPITestCase(UserMixin, APITestCase):
|
||||
class BaseAPITestCase(APITestCase, GenericViewTestCase):
|
||||
"""
|
||||
API test case class that invalidates permissions and smart settings
|
||||
"""
|
||||
expected_content_type = None
|
||||
|
||||
def setUp(self):
|
||||
super(BaseAPITestCase, self).setUp()
|
||||
Namespace.invalidate_cache_all()
|
||||
Permission.invalidate_cache()
|
||||
|
||||
def tearDown(self):
|
||||
self.client.logout()
|
||||
super(BaseAPITestCase, self).tearDown()
|
||||
|
||||
def delete(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
return self.client.delete(
|
||||
path=path, data=data, follow=follow
|
||||
)
|
||||
|
||||
def get(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
return self.client.get(
|
||||
path=path, data=data, follow=follow
|
||||
)
|
||||
|
||||
def login(self, username, password):
|
||||
logged_in = self.client.login(username=username, password=password)
|
||||
|
||||
user = get_user_model().objects.get(username=username)
|
||||
|
||||
self.assertTrue(logged_in)
|
||||
self.assertTrue(user.is_authenticated)
|
||||
return user.is_authenticated
|
||||
|
||||
def login_user(self):
|
||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||
|
||||
def login_admin_user(self):
|
||||
self.login(username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD)
|
||||
|
||||
def logout(self):
|
||||
self.client.logout()
|
||||
|
||||
def patch(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
return self.client.patch(
|
||||
path=path, data=data, follow=follow
|
||||
)
|
||||
|
||||
def post(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
return self.client.post(
|
||||
path=path, data=data, follow=follow
|
||||
)
|
||||
|
||||
def put(self, viewname=None, path=None, *args, **kwargs):
|
||||
data = kwargs.pop('data', {})
|
||||
follow = kwargs.pop('follow', False)
|
||||
|
||||
if viewname:
|
||||
path = reverse(viewname=viewname, *args, **kwargs)
|
||||
|
||||
return self.client.put(
|
||||
path=path, data=data, follow=follow
|
||||
)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from .literals import * # NOQA
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__all__ = (
|
||||
'TEST_ADMIN_EMAIL', 'TEST_ADMIN_PASSWORD', 'TEST_ADMIN_USERNAME',
|
||||
'TEST_GROUP_NAME', 'TEST_GROUP_NAME_EDITED', 'TEST_USER_EMAIL',
|
||||
'TEST_USER_PASSWORD', 'TEST_USER_PASSWORD_EDITED', 'TEST_USER_USERNAME'
|
||||
)
|
||||
|
||||
TEST_ADMIN_EMAIL = 'admin@example.com'
|
||||
TEST_ADMIN_PASSWORD = 'test admin password'
|
||||
TEST_ADMIN_USERNAME = 'test_admin'
|
||||
|
||||
@@ -3,7 +3,9 @@ from __future__ import unicode_literals
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from .literals import (
|
||||
TEST_USER_2_EMAIL, TEST_USER_2_PASSWORD, TEST_USER_2_USERNAME
|
||||
TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_USER_PASSWORD,
|
||||
TEST_USER_USERNAME, TEST_USER_2_EMAIL, TEST_USER_2_PASSWORD,
|
||||
TEST_USER_2_USERNAME
|
||||
)
|
||||
|
||||
|
||||
@@ -18,3 +20,22 @@ class UserTestMixin(object):
|
||||
self.user_2 = get_user_model().objects.create(
|
||||
username=TEST_USER_2_USERNAME
|
||||
)
|
||||
|
||||
|
||||
class UserTestCaseMixin(object):
|
||||
def tearDown(self):
|
||||
self.client.logout()
|
||||
super(UserTestCaseMixin, self).tearDown()
|
||||
|
||||
def login(self, username, password):
|
||||
logged_in = self.client.login(username=username, password=password)
|
||||
return logged_in
|
||||
|
||||
def login_user(self):
|
||||
self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD)
|
||||
|
||||
def login_admin_user(self):
|
||||
self.login(username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD)
|
||||
|
||||
def logout(self):
|
||||
self.client.logout()
|
||||
|
||||
@@ -87,11 +87,12 @@ class UserManagementViewTestCase(UserTestMixin, GenericViewTestCase):
|
||||
|
||||
self.logout()
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
self.login(
|
||||
result = self.login(
|
||||
username=TEST_USER_2_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
response = self.get('common:current_user_details')
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
@@ -134,11 +135,12 @@ class UserManagementViewTestCase(UserTestMixin, GenericViewTestCase):
|
||||
|
||||
self.logout()
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
self.login(
|
||||
result = self.login(
|
||||
username=TEST_USER_2_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertFalse(result)
|
||||
|
||||
response = self.get('common:current_user_details')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user