From 018d110ef547acb71b0526d0ff934e1e476244e4 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Sat, 29 Nov 2014 17:41:06 +0100 Subject: [PATCH] Fixed #23911 -- Added support for buffer file uploads in the test client --- AUTHORS | 1 + django/test/client.py | 11 ++++++++--- docs/releases/1.8.txt | 2 ++ docs/topics/testing/tools.txt | 7 +++++++ tests/file_uploads/tests.py | 30 +++++++++++++++++++++++++++++- 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index bf10e80afd..4adfe2310b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -650,6 +650,7 @@ answer newbie questions, and generally made Django that much better: Thomas Sorrel Thomas Steinacher Thomas Stromberg + Thomas Tanner tibimicu@gmx.net Tim Graham Tim Heap diff --git a/django/test/client.py b/django/test/client.py index b2633bd7e7..62f7c5fb6c 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -195,20 +195,25 @@ def encode_multipart(boundary, data): def encode_file(boundary, key, file): to_bytes = lambda s: force_bytes(s, settings.DEFAULT_CHARSET) + filename = os.path.basename(file.name) if hasattr(file, 'name') else '' if hasattr(file, 'content_type'): content_type = file.content_type + elif filename: + content_type = mimetypes.guess_type(filename)[0] else: - content_type = mimetypes.guess_type(file.name)[0] + content_type = None if content_type is None: content_type = 'application/octet-stream' + if not filename: + filename = key return [ to_bytes('--%s' % boundary), to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"' - % (key, os.path.basename(file.name))), + % (key, filename)), to_bytes('Content-Type: %s' % content_type), b'', - file.read() + to_bytes(file.read()) ] diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index d95599f613..375a39920f 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -511,6 +511,8 @@ Tests :meth:`TestCase.setUpTestData() `. Using this technique can speed up the tests as compared to using ``setUp()``. +* Added test client support for file uploads with file-like objects. + Validators ^^^^^^^^^^ diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index 454da239e0..69649d7a2e 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -238,6 +238,13 @@ Use the ``django.test.Client`` class to make requests. (The name ``attachment`` here is not relevant; use whatever name your file-processing code expects.) + You may also provide any file-like object (e.g., :class:`~io.StringIO` or + :class:`~io.BytesIO`) as a file handle. + + .. versionadded:: 1.8 + + The ability to use a file-like object was added. + Note that if you wish to use the same file handle for multiple ``post()`` calls then you will need to manually reset the file pointer between posts. The easiest way to do this is to diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py index 85363744d8..37954a99ca 100644 --- a/tests/file_uploads/tests.py +++ b/tests/file_uploads/tests.py @@ -17,7 +17,7 @@ from django.test import TestCase, client from django.test import override_settings from django.utils.encoding import force_bytes from django.utils.http import urlquote -from django.utils.six import StringIO +from django.utils.six import BytesIO, StringIO from . import uploadhandler from .models import FileModel @@ -262,6 +262,34 @@ class FileUploadTests(TestCase): self.assertLess(len(got), 256, "Got a long file name (%s characters)." % len(got)) + def test_file_content(self): + tdir = tempfile.gettempdir() + + file = tempfile.NamedTemporaryFile + with file(suffix=".ctype_extra", dir=tdir) as no_content_type, \ + file(suffix=".ctype_extra", dir=tdir) as simple_file: + no_content_type.write(b'no content') + no_content_type.seek(0) + + simple_file.write(b'text content') + simple_file.seek(0) + simple_file.content_type = 'text/plain' + + string_io = StringIO('string content') + bytes_io = BytesIO(b'binary content') + + response = self.client.post('/echo_content/', { + 'no_content_type': no_content_type, + 'simple_file': simple_file, + 'string': string_io, + 'binary': bytes_io, + }) + received = json.loads(response.content.decode('utf-8')) + self.assertEqual(received['no_content_type'], 'no content') + self.assertEqual(received['simple_file'], 'text content') + self.assertEqual(received['string'], 'string content') + self.assertEqual(received['binary'], 'binary content') + def test_content_type_extra(self): """Uploaded files may have content type parameters available.""" tdir = tempfile.gettempdir()