summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPavel Shpilev <pavel.shpilev@medibank.com.au>2014-10-15 18:42:06 +1100
committerTim Graham <timograham@gmail.com>2015-01-12 09:09:18 -0500
commita7c256cb5491bf2a77abdff01638239db5bfd9d5 (patch)
tree3b3cd98b0f2f42efd989ae9dd9ab3dba473d972a /tests
parentb5c1a85b50c709770b8e98aeecfeb8e81ca29dcf (diff)
Fixed #9893 -- Allowed using a field's max_length in the Storage.
Diffstat (limited to 'tests')
-rw-r--r--tests/file_storage/models.py19
-rw-r--r--tests/file_storage/tests.py70
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'))