labelbase/django/attachments/tests/test_views.py
2024-04-14 01:59:27 +02:00

273 lines
10 KiB
Python

import os
import json
import mock
from http import HTTPStatus
from django.urls import reverse
from ..models import Attachment
from .base import BaseTestCase
from .testapp.models import ModelWithUuidPk
class ViewTestCase(BaseTestCase):
def test_empty_post_to_form_wont_create_attachment(self):
add_url = reverse(
"attachments:add",
kwargs={
"app_label": "testapp",
"model_name": "testmodel",
"pk": self.obj.pk,
},
)
response = self.client.post(add_url)
self.assertEqual(302, response.status_code)
self.assertEqual(Attachment.objects.count(), 0)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 0
)
def test_invalid_model_wont_fail(self):
add_url = reverse(
"attachments:add",
kwargs={
"app_label": "thisdoes",
"model_name": "notexist",
"pk": self.obj.pk,
},
)
response = self.client.post(add_url)
self.assertEqual(302, response.status_code)
self.assertEqual(Attachment.objects.count(), 0)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 0
)
def test_invalid_attachment_wont_fail(self):
self.client.login(**self.cred_jon)
self._upload_testfile(file_obj="Not a UploadedFile object")
self.assertEqual(Attachment.objects.count(), 0)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 0
)
def test_upload_size_less_than_limit(self):
# NOTE: in all of the other tests there's no limit specified
# so they will cover the branch where the setting is missing
with self.settings(FILE_UPLOAD_MAX_SIZE=1024):
self.client.login(**self.cred_jon)
self._upload_testfile()
self.assertEqual(Attachment.objects.count(), 1)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 1
)
def test_upload_size_more_than_limit(self):
# we set a limit of 1 byte b/c the file used for testing
# is very small
with self.settings(FILE_UPLOAD_MAX_SIZE=1):
self.client.login(**self.cred_jon)
response = self._upload_testfile()
self.assertContains(response, "File exceeds maximum size of 1")
self.assertEqual(Attachment.objects.count(), 0)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 0
)
def test_upload_without_permission(self):
"""
Remove the 'add permission' and try to upload a file.
"""
self.jon.user_permissions.remove(self.add_permission)
self.client.login(**self.cred_jon)
self._upload_testfile()
self.assertEqual(Attachment.objects.count(), 0)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 0
)
def test_upload_with_permission(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
self.assertEqual(Attachment.objects.count(), 1)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 1
)
def test_unauthed_user_cant_delete_attachment(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
att = Attachment.objects.first()
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": att.pk}
)
self.client.logout()
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 1)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 1
)
def test_author_can_delete_attachment(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
att = Attachment.objects.first()
file_path = att.attachment_file.path
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": att.pk}
)
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 0)
# file on disk is still present b/c setting not specified
self.assertTrue(os.path.exists(file_path))
def test_author_cant_delete_attachment_if_no_delete_permission(self):
self.jon.user_permissions.remove(self.del_permission)
self.client.login(**self.cred_jon)
self._upload_testfile()
att = Attachment.objects.first()
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": att.pk}
)
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 1)
def test_author_cant_delete_others_attachment(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
obj1 = Attachment.objects.order_by("-created")[0]
self.client.login(**self.cred_jane)
self._upload_testfile()
obj2 = Attachment.objects.order_by("-created")[0]
self.assertNotEqual(obj1, obj2)
# Jon can't delete Janes attachment
self.client.login(**self.cred_jon)
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": obj2.pk}
)
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 2)
def test_author_can_delete_others_attachment_with_permission(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
obj1 = Attachment.objects.order_by("-created")[0]
path1 = obj1.attachment_file.path
self.client.login(**self.cred_jane)
self._upload_testfile()
obj2 = Attachment.objects.order_by("-created")[0]
path2 = obj2.attachment_file.path
self.assertNotEqual(obj1, obj2)
# Jon has the `delete_foreign_attachments` permission so he can
# delete Janes attachment
self.jon.user_permissions.add(self.del_foreign_permission)
self.client.login(**self.cred_jon)
# explicitly set the delete setting to False to
# cover that branch as well
with self.settings(DELETE_ATTACHMENT_FILE=False):
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": obj2.pk}
)
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 1)
self.assertTrue(os.path.exists(path1))
self.assertTrue(os.path.exists(path2))
def test_delete_removes_files_from_disk_if_settings(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
att = Attachment.objects.first()
file_path = att.attachment_file.path
with self.settings(DELETE_ATTACHMENTS_FROM_DISK=True):
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": att.pk}
)
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 0)
self.assertFalse(os.path.exists(file_path))
def test_delete_does_not_raise_if_settings_and_file_missing(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
att = Attachment.objects.first()
file_path = att.attachment_file.path
# remove the file before hand
os.remove(file_path)
with self.settings(DELETE_ATTACHMENTS_FROM_DISK=True):
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": att.pk}
)
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 0)
self.assertFalse(os.path.exists(file_path))
def test_delete_does_not_raise_if_os_remove_raises(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
att = Attachment.objects.first()
with mock.patch("attachments.views.os.remove") as _mock:
_mock.side_effect = OSError("Test file does not exist")
with self.settings(DELETE_ATTACHMENTS_FROM_DISK=True):
del_url = reverse(
"attachments:delete", kwargs={"attachment_pk": att.pk}
)
self.client.get(del_url, follow=True)
self.assertEqual(Attachment.objects.count(), 0)
# NOTE: we don't assert the file path here because
# the mock which raises will not actually delete it
class UUIDTestCase(BaseTestCase):
target_model_class = ModelWithUuidPk
def test_upload_with_permission(self):
self.client.login(**self.cred_jon)
self._upload_testfile()
self.assertEqual(Attachment.objects.count(), 1)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 1
)
class CustomValidatorsTestCase(BaseTestCase):
def test_deny_specific_content(self):
self.client.login(**self.cred_jon)
response = self._upload_testfile(file_content=b"<xml>this is not allowed</xml>")
self.assertContains(response, "XML is forbidden")
self.assertEqual(Attachment.objects.count(), 0)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 0
)
def test_form_errors_are_returned_as_json(self):
self.client.login(**self.cred_jon)
response = self._upload_testfile(
file_content=b"<xml>this is not allowed</xml>",
HTTP_X_RETURN_FORM_ERRORS=True,
)
self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
self.assertEqual(response.headers.get("Content-Type"), "application/json")
# this should be a dict
errors = json.loads(response.content)
# note: field errors are a list of string messages
self.assertEqual(errors["attachment_file"], ["XML is forbidden"])
self.assertEqual(Attachment.objects.count(), 0)
self.assertEqual(
Attachment.objects.attachments_for_object(self.obj).count(), 0
)