changeset 972:7138883966b3

Started unit tests for hotlinking images.
author Brian Neal <bgneal@gmail.com>
date Wed, 23 Sep 2015 21:26:09 -0500 (2015-09-24)
parents 4f265f61874b
children 6f55c086db1e
files core/download.py user_photos/forms.py user_photos/tests/__init__.py user_photos/tests/test_forms.py user_photos/tests/test_views.py user_photos/views.py
diffstat 4 files changed, 152 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/download.py	Wed Sep 23 21:26:09 2015 -0500
@@ -0,0 +1,61 @@
+"""This module contains routines for downloading files."""
+
+import logging
+import os
+import shutil
+import tempfile
+
+import requests
+
+
+logger = logging.getLogger(__name__)
+
+
+def download_file(url, path=None):
+    """Downloads the image file from the given source URL and stores it in the
+    filename given by path. If path is None, a temporary file will be created.
+
+    If successful returns the path to the downloaded file. Otherwise None is
+    returned.
+
+    This function may raise various exceptions from the requests library.
+    """
+    logger.info("download_file from %s; path=%s", url, path)
+
+    try:
+        r = requests.get(url, stream=True)
+    except requests.RequestException:
+        logger.exception("download_file requests.get('%s') exception", url)
+        raise
+
+    if r.status_code != 200:
+        logger.error("download_file from %s: error code %d", url, r.status_code)
+        return None
+
+    # Save file data
+
+    if not path:
+        fd, path = tempfile.mkstemp()
+        os.close(fd)
+
+    try:
+        with open(path, 'wb') as fp:
+            r.raw.decode_content = True
+            shutil.copyfileobj(r.raw, fp)
+    except requests.RequestException:
+        logger.exception("download_file download exception")
+        raise
+
+    file_size = os.stat(path).st_size
+    logger.info("download_file retrieved %s bytes from %s; saved to %s", file_size, url, path)
+    return path
+
+
+if __name__ == '__main__':
+    import sys
+    s = "%(asctime)s : %(levelname)s : %(message)s"
+    logging.basicConfig(level=logging.DEBUG, format=s)
+    logging.info("argument is %s", sys.argv[1])
+    result = download_file(sys.argv[1])
+    if result:
+        print result
--- a/user_photos/forms.py	Tue Sep 22 20:23:50 2015 -0500
+++ b/user_photos/forms.py	Wed Sep 23 21:26:09 2015 -0500
@@ -144,8 +144,6 @@
         return cleaned_data
 
     def save(self):
-        import pdb; pdb.set_trace()
-
         url = self.url_parts.geturl()
         if (self.url_parts.scheme == 'https' and
                 self.url_parts.hostname in settings.USER_IMAGES_SOURCES):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/user_photos/tests/test_views.py	Wed Sep 23 21:26:09 2015 -0500
@@ -0,0 +1,88 @@
+"""
+Tests for the views in the user_photos application.
+
+"""
+import json
+
+import mock
+from django.test import TestCase
+from django.test.utils import override_settings
+from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
+from django.http import JsonResponse
+
+
+class HotlinkImageTestCase(TestCase):
+
+    def setUp(self):
+        self.username = 'test_user'
+        self.pw = 'password'
+        self.user = User.objects.create_user(self.username, '', self.pw)
+        self.user.save()
+        self.assertTrue(self.client.login(username=self.username, password=self.pw))
+        self.view_url = reverse('user_photos-hotlink')
+
+    def testUnauthenticatedUser(self):
+        self.client.logout()
+        response = self.client.post(self.view_url,
+                                    {'url': 'http://example.com/test.jpg'},
+                                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+        self.assertIsInstance(response, JsonResponse)
+        self.assertEqual(response.status_code, 403)
+
+    def testWrongMethod(self):
+        response = self.client.get(self.view_url)
+        self.assertIsInstance(response, JsonResponse)
+        self.assertEqual(response.status_code, 405)
+
+    def testNotAjax(self):
+        response = self.client.post(self.view_url,
+                                    {'url': 'http://example.com/test.jpg'})
+        self.assertIsInstance(response, JsonResponse)
+        self.assertEqual(response.status_code, 405)
+
+    @override_settings(USER_PHOTOS_ENABLED=False)
+    def testServiceDisabled(self):
+        response = self.client.post(self.view_url,
+                                    {'url': 'http://example.com/test.jpg'},
+                                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+        self.assertIsInstance(response, JsonResponse)
+        self.assertEqual(response.status_code, 403)
+
+    @mock.patch('user_photos.views.HotLinkImageForm')
+    def testFormInvalid(self, form_mock):
+        form_mock.return_value.is_valid.return_value = False
+        form_mock.return_value.non_field_errors.return_value = mock.Mock()
+        form_mock.return_value.non_field_errors.return_value.as_text.return_value = 'bad stuff'
+        form_mock.return_value.errors.return_value = {}
+
+        response = self.client.post(self.view_url,
+                                    {'url': 'http://example.com/test.jpg'},
+                                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+        self.assertIsInstance(response, JsonResponse)
+        self.assertEqual(response.status_code, 400)
+
+    @mock.patch('user_photos.views.HotLinkImageForm')
+    def testHappyPath(self, form_mock):
+        form_mock.return_value.is_valid.return_value = True
+        form_mock.return_value.save.return_value = 'https://example.com/a.jpg'
+
+        response = self.client.post(self.view_url,
+                                    {'url': 'http://example.com/test.jpg'},
+                                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+        self.assertIsInstance(response, JsonResponse)
+        self.assertEqual(response.status_code, 200)
+        expected = {"url": "https://example.com/a.jpg", "error_msg": ""}
+        actual = json.loads(response.content)
+        self.assertEqual(actual, expected)
+
+    @mock.patch('user_photos.views.HotLinkImageForm')
+    def testFormSaveRaises(self, form_mock):
+        form_mock.return_value.is_valid.return_value = True
+        form_mock.return_value.save.side_effect = ValueError
+
+        response = self.client.post(self.view_url,
+                                    {'url': 'http://example.com/test.jpg'},
+                                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+        self.assertIsInstance(response, JsonResponse)
+        self.assertEqual(response.status_code, 500)
--- a/user_photos/views.py	Tue Sep 22 20:23:50 2015 -0500
+++ b/user_photos/views.py	Wed Sep 23 21:26:09 2015 -0500
@@ -156,13 +156,13 @@
 
     if not request.user.is_authenticated():
         ret['error_msg'] = 'Please login to use this service'
-        return JsonResponse(ret, status=status_code)
+        return JsonResponse(ret, status=403)
     if not request.is_ajax() or request.method != 'POST':
         ret['error_msg'] = 'This method is not allowed'
-        return JsonResponse(ret, status=status_code)
+        return JsonResponse(ret, status=405)
 
     if settings.USER_PHOTOS_ENABLED:
-        form = HotLinkImageForm(request.POST, request.FILES, user=request.user)
+        form = HotLinkImageForm(request.POST, user=request.user)
         if form.is_valid():
             try:
                 ret['url'] = form.save()