diff options
| author | Pavel Shpilev <pavel.shpilev@medibank.com.au> | 2014-10-15 18:42:06 +1100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-01-12 09:09:18 -0500 |
| commit | a7c256cb5491bf2a77abdff01638239db5bfd9d5 (patch) | |
| tree | 3b3cd98b0f2f42efd989ae9dd9ab3dba473d972a /tests | |
| parent | b5c1a85b50c709770b8e98aeecfeb8e81ca29dcf (diff) | |
Fixed #9893 -- Allowed using a field's max_length in the Storage.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/file_storage/models.py | 19 | ||||
| -rw-r--r-- | tests/file_storage/tests.py | 70 |
2 files changed, 86 insertions, 3 deletions
diff --git a/tests/file_storage/models.py b/tests/file_storage/models.py index 126cd202aa..2f58c53ddd 100644 --- a/tests/file_storage/models.py +++ b/tests/file_storage/models.py @@ -13,6 +13,19 @@ from django.db import models from django.core.files.storage import FileSystemStorage +class OldStyleFSStorage(FileSystemStorage): + """ + Storage backend without support for the ``max_length`` argument in + ``get_available_name()`` and ``save()``; for backward-compatibility and + deprecation testing. + """ + def get_available_name(self, name): + return name + + def save(self, name, content): + return super(OldStyleFSStorage, self).save(name, content) + + temp_storage_location = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) temp_storage = FileSystemStorage(location=temp_storage_location) @@ -31,3 +44,9 @@ class Storage(models.Model): random = models.FileField(storage=temp_storage, upload_to=random_upload_to) default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt') empty = models.FileField(storage=temp_storage) + limited_length = models.FileField(storage=temp_storage, upload_to='tests', max_length=20) + extended_length = models.FileField(storage=temp_storage, upload_to='tests', max_length=300) + old_style = models.FileField( + storage=OldStyleFSStorage(location=temp_storage_location), + upload_to='tests', + ) diff --git a/tests/file_storage/tests.py b/tests/file_storage/tests.py index e1938e9ea5..8e8b0e0a32 100644 --- a/tests/file_storage/tests.py +++ b/tests/file_storage/tests.py @@ -8,6 +8,7 @@ import sys import tempfile import time import unittest +import warnings from datetime import datetime, timedelta try: @@ -16,7 +17,7 @@ except ImportError: import dummy_threading as threading from django.core.cache import cache -from django.core.exceptions import SuspiciousOperation +from django.core.exceptions import SuspiciousOperation, SuspiciousFileOperation from django.core.files.base import File, ContentFile from django.core.files.storage import FileSystemStorage, get_storage_class from django.core.files.uploadedfile import (InMemoryUploadedFile, SimpleUploadedFile, @@ -407,7 +408,7 @@ class FileStorageTests(unittest.TestCase): class CustomStorage(FileSystemStorage): - def get_available_name(self, name): + def get_available_name(self, name, max_length=None): """ Append numbers to duplicate files rather than underscores, like Trac. """ @@ -433,7 +434,7 @@ class CustomStorageTests(FileStorageTests): self.storage.delete(second) -class FileFieldStorageTests(unittest.TestCase): +class FileFieldStorageTests(SimpleTestCase): def tearDown(self): shutil.rmtree(temp_storage_location) @@ -503,6 +504,69 @@ class FileFieldStorageTests(unittest.TestCase): for o in objs: o.delete() + def test_file_truncation(self): + # Given the max_length is limited, when multiple files get uploaded + # under the same name, then the filename get truncated in order to fit + # in _(7 random chars). When most of the max_length is taken by + # dirname + extension and there are not enough characters in the + # filename to truncate, an exception should be raised. + objs = [Storage() for i in range(2)] + filename = 'filename.ext' + + for o in objs: + o.limited_length.save(filename, ContentFile('Same Content')) + try: + # Testing truncation. + names = [o.limited_length.name for o in objs] + self.assertEqual(names[0], 'tests/%s' % filename) + six.assertRegex(self, names[1], 'tests/fi_%s.ext' % FILE_SUFFIX_REGEX) + + # Testing exception is raised when filename is too short to truncate. + filename = 'short.longext' + objs[0].limited_length.save(filename, ContentFile('Same Content')) + self.assertRaisesMessage( + SuspiciousFileOperation, 'Storage can not find an available filename', + objs[1].limited_length.save, *(filename, ContentFile('Same Content')) + ) + finally: + for o in objs: + o.delete() + + def test_extended_length_storage(self): + # Testing FileField with max_length > 255. Most systems have filename + # length limitation of 255. Path takes extra chars. + filename = 251 * 'a' # 4 chars for extension. + obj = Storage() + obj.extended_length.save('%s.txt' % filename, ContentFile('Same Content')) + self.assertEqual(obj.extended_length.name, 'tests/%s.txt' % filename) + self.assertEqual(obj.extended_length.read(), b'Same Content') + obj.extended_length.close() + + def test_old_style_storage(self): + # Testing backward-compatibility with old-style storage backends that + # don't take ``max_length`` parameter in ``get_available_name()`` + # and save(). A deprecation warning should be raised. + obj = Storage() + with warnings.catch_warnings(record=True) as warns: + warnings.simplefilter('always') + obj.old_style.save('deprecated_storage_test.txt', ContentFile('Same Content')) + self.assertEqual(len(warns), 2) + self.assertEqual( + str(warns[0].message), + 'Backwards compatibility for storage backends without support for ' + 'the `max_length` argument in Storage.save() will be removed in ' + 'Django 2.0.' + ) + self.assertEqual( + str(warns[1].message), + 'Backwards compatibility for storage backends without support for ' + 'the `max_length` argument in Storage.get_available_name() will ' + 'be removed in Django 2.0.' + ) + self.assertEqual(obj.old_style.name, 'tests/deprecated_storage_test.txt') + self.assertEqual(obj.old_style.read(), b'Same Content') + obj.old_style.close() + def test_filefield_default(self): # Default values allow an object to access a single file. temp_storage.save('tests/default.txt', ContentFile('default content')) |
