# HG changeset patch # User Brian Neal # Date 1443061569 18000 # Node ID 7138883966b3bf118aedc1b5683b71a7321037c9 # Parent 4f265f61874be458914340dd5c38906c5ed9948c Started unit tests for hotlinking images. diff -r 4f265f61874b -r 7138883966b3 core/download.py --- /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 diff -r 4f265f61874b -r 7138883966b3 user_photos/forms.py --- 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): diff -r 4f265f61874b -r 7138883966b3 user_photos/tests/test_views.py --- /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) diff -r 4f265f61874b -r 7138883966b3 user_photos/views.py --- 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()