1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00
Files
netbox-community-netbox/netbox/utilities/testing/views.py

907 lines
35 KiB
Python
Raw Normal View History

from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import ForeignKey, ManyToManyField
from django.forms.models import model_to_dict
from django.test import Client, TestCase as _TestCase, override_settings
2020-01-31 10:30:13 -05:00
from django.urls import reverse, NoReverseMatch
2020-06-12 11:33:23 -04:00
from django.utils.text import slugify
from netaddr import IPNetwork
2020-06-12 11:33:23 -04:00
from extras.models import Tag
2020-06-08 10:46:53 -04:00
from users.models import ObjectPermission
2020-06-02 09:26:45 -04:00
from utilities.permissions import resolve_permission_ct
from .utils import disable_warnings, post_data
2020-06-08 10:46:53 -04:00
__all__ = (
'TestCase',
'ModelViewTestCase',
'ViewTestCases',
)
class TestCase(_TestCase):
user_permissions = ()
def setUp(self):
# Create the test user and assign permissions
self.user = User.objects.create_user(username='testuser')
self.add_permissions(*self.user_permissions)
# Initialize the test client
self.client = Client()
self.client.force_login(self.user)
#
# Permissions management
#
def add_permissions(self, *names):
"""
Assign a set of permissions to the test user. Accepts permission names in the form <app>.<action>_<model>.
"""
for name in names:
2020-06-02 09:26:45 -04:00
ct, action = resolve_permission_ct(name)
obj_perm = ObjectPermission(actions=[action])
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ct)
#
2020-06-12 11:33:23 -04:00
# Custom assertions
#
def assertHttpStatus(self, response, expected_status):
"""
TestCase method. Provide more detail in the event of an unexpected HTTP response.
"""
err_message = "Expected HTTP status {}; received {}: {}"
self.assertEqual(response.status_code, expected_status, err_message.format(
expected_status, response.status_code, getattr(response, 'data', 'No data')
))
def assertInstanceEqual(self, instance, data, api=False):
"""
Compare a model instance to a dictionary, checking that its attribute values match those specified
in the dictionary.
:instance: Python object instance
:data: Dictionary of test data used to define the instance
:api: Set to True is the data is a JSON representation of the instance
"""
model_dict = model_to_dict(instance, fields=data.keys())
for key, value in list(model_dict.items()):
# TODO: Differentiate between tags assigned to the instance and a M2M field for tags (ex: ConfigContext)
if key == 'tags':
2020-06-12 11:33:23 -04:00
model_dict[key] = sorted(value)
# Convert ManyToManyField to list of instance PKs
elif model_dict[key] and type(value) in (list, tuple) and hasattr(value[0], 'pk'):
model_dict[key] = [obj.pk for obj in value]
if api:
# Replace ContentType numeric IDs with <app_label>.<model>
field = instance._meta.get_field(key)
if type(field) is ForeignKey and field.related_model is ContentType:
ct = ContentType.objects.get(pk=value)
model_dict[key] = f'{ct.app_label}.{ct.model}'
elif type(field) is ManyToManyField and field.related_model is ContentType:
model_dict[key] = [f'{ct.app_label}.{ct.model}' for ct in value]
# Convert IPNetwork instances to strings
elif type(value) is IPNetwork:
model_dict[key] = str(value)
else:
# Convert ArrayFields to CSV strings
if type(instance._meta.get_field(key)) is ArrayField:
model_dict[key] = ','.join([str(v) for v in value])
# Omit any dictionary keys which are not instance attributes
relevant_data = {
k: v for k, v in data.items() if hasattr(instance, k)
}
self.assertDictEqual(model_dict, relevant_data)
2020-06-12 11:33:23 -04:00
#
# Convenience methods
#
@classmethod
def create_tags(cls, *names):
"""
Create and return a Tag instance for each name given.
"""
tags = [Tag(name=name, slug=slugify(name)) for name in names]
Tag.objects.bulk_create(tags)
return tags
#
# UI Tests
#
class ModelViewTestCase(TestCase):
"""
Base TestCase for model views. Subclass to test individual views.
"""
model = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.model is None:
raise Exception("Test case requires model to be defined")
def _get_base_url(self):
"""
Return the base format for a URL for the test's model. Override this to test for a model which belongs
to a different app (e.g. testing Interfaces within the virtualization app).
"""
return '{}:{}_{{}}'.format(
self.model._meta.app_label,
self.model._meta.model_name
)
def _get_url(self, action, instance=None):
"""
Return the URL name for a specific action. An instance must be specified for
get/edit/delete views.
"""
url_format = self._get_base_url()
if action in ('list', 'add', 'import', 'bulk_edit', 'bulk_delete'):
return reverse(url_format.format(action))
elif action in ('get', 'edit', 'delete'):
if instance is None:
raise Exception("Resolving {} URL requires specifying an instance".format(action))
# Attempt to resolve using slug first
if hasattr(self.model, 'slug'):
try:
return reverse(url_format.format(action), kwargs={'slug': instance.slug})
except NoReverseMatch:
pass
return reverse(url_format.format(action), kwargs={'pk': instance.pk})
else:
raise Exception("Invalid action for URL resolution: {}".format(action))
class ViewTestCases:
"""
We keep any TestCases with test_* methods inside a class to prevent unittest from trying to run them.
"""
class GetObjectViewTestCase(ModelViewTestCase):
"""
Retrieve a single instance.
"""
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_get_object_anonymous(self):
# Make the request as an unauthenticated user
self.client.logout()
response = self.client.get(self.model.objects.first().get_absolute_url())
self.assertHttpStatus(response, 200)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_get_object_without_permission(self):
instance = self.model.objects.first()
# Try GET without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.get(instance.get_absolute_url()), 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_get_object_with_permission(self):
2020-05-28 11:08:35 -04:00
instance = self.model.objects.first()
# Add model-level permission
obj_perm = ObjectPermission(
actions=['view']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try GET with model-level permission
self.assertHttpStatus(self.client.get(instance.get_absolute_url()), 200)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_get_object_with_constrained_permission(self):
instance1, instance2 = self.model.objects.all()[:2]
# Add object-level permission
obj_perm = ObjectPermission(
2020-06-03 09:43:46 -04:00
constraints={'pk': instance1.pk},
actions=['view']
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
# Try GET to permitted object
self.assertHttpStatus(self.client.get(instance1.get_absolute_url()), 200)
# Try GET to non-permitted object
self.assertHttpStatus(self.client.get(instance2.get_absolute_url()), 404)
class CreateObjectViewTestCase(ModelViewTestCase):
"""
Create a single new instance.
2020-06-11 16:12:50 -04:00
:form_data: Data to be used when creating a new object.
"""
form_data = {}
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_create_object_without_permission(self):
# Try GET without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(self._get_url('add')), 403)
# Try POST without permission
request = {
'path': self._get_url('add'),
'data': post_data(self.form_data),
}
response = self.client.post(**request)
with disable_warnings('django.request'):
self.assertHttpStatus(response, 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_create_object_with_permission(self):
2020-05-28 11:08:35 -04:00
initial_count = self.model.objects.count()
2020-06-11 16:12:50 -04:00
# Assign unconstrained permission
obj_perm = ObjectPermission(
actions=['add']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try GET with model-level permission
self.assertHttpStatus(self.client.get(self._get_url('add')), 200)
# Try POST with model-level permission
request = {
'path': self._get_url('add'),
'data': post_data(self.form_data),
}
self.assertHttpStatus(self.client.post(**request), 302)
self.assertEqual(initial_count + 1, self.model.objects.count())
self.assertInstanceEqual(self.model.objects.order_by('pk').last(), self.form_data)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_create_object_with_constrained_permission(self):
initial_count = self.model.objects.count()
2020-06-11 16:12:50 -04:00
# Assign constrained permission
obj_perm = ObjectPermission(
2020-06-11 16:12:50 -04:00
constraints={'pk': 0}, # Dummy permission to deny all
actions=['add']
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
# Try GET with object-level permission
self.assertHttpStatus(self.client.get(self._get_url('add')), 200)
2020-06-11 16:12:50 -04:00
# Try to create an object (not permitted)
request = {
'path': self._get_url('add'),
'data': post_data(self.form_data),
}
2020-06-11 16:12:50 -04:00
self.assertHttpStatus(self.client.post(**request), 200)
self.assertEqual(initial_count, self.model.objects.count()) # Check that no object was created
2020-06-11 16:12:50 -04:00
# Update the ObjectPermission to allow creation
obj_perm.constraints = {'pk__gt': 0}
obj_perm.save()
2020-06-11 16:12:50 -04:00
# Try to create an object (permitted)
request = {
'path': self._get_url('add'),
'data': post_data(self.form_data),
}
2020-06-11 16:12:50 -04:00
self.assertHttpStatus(self.client.post(**request), 302)
self.assertEqual(initial_count + 1, self.model.objects.count())
self.assertInstanceEqual(self.model.objects.order_by('pk').last(), self.form_data)
class EditObjectViewTestCase(ModelViewTestCase):
"""
Edit a single existing instance.
2020-06-11 16:12:50 -04:00
:form_data: Data to be used when updating the first existing object.
"""
form_data = {}
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_edit_object_without_permission(self):
instance = self.model.objects.first()
# Try GET without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(self._get_url('edit', instance)), 403)
# Try POST without permission
request = {
'path': self._get_url('edit', instance),
'data': post_data(self.form_data),
}
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(**request), 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_edit_object_with_permission(self):
2020-05-28 11:08:35 -04:00
instance = self.model.objects.first()
# Assign model-level permission
obj_perm = ObjectPermission(
actions=['change']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try GET with model-level permission
self.assertHttpStatus(self.client.get(self._get_url('edit', instance)), 200)
# Try POST with model-level permission
request = {
'path': self._get_url('edit', instance),
'data': post_data(self.form_data),
}
self.assertHttpStatus(self.client.post(**request), 302)
self.assertInstanceEqual(self.model.objects.get(pk=instance.pk), self.form_data)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_edit_object_with_constrained_permission(self):
instance1, instance2 = self.model.objects.all()[:2]
2020-06-11 16:12:50 -04:00
# Assign constrained permission
obj_perm = ObjectPermission(
2020-06-03 09:43:46 -04:00
constraints={'pk': instance1.pk},
actions=['change']
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
# Try GET with a permitted object
self.assertHttpStatus(self.client.get(self._get_url('edit', instance1)), 200)
# Try GET with a non-permitted object
self.assertHttpStatus(self.client.get(self._get_url('edit', instance2)), 404)
# Try to edit a permitted object
request = {
'path': self._get_url('edit', instance1),
'data': post_data(self.form_data),
}
self.assertHttpStatus(self.client.post(**request), 302)
self.assertInstanceEqual(self.model.objects.get(pk=instance1.pk), self.form_data)
# Try to edit a non-permitted object
request = {
'path': self._get_url('edit', instance2),
'data': post_data(self.form_data),
}
self.assertHttpStatus(self.client.post(**request), 404)
class DeleteObjectViewTestCase(ModelViewTestCase):
"""
Delete a single instance.
"""
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_delete_object_without_permission(self):
instance = self.model.objects.first()
# Try GET without permission
with disable_warnings('django.request'):
2020-05-22 16:33:56 -04:00
self.assertHttpStatus(self.client.get(self._get_url('delete', instance)), 403)
# Try POST without permission
request = {
'path': self._get_url('delete', instance),
'data': post_data({'confirm': True}),
}
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(**request), 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_delete_object_with_permission(self):
2020-05-28 11:08:35 -04:00
instance = self.model.objects.first()
# Assign model-level permission
obj_perm = ObjectPermission(
actions=['delete']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try GET with model-level permission
self.assertHttpStatus(self.client.get(self._get_url('delete', instance)), 200)
# Try POST with model-level permission
request = {
'path': self._get_url('delete', instance),
'data': post_data({'confirm': True}),
}
self.assertHttpStatus(self.client.post(**request), 302)
with self.assertRaises(ObjectDoesNotExist):
self.model.objects.get(pk=instance.pk)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_delete_object_with_constrained_permission(self):
instance1, instance2 = self.model.objects.all()[:2]
# Assign object-level permission
obj_perm = ObjectPermission(
2020-06-03 09:43:46 -04:00
constraints={'pk': instance1.pk},
actions=['delete']
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
# Try GET with a permitted object
self.assertHttpStatus(self.client.get(self._get_url('delete', instance1)), 200)
# Try GET with a non-permitted object
self.assertHttpStatus(self.client.get(self._get_url('delete', instance2)), 404)
# Try to delete a permitted object
request = {
'path': self._get_url('delete', instance1),
'data': post_data({'confirm': True}),
}
self.assertHttpStatus(self.client.post(**request), 302)
with self.assertRaises(ObjectDoesNotExist):
self.model.objects.get(pk=instance1.pk)
# Try to delete a non-permitted object
request = {
'path': self._get_url('delete', instance2),
'data': post_data({'confirm': True}),
}
self.assertHttpStatus(self.client.post(**request), 404)
self.assertTrue(self.model.objects.filter(pk=instance2.pk).exists())
class ListObjectsViewTestCase(ModelViewTestCase):
"""
Retrieve multiple instances.
"""
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
def test_list_objects_anonymous(self):
# Make the request as an unauthenticated user
self.client.logout()
response = self.client.get(self._get_url('list'))
self.assertHttpStatus(response, 200)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_list_objects_without_permission(self):
# Try GET without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.get(self._get_url('list')), 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_list_objects_with_permission(self):
2020-05-28 11:08:35 -04:00
# Add model-level permission
obj_perm = ObjectPermission(
actions=['view']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try GET with model-level permission
self.assertHttpStatus(self.client.get(self._get_url('list')), 200)
# Built-in CSV export
if hasattr(self.model, 'csv_headers'):
response = self.client.get('{}?export'.format(self._get_url('list')))
self.assertHttpStatus(response, 200)
self.assertEqual(response.get('Content-Type'), 'text/csv')
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_list_objects_with_constrained_permission(self):
instance1, instance2 = self.model.objects.all()[:2]
# Add object-level permission
obj_perm = ObjectPermission(
2020-06-03 09:43:46 -04:00
constraints={'pk': instance1.pk},
actions=['view']
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
# Try GET with object-level permission
2020-06-11 16:12:50 -04:00
response = self.client.get(self._get_url('list'))
self.assertHttpStatus(response, 200)
content = str(response.content)
if hasattr(self.model, 'name'):
self.assertIn(instance1.name, content)
self.assertNotIn(instance2.name, content)
else:
self.assertIn(instance1.get_absolute_url(), content)
self.assertNotIn(instance2.get_absolute_url(), content)
class BulkCreateObjectsViewTestCase(ModelViewTestCase):
"""
Create multiple instances using a single form. Expects the creation of three new instances by default.
2020-06-11 16:12:50 -04:00
:bulk_create_count: The number of objects expected to be created (default: 3).
:bulk_create_data: A dictionary of data to be used for bulk object creation.
"""
bulk_create_count = 3
bulk_create_data = {}
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_bulk_create_objects_without_permission(self):
request = {
'path': self._get_url('add'),
'data': post_data(self.bulk_create_data),
}
2020-06-11 16:12:50 -04:00
# Try POST without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(**request), 403)
2020-06-11 16:12:50 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_bulk_create_objects_with_permission(self):
initial_count = self.model.objects.count()
request = {
'path': self._get_url('add'),
'data': post_data(self.bulk_create_data),
}
# Assign non-constrained permission
obj_perm = ObjectPermission(
actions=['add'],
)
2020-05-27 16:53:30 -04:00
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-27 16:53:30 -04:00
2020-06-11 16:12:50 -04:00
# Bulk create objects
response = self.client.post(**request)
self.assertHttpStatus(response, 302)
2020-06-11 16:12:50 -04:00
self.assertEqual(initial_count + self.bulk_create_count, self.model.objects.count())
for instance in self.model.objects.order_by('-pk')[:self.bulk_create_count]:
self.assertInstanceEqual(instance, self.bulk_create_data)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_bulk_create_objects_with_constrained_permission(self):
initial_count = self.model.objects.count()
request = {
'path': self._get_url('add'),
'data': post_data(self.bulk_create_data),
}
# Assign constrained permission
obj_perm = ObjectPermission(
actions=['add'],
constraints={'pk': 0} # Dummy constraint to deny all
)
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
# Attempt to make the request with unmet constraints
self.assertHttpStatus(self.client.post(**request), 200)
self.assertEqual(self.model.objects.count(), initial_count)
# Update the ObjectPermission to allow creation
obj_perm.constraints = {'pk__gt': 0} # Dummy constraint to allow all
obj_perm.save()
2020-06-11 16:12:50 -04:00
response = self.client.post(**request)
self.assertHttpStatus(response, 302)
self.assertEqual(initial_count + self.bulk_create_count, self.model.objects.count())
for instance in self.model.objects.order_by('-pk')[:self.bulk_create_count]:
self.assertInstanceEqual(instance, self.bulk_create_data)
2020-05-22 14:57:35 -04:00
class BulkImportObjectsViewTestCase(ModelViewTestCase):
"""
Create multiple instances from imported data.
2020-06-11 16:12:50 -04:00
:csv_data: A list of CSV-formatted lines (starting with the headers) to be used for bulk object import.
"""
csv_data = ()
def _get_csv_data(self):
return '\n'.join(self.csv_data)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_bulk_import_objects_without_permission(self):
data = {
'csv': self._get_csv_data(),
}
# Test GET without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.get(self._get_url('import')), 403)
# Try POST without permission
response = self.client.post(self._get_url('import'), data)
with disable_warnings('django.request'):
self.assertHttpStatus(response, 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_bulk_import_objects_with_permission(self):
2020-05-28 11:08:35 -04:00
initial_count = self.model.objects.count()
data = {
'csv': self._get_csv_data(),
}
# Assign model-level permission
obj_perm = ObjectPermission(
actions=['add']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try GET with model-level permission
self.assertHttpStatus(self.client.get(self._get_url('import')), 200)
# Test POST with permission
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200)
self.assertEqual(self.model.objects.count(), initial_count + len(self.csv_data) - 1)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_bulk_import_objects_with_constrained_permission(self):
initial_count = self.model.objects.count()
data = {
'csv': self._get_csv_data(),
}
2020-06-11 16:12:50 -04:00
# Assign constrained permission
obj_perm = ObjectPermission(
2020-06-11 16:12:50 -04:00
constraints={'pk': 0}, # Dummy permission to deny all
actions=['add']
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-06-11 16:12:50 -04:00
# Attempt to import non-permitted objects
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200)
2020-06-11 16:12:50 -04:00
self.assertEqual(self.model.objects.count(), initial_count)
# Update permission constraints
obj_perm.constraints = {'pk__gt': 0} # Dummy permission to allow all
obj_perm.save()
2020-02-03 10:04:09 -05:00
2020-06-11 16:12:50 -04:00
# Import permitted objects
self.assertHttpStatus(self.client.post(self._get_url('import'), data), 200)
self.assertEqual(self.model.objects.count(), initial_count + len(self.csv_data) - 1)
class BulkEditObjectsViewTestCase(ModelViewTestCase):
"""
Edit multiple instances.
2020-06-11 16:12:50 -04:00
:bulk_edit_data: A dictionary of data to be used when bulk editing a set of objects. This data should differ
from that used for initial object creation within setUpTestData().
"""
bulk_edit_data = {}
2020-02-03 13:32:53 -05:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_bulk_edit_objects_without_permission(self):
pk_list = self.model.objects.values_list('pk', flat=True)[:3]
data = {
'pk': pk_list,
'_apply': True, # Form button
}
2020-02-03 13:32:53 -05:00
# Test GET without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.get(self._get_url('bulk_edit')), 403)
# Try POST without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(self._get_url('bulk_edit'), data), 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_bulk_edit_objects_with_permission(self):
2020-05-28 11:08:35 -04:00
pk_list = self.model.objects.values_list('pk', flat=True)[:3]
data = {
'pk': pk_list,
'_apply': True, # Form button
}
# Append the form data to the request
data.update(post_data(self.bulk_edit_data))
# Assign model-level permission
obj_perm = ObjectPermission(
actions=['change']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try POST with model-level permission
self.assertHttpStatus(self.client.post(self._get_url('bulk_edit'), data), 302)
for i, instance in enumerate(self.model.objects.filter(pk__in=pk_list)):
self.assertInstanceEqual(instance, self.bulk_edit_data)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_bulk_edit_objects_with_constrained_permission(self):
initial_instances = self.model.objects.all()[:3]
pk_list = list(self.model.objects.values_list('pk', flat=True)[:3])
data = {
'pk': pk_list,
'_apply': True, # Form button
}
# Append the form data to the request
data.update(post_data(self.bulk_edit_data))
2020-06-11 16:12:50 -04:00
# Dynamically determine a constraint that will *not* be matched by the updated objects.
attr_name = list(self.bulk_edit_data.keys())[0]
field = self.model._meta.get_field(attr_name)
value = field.value_from_object(self.model.objects.first())
# Assign constrained permission
obj_perm = ObjectPermission(
2020-06-11 16:12:50 -04:00
constraints={attr_name: value},
actions=['change']
2020-02-03 13:32:53 -05:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-02-03 13:32:53 -05:00
2020-06-11 16:12:50 -04:00
# Attempt to bulk edit permitted objects into a non-permitted state
response = self.client.post(self._get_url('bulk_edit'), data)
self.assertHttpStatus(response, 200)
# Update permission constraints
obj_perm.constraints = {'pk__gt': 0}
obj_perm.save()
# Bulk edit permitted objects
self.assertHttpStatus(self.client.post(self._get_url('bulk_edit'), data), 302)
2020-02-03 13:32:53 -05:00
for i, instance in enumerate(self.model.objects.filter(pk__in=pk_list)):
self.assertInstanceEqual(instance, self.bulk_edit_data)
2020-02-03 13:32:53 -05:00
class BulkDeleteObjectsViewTestCase(ModelViewTestCase):
"""
Delete multiple instances.
"""
2020-02-03 10:04:09 -05:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
def test_bulk_delete_objects_without_permission(self):
pk_list = self.model.objects.values_list('pk', flat=True)[:3]
data = {
'pk': pk_list,
'confirm': True,
'_confirm': True, # Form button
2020-02-03 10:04:09 -05:00
}
# Test GET without permission
2020-02-03 10:04:09 -05:00
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.get(self._get_url('bulk_delete')), 403)
2020-02-03 10:04:09 -05:00
# Try POST without permission
with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(self._get_url('bulk_delete'), data), 403)
2020-05-28 11:08:35 -04:00
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_bulk_delete_objects_with_permission(self):
2020-05-28 11:08:35 -04:00
pk_list = self.model.objects.values_list('pk', flat=True)
data = {
'pk': pk_list,
'confirm': True,
'_confirm': True, # Form button
}
2020-06-11 16:12:50 -04:00
# Assign unconstrained permission
obj_perm = ObjectPermission(
actions=['delete']
2020-05-28 11:08:35 -04:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-05-28 11:08:35 -04:00
# Try POST with model-level permission
self.assertHttpStatus(self.client.post(self._get_url('bulk_delete'), data), 302)
self.assertEqual(self.model.objects.count(), 0)
@override_settings(EXEMPT_VIEW_PERMISSIONS=[])
2020-06-11 16:12:50 -04:00
def test_bulk_delete_objects_with_constrained_permission(self):
initial_count = self.model.objects.count()
pk_list = self.model.objects.values_list('pk', flat=True)
data = {
'pk': pk_list,
'confirm': True,
'_confirm': True, # Form button
}
2020-06-11 16:12:50 -04:00
# Assign constrained permission
obj_perm = ObjectPermission(
2020-06-11 16:12:50 -04:00
constraints={'pk': 0}, # Dummy permission to deny all
actions=['delete']
2020-02-03 10:04:09 -05:00
)
obj_perm.save()
obj_perm.users.add(self.user)
2020-06-03 09:27:20 -04:00
obj_perm.object_types.add(ContentType.objects.get_for_model(self.model))
2020-02-03 10:04:09 -05:00
2020-06-11 16:12:50 -04:00
# Attempt to bulk delete non-permitted objects
self.assertHttpStatus(self.client.post(self._get_url('bulk_delete'), data), 302)
2020-06-11 16:12:50 -04:00
self.assertEqual(self.model.objects.count(), initial_count)
# Update permission constraints
obj_perm.constraints = {'pk__gt': 0} # Dummy permission to allow all
obj_perm.save()
2020-06-11 16:12:50 -04:00
# Bulk delete permitted objects
self.assertHttpStatus(self.client.post(self._get_url('bulk_delete'), data), 302)
self.assertEqual(self.model.objects.count(), 0)
class PrimaryObjectViewTestCase(
GetObjectViewTestCase,
CreateObjectViewTestCase,
EditObjectViewTestCase,
DeleteObjectViewTestCase,
ListObjectsViewTestCase,
2020-05-22 14:57:35 -04:00
BulkImportObjectsViewTestCase,
BulkEditObjectsViewTestCase,
BulkDeleteObjectsViewTestCase,
):
"""
TestCase suitable for testing all standard View functions for primary objects
"""
maxDiff = None
class OrganizationalObjectViewTestCase(
CreateObjectViewTestCase,
EditObjectViewTestCase,
ListObjectsViewTestCase,
2020-05-22 14:57:35 -04:00
BulkImportObjectsViewTestCase,
BulkDeleteObjectsViewTestCase,
):
"""
TestCase suitable for all organizational objects
"""
maxDiff = None
class DeviceComponentTemplateViewTestCase(
EditObjectViewTestCase,
DeleteObjectViewTestCase,
BulkCreateObjectsViewTestCase,
BulkEditObjectsViewTestCase,
BulkDeleteObjectsViewTestCase,
):
"""
TestCase suitable for testing device component template models (ConsolePortTemplates, InterfaceTemplates, etc.)
"""
maxDiff = None
class DeviceComponentViewTestCase(
EditObjectViewTestCase,
DeleteObjectViewTestCase,
ListObjectsViewTestCase,
BulkCreateObjectsViewTestCase,
2020-05-22 14:57:35 -04:00
BulkImportObjectsViewTestCase,
BulkEditObjectsViewTestCase,
BulkDeleteObjectsViewTestCase,
):
"""
TestCase suitable for testing device component models (ConsolePorts, Interfaces, etc.)
"""
maxDiff = None