Reuse existing files as previews, icons or featured images #161

Merged
Anna Sirota merged 11 commits from files-allow-reuse-between-ext into main 2024-06-04 12:23:26 +02:00
2 changed files with 98 additions and 43 deletions
Showing only changes of commit aa79172b50 - Show all commits

View File

@ -21,23 +21,38 @@ class DeleteTest(TestCase):
def test_unlisted_unrated_extension_can_be_deleted_by_author(self):
self.maxDiff = None
reused_image = FileFactory(
type=files.models.File.TYPES.IMAGE,
original_name='extension_feature_image.png',
source='images/b0/b03fa981527593fbe15b28cf37c020220c3d83021999eab036b87f3bca9c9168.png',
)
version = create_version(
file__status=files.models.File.STATUSES.AWAITING_REVIEW,
ratings=[],
extension__icon=FileFactory(
type=files.models.File.TYPES.IMAGE,
original_name='extension_icon_final.png',
source='images/8a/8a01102de8573d50bbc90033f55f232b7cacc4f1eb3e3c3d851615841d2956e1.png',
),
extension__featured_image=reused_image,
extension__previews=[
reused_image,
FileFactory(
type=files.models.File.TYPES.IMAGE,
original_name='extension_preview_001.png',
source='images/b0/b03fa981527593fbe15b28cf37c020220c3d83021999eab036b87f3bca9c9168.png',
)
),
],
)
extension = version.extension
version_file = version.file
icon = extension.icon
featured_image = extension.featured_image
self.assertEqual(version_file.get_status_display(), 'Awaiting Review')
self.assertEqual(extension.get_status_display(), 'Draft')
self.assertFalse(extension.is_listed)
self.assertEqual(extension.cannot_be_deleted_reasons, [])
preview_file = extension.previews.first()
preview_file = extension.previews.last()
self.assertIsNotNone(preview_file)
# Create some ApprovalActivity as well
moderator = create_moderator()
@ -55,11 +70,11 @@ class DeleteTest(TestCase):
repr,
[
version_file,
preview_file,
file_validation,
extension,
approval_activity,
file_validation,
preview_file.preview,
preview_file.preview_set.first(),
reused_image.preview_set.first(),
version,
],
)
@ -78,12 +93,16 @@ class DeleteTest(TestCase):
version.refresh_from_db()
with self.assertRaises(files.models.File.DoesNotExist):
version_file.refresh_from_db()
with self.assertRaises(files.models.File.DoesNotExist):
# Preview files aren't deleted: they might be re-uploaded shortly and should be looked up by hash
preview_file.refresh_from_db()
icon.refresh_from_db()
featured_image.refresh_from_db()
self.assertIsNone(extensions.models.Extension.objects.filter(pk=extension.pk).first())
self.assertIsNone(extensions.models.Version.objects.filter(pk=version.pk).first())
self.assertIsNone(files.models.File.objects.filter(pk=version_file.pk).first())
self.assertIsNone(files.models.File.objects.filter(pk=preview_file.pk).first())
self.assertIsNotNone(files.models.File.objects.filter(pk=preview_file.pk).first())
self.assertIsNotNone(files.models.File.objects.filter(pk=icon.pk).first())
self.assertIsNotNone(files.models.File.objects.filter(pk=featured_image.pk).first())
# Check that each of the deleted records was logged
deletion_log_entries_q = LogEntry.objects.filter(action_flag=DELETION)

View File

@ -81,7 +81,7 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
self.assertEqual(File.objects.filter(type=File.TYPES.IMAGE).count(), 1)
self.assertEqual(extension.previews.count(), 1)
file1 = extension.previews.all()[0]
self.assertEqual(file1.preview.caption, 'First Preview Caption Text')
self.assertEqual(file1.preview_set.first().caption, 'First Preview Caption Text')
self.assertEqual(
file1.original_hash,
'sha256:643e15eb6c4831173bbcf71b8c85efc70cf3437321bf2559b39aa5e9acfd5340',
@ -125,7 +125,7 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
extension.refresh_from_db()
self.assertEqual(extension.previews.count(), 1)
video_file = extension.previews.all()[0]
self.assertEqual(video_file.preview.caption, 'First Preview Caption Text')
self.assertEqual(video_file.preview_set.first().caption, 'First Preview Caption Text')
self._test_file_properties(
video_file,
content_type='video/mp4',
@ -169,8 +169,8 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
self.assertEqual(extension.previews.count(), 2)
file1 = extension.previews.all()[0]
file2 = extension.previews.all()[1]
self.assertEqual(file1.preview.caption, 'First Preview Caption Text')
self.assertEqual(file2.preview.caption, 'Second Preview Caption Text')
self.assertEqual(file1.preview_set.first().caption, 'First Preview Caption Text')
self.assertEqual(file2.preview_set.first().caption, 'Second Preview Caption Text')
self.assertEqual(
file1.original_hash,
'sha256:643e15eb6c4831173bbcf71b8c85efc70cf3437321bf2559b39aa5e9acfd5340',
@ -222,8 +222,10 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
_get_all_form_errors(response),
)
def test_post_upload_validation_error_duplicate_images(self):
def test_post_upload_duplicate_preview_files_ignored(self):
extension = create_approved_version().extension
images_count_before = File.objects.filter(type=File.TYPES.IMAGE).count()
self.assertEqual(extension.previews.count(), 0)
data = {
**POST_DATA,
@ -243,31 +245,23 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
}
response = self.client.post(url, {**data, **files})
self.assertEqual(response.status_code, 200)
self.maxDiff = None
self.assertEqual(response.status_code, 302)
# One image was uploaded successfully
self.assertEqual(
[
response.context['add_preview_formset'].forms[0].errors,
response.context['add_preview_formset'].forms[1].errors,
response.context['add_preview_formset'].non_form_errors(),
],
[
{},
{
'__all__': ['Please correct the duplicate values below.'],
'source': ['Please select another file instead of the duplicate.'],
},
['Please select another file instead of the duplicate'],
],
File.objects.filter(type=File.TYPES.IMAGE).count(), images_count_before + 1
)
self.assertEqual(extension.previews.count(), 1)
def test_post_upload_validation_error_image_already_exists(self):
FileFactory(
def test_post_upload_existing_image_file_linked_to_extension(self):
file = FileFactory(
type=File.TYPES.IMAGE,
original_hash='sha256:643e15eb6c4831173bbcf71b8c85efc70cf3437321bf2559b39aa5e9acfd5340',
hash='sha256:643e15eb6c4831173bbcf71b8c85efc70cf3437321bf2559b39aa5e9acfd5340',
source='file/original_image_source.jpg',
)
extension = create_approved_version().extension
images_count_before = File.objects.filter(type=File.TYPES.IMAGE).count()
self.assertEqual(extension.previews.count(), 0)
data = {
**POST_DATA,
@ -281,15 +275,51 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
files = {'form-0-source': fp1}
response = self.client.post(url, {**data, **files})
self.assertEqual(response.status_code, 200)
self.maxDiff = None
self.assertEqual(
response.context['add_preview_formset'].forms[0].errors,
{'source': ['File with this Original hash already exists.']},
)
self.assertEqual(response.status_code, 302)
# No new files were created: the existing one was linked to the extension instead
self.assertEqual(File.objects.filter(type=File.TYPES.IMAGE).count(), images_count_before)
self.assertEqual(extension.previews.count(), 1)
self.assertEqual(extension.previews.first().original_hash, file.original_hash)
self.assertEqual(extension.previews.first().pk, file.pk)
def test_post_upload_validation_error_duplicate_across_forms(self):
def test_post_upload_existing_preview_file_linked_to_extension(self):
file = FileFactory(
type=File.TYPES.IMAGE,
original_hash='sha256:643e15eb6c4831173bbcf71b8c85efc70cf3437321bf2559b39aa5e9acfd5340',
hash='sha256:643e15eb6c4831173bbcf71b8c85efc70cf3437321bf2559b39aa5e9acfd5340',
source='file/original_image_source.jpg',
)
extension = create_approved_version().extension
another_extension = create_approved_version(extension__previews=[file]).extension
images_count_before = File.objects.filter(type=File.TYPES.IMAGE).count()
self.assertEqual(extension.previews.count(), 0)
self.assertEqual(another_extension.previews.count(), 1)
data = {
**POST_DATA,
'form-TOTAL_FORMS': ['1'],
}
file_name1 = 'test_preview_image_0001.png'
url = extension.get_manage_url()
user = extension.authors.first()
self.client.force_login(user)
with open(TEST_FILES_DIR / file_name1, 'rb') as fp1:
files = {'form-0-source': fp1}
response = self.client.post(url, {**data, **files})
self.assertEqual(response.status_code, 302)
# No new files were created: the existing one was linked to the extension instead
self.assertEqual(File.objects.filter(type=File.TYPES.IMAGE).count(), images_count_before)
self.assertEqual(extension.previews.count(), 1)
self.assertEqual(extension.previews.first().original_hash, file.original_hash)
self.assertEqual(extension.previews.first().pk, file.pk)
# File is referenced as a preview by both extensions
self.assertEqual(file.preview_set.count(), 2)
def test_post_upload_duplicates_are_ignored_across_forms(self):
extension = create_approved_version().extension
images_count_before = File.objects.filter(type=File.TYPES.IMAGE).count()
self.assertEqual(extension.previews.count(), 0)
data = {
**POST_DATA,
@ -304,12 +334,12 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
files = {'form-0-source': fp, 'icon-source': fp1}
response = self.client.post(url, {**data, **files})
self.assertEqual(response.status_code, 200)
self.maxDiff = None
self.assertEqual(response.status_code, 302)
# One image was uploaded successfully
self.assertEqual(
response.context['icon_form'].errors,
{'source': ['Please select another file instead of the duplicate.']},
File.objects.filter(type=File.TYPES.IMAGE).count(), images_count_before + 1
)
self.assertEqual(extension.previews.count(), 1)
def test_post_upload_validation_error_unexpected_preview_format_gif(self):
extension = create_approved_version().extension
@ -419,6 +449,7 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
original_name='old_icon.png',
size_bytes=1234,
)
old_icon = extension.icon
self.client.force_login(extension.authors.first())
url = extension.get_manage_url()
@ -427,7 +458,9 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
response = self.client.post(url, {**POST_DATA, **files})
self.assertEqual(response.status_code, 302)
extension.icon.refresh_from_db()
old_icon.refresh_from_db()
extension.refresh_from_db()
self.assertNotEqual(extension.icon_id, old_icon.pk)
self._test_file_properties(
extension.icon,
content_type='image/png',
@ -456,6 +489,7 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
original_name='old_featured_image.png',
size_bytes=1234,
)
old_featured_image = extension.featured_image
self.client.force_login(extension.authors.first())
url = extension.get_manage_url()
@ -464,7 +498,9 @@ class UpdateTest(CheckFilePropertiesMixin, TestCase):
response = self.client.post(url, {**POST_DATA, **files})
self.assertEqual(response.status_code, 302)
extension.featured_image.refresh_from_db()
old_featured_image.refresh_from_db()
extension.refresh_from_db()
self.assertNotEqual(extension.featured_image_id, old_featured_image.pk)
self._test_file_properties(
extension.featured_image,
content_type='image/png',