|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +"""Browser views tests for this package.""" |
| 3 | +from imio.esign.browser.views import DownloadFileView |
| 4 | +from imio.esign.testing import IMIO_ESIGN_FUNCTIONAL_TESTING |
| 5 | +from imio.pyutils.utils import shortuid_encode_id |
| 6 | +from plone import api |
| 7 | +from plone.app.testing import logout |
| 8 | +from plone.app.testing import setRoles |
| 9 | +from plone.app.testing import TEST_USER_ID |
| 10 | +from plone.namedfile.file import NamedBlobFile |
| 11 | +from plone.namedfile.file import NamedBlobImage |
| 12 | +from plone.testing import z2 |
| 13 | + |
| 14 | +import collective.iconifiedcategory |
| 15 | +import os |
| 16 | +import transaction |
| 17 | +import unittest |
| 18 | + |
| 19 | + |
| 20 | +class TestDownloadFileView(unittest.TestCase): |
| 21 | + """Test DownloadFileView browser view.""" |
| 22 | + |
| 23 | + layer = IMIO_ESIGN_FUNCTIONAL_TESTING |
| 24 | + |
| 25 | + def setUp(self): |
| 26 | + """Set up test fixtures.""" |
| 27 | + self.app = self.layer["app"] |
| 28 | + self.portal = self.layer["portal"] |
| 29 | + self.request = self.layer["request"] |
| 30 | + setRoles(self.portal, TEST_USER_ID, ["Manager"]) |
| 31 | + |
| 32 | + # Setup content category configuration (like in test_utils.py) |
| 33 | + at_folder = api.content.create( |
| 34 | + container=self.portal, |
| 35 | + id="annexes_types", |
| 36 | + title="Annexes Types", |
| 37 | + type="ContentCategoryConfiguration", |
| 38 | + exclude_from_nav=True, |
| 39 | + ) |
| 40 | + category_group = api.content.create( |
| 41 | + type="ContentCategoryGroup", |
| 42 | + title="Annexes", |
| 43 | + container=at_folder, |
| 44 | + id="annexes", |
| 45 | + ) |
| 46 | + icon_path = os.path.join(os.path.dirname(collective.iconifiedcategory.__file__), "tests", "icône1.png") |
| 47 | + with open(icon_path, "rb") as fl: |
| 48 | + api.content.create( |
| 49 | + type="ContentCategory", |
| 50 | + title="To sign", |
| 51 | + container=category_group, |
| 52 | + icon=NamedBlobImage(fl.read(), filename=u"icône1.png"), |
| 53 | + id="to_sign", |
| 54 | + predefined_title="To be signed", |
| 55 | + to_sign=True, |
| 56 | + show_preview=False, |
| 57 | + ) |
| 58 | + |
| 59 | + # Create a folder to hold test annexes |
| 60 | + self.folder = api.content.create( |
| 61 | + container=self.portal, |
| 62 | + type="Folder", |
| 63 | + id="test_folder", |
| 64 | + title="Test Folder", |
| 65 | + ) |
| 66 | + |
| 67 | + # Create test annexes with NamedBlobFile (Plone 4.3 Archetypes) |
| 68 | + tests_dir = os.path.dirname(__file__) |
| 69 | + pdf_file = "annex1.pdf" |
| 70 | + with open(os.path.join(tests_dir, pdf_file), "rb") as f: |
| 71 | + file_data = f.read() |
| 72 | + self.test_annex = api.content.create( |
| 73 | + container=self.folder, |
| 74 | + type="annex", |
| 75 | + id="test_annex", |
| 76 | + title="Test Annex", |
| 77 | + content_category="to_sign", |
| 78 | + file=NamedBlobFile( |
| 79 | + data=file_data, |
| 80 | + filename=u"test_document__uid.pdf", |
| 81 | + contentType="application/pdf" |
| 82 | + ), |
| 83 | + ) |
| 84 | + |
| 85 | + self.file_uid = self.test_annex.UID() |
| 86 | + self.encoded_uid = shortuid_encode_id(self.file_uid, separator="-", block_size=5) |
| 87 | + |
| 88 | + # Commit transaction for functional testing |
| 89 | + transaction.commit() |
| 90 | + |
| 91 | + def test_download_file_view(self): |
| 92 | + """Test DownloadFileView with various scenarios.""" |
| 93 | + logout() # anonymous usage |
| 94 | + |
| 95 | + # View exists and can be instantiated |
| 96 | + view = api.content.get_view("download-file", self.portal, self.request) |
| 97 | + self.assertIsInstance(view, DownloadFileView) |
| 98 | + view = DownloadFileView(self.portal, self.request) |
| 99 | + self.assertEqual(view.file_id, None) |
| 100 | + self.assertEqual(view.shortuid_separator, "-") |
| 101 | + self.assertEqual(view.named_blob_file_attribute, "file") |
| 102 | + |
| 103 | + # Download file without UID |
| 104 | + view = DownloadFileView(self.portal, self.request) |
| 105 | + result = view() |
| 106 | + self.assertIn("A file identifier must be passed in the url", result) |
| 107 | + # invalid UID format |
| 108 | + view.file_id = "$$$" |
| 109 | + result = view() |
| 110 | + self.assertIn("This file identifier is not correct", result) |
| 111 | + # valid format but non-existent UID |
| 112 | + view.file_id = "aabbccddee" |
| 113 | + result = view() |
| 114 | + self.assertIn("The corresponding file identifier cannot be retrieved", result) |
| 115 | + # download from object without file attribute |
| 116 | + folder_uid = self.folder.UID() |
| 117 | + encoded_folder_uid = shortuid_encode_id(folder_uid, separator="-", block_size=5) |
| 118 | + view.file_id = encoded_folder_uid |
| 119 | + result = view() |
| 120 | + self.assertIn("The corresponding file content cannot be retrieved", result) |
| 121 | + |
| 122 | + # Download file with valid UID |
| 123 | + view.file_id = self.encoded_uid |
| 124 | + result = view() |
| 125 | + # Check that we got binary data (the file content) |
| 126 | + self.assertIsInstance(result, str) # In Python 2, binary data is str |
| 127 | + self.assertTrue(len(result) > 0) |
| 128 | + self.assertTrue(result.startswith(b"%PDF") or result.startswith("%PDF")) |
| 129 | + # Check response headers |
| 130 | + response = self.request.RESPONSE |
| 131 | + self.assertIn("application/pdf", response.getHeader("Content-Type")) |
| 132 | + self.assertIn("inline", response.getHeader("Content-Disposition")) |
| 133 | + self.assertIn("test_document.pdf", response.getHeader("Content-Disposition")) |
| 134 | + self.assertTrue(int(response.getHeader("Content-Length")) > 0) |
| 135 | + |
| 136 | + # Test URL traversal mechanism |
| 137 | + browser = z2.Browser(self.app) |
| 138 | + portal_url = self.portal.absolute_url() |
| 139 | + browser.open("{}/download-file/{}".format(portal_url, "aabbccddee")) |
| 140 | + self.assertIn("The corresponding file identifier cannot be retrieved (aabbccddee)", browser.contents) |
| 141 | + browser.open("{}/download-file/{}".format(portal_url, "aabbccddee/ffgghh")) |
| 142 | + self.assertIn("The corresponding file identifier cannot be retrieved (aabbccddee)", browser.contents) |
| 143 | + browser.open("{}/download-file/{}".format(portal_url, "aabbccddee?param=value")) |
| 144 | + self.assertIn("The corresponding file identifier cannot be retrieved (aabbccddee)", browser.contents) |
0 commit comments