From 56e777eba3687bd56b954de4b3dfa44775481b14 Mon Sep 17 00:00:00 2001 From: Preston Timmons Date: Sat, 6 Apr 2013 14:04:06 -0500 Subject: Modified view_tests for unittest2 discovery. --- tests/view_tests/tests/__init__.py | 10 +- tests/view_tests/tests/debug.py | 559 ------------------------------- tests/view_tests/tests/defaults.py | 61 ---- tests/view_tests/tests/i18n.py | 221 ------------ tests/view_tests/tests/shortcuts.py | 59 ---- tests/view_tests/tests/specials.py | 39 --- tests/view_tests/tests/static.py | 120 ------- tests/view_tests/tests/test_debug.py | 559 +++++++++++++++++++++++++++++++ tests/view_tests/tests/test_defaults.py | 61 ++++ tests/view_tests/tests/test_i18n.py | 221 ++++++++++++ tests/view_tests/tests/test_shortcuts.py | 59 ++++ tests/view_tests/tests/test_specials.py | 39 +++ tests/view_tests/tests/test_static.py | 120 +++++++ 13 files changed, 1064 insertions(+), 1064 deletions(-) delete mode 100644 tests/view_tests/tests/debug.py delete mode 100644 tests/view_tests/tests/defaults.py delete mode 100644 tests/view_tests/tests/i18n.py delete mode 100644 tests/view_tests/tests/shortcuts.py delete mode 100644 tests/view_tests/tests/specials.py delete mode 100644 tests/view_tests/tests/static.py create mode 100644 tests/view_tests/tests/test_debug.py create mode 100644 tests/view_tests/tests/test_defaults.py create mode 100644 tests/view_tests/tests/test_i18n.py create mode 100644 tests/view_tests/tests/test_shortcuts.py create mode 100644 tests/view_tests/tests/test_specials.py create mode 100644 tests/view_tests/tests/test_static.py diff --git a/tests/view_tests/tests/__init__.py b/tests/view_tests/tests/__init__.py index 17f3f4562d..06b50c14b2 100644 --- a/tests/view_tests/tests/__init__.py +++ b/tests/view_tests/tests/__init__.py @@ -1,10 +1,10 @@ from __future__ import absolute_import -from .debug import (DebugViewTests, ExceptionReporterTests, +from .test_debug import (DebugViewTests, ExceptionReporterTests, ExceptionReporterTests, PlainTextReportTests, ExceptionReporterFilterTests, AjaxResponseExceptionReporterFilter) from .defaults import DefaultsTests -from .i18n import JsI18NTests, I18NTests, JsI18NTestsMultiPackage, JavascriptI18nTests -from .shortcuts import ShortcutTests -from .specials import URLHandling -from .static import StaticHelperTest, StaticUtilsTests, StaticTests +from .test_i18n import JsI18NTests, I18NTests, JsI18NTestsMultiPackage, JavascriptI18nTests +from .test_shortcuts import ShortcutTests +from .test_specials import URLHandling +from .test_static import StaticHelperTest, StaticUtilsTests, StaticTests diff --git a/tests/view_tests/tests/debug.py b/tests/view_tests/tests/debug.py deleted file mode 100644 index b44cd88abe..0000000000 --- a/tests/view_tests/tests/debug.py +++ /dev/null @@ -1,559 +0,0 @@ -# -*- coding: utf-8 -*- -# This coding header is significant for tests, as the debug view is parsing -# files to search for such a header to decode the source file content -from __future__ import absolute_import, unicode_literals - -import inspect -import os -import sys - -from django.core import mail -from django.core.files.uploadedfile import SimpleUploadedFile -from django.core.urlresolvers import reverse -from django.test import TestCase, RequestFactory -from django.test.utils import (override_settings, setup_test_template_loader, - restore_template_loaders) -from django.utils.encoding import force_text -from django.views.debug import ExceptionReporter - -from .. import BrokenException, except_args -from ..views import (sensitive_view, non_sensitive_view, paranoid_view, - custom_exception_reporter_filter_view, sensitive_method_view, - sensitive_args_function_caller, sensitive_kwargs_function_caller) - - -@override_settings(DEBUG=True, TEMPLATE_DEBUG=True) -class DebugViewTests(TestCase): - urls = "view_tests.urls" - - def test_files(self): - response = self.client.get('/raises/') - self.assertEqual(response.status_code, 500) - - data = { - 'file_data.txt': SimpleUploadedFile('file_data.txt', b'haha'), - } - response = self.client.post('/raises/', data) - self.assertContains(response, 'file_data.txt', status_code=500) - self.assertNotContains(response, 'haha', status_code=500) - - def test_403(self): - # Ensure no 403.html template exists to test the default case. - setup_test_template_loader({}) - try: - response = self.client.get('/views/raises403/') - self.assertContains(response, '

403 Forbidden

', status_code=403) - finally: - restore_template_loaders() - - def test_403_template(self): - # Set up a test 403.html template. - setup_test_template_loader( - {'403.html': 'This is a test template for a 403 Forbidden error.'} - ) - try: - response = self.client.get('/views/raises403/') - self.assertContains(response, 'test template', status_code=403) - finally: - restore_template_loaders() - - def test_404(self): - response = self.client.get('/views/raises404/') - self.assertEqual(response.status_code, 404) - - def test_view_exceptions(self): - for n in range(len(except_args)): - self.assertRaises(BrokenException, self.client.get, - reverse('view_exception', args=(n,))) - - def test_template_exceptions(self): - for n in range(len(except_args)): - try: - self.client.get(reverse('template_exception', args=(n,))) - except Exception: - raising_loc = inspect.trace()[-1][-2][0].strip() - self.assertFalse(raising_loc.find('raise BrokenException') == -1, - "Failed to find 'raise BrokenException' in last frame of traceback, instead found: %s" % - raising_loc) - - def test_template_loader_postmortem(self): - response = self.client.get(reverse('raises_template_does_not_exist')) - template_path = os.path.join('templates', 'i_dont_exist.html') - self.assertContains(response, template_path, status_code=500) - - -class ExceptionReporterTests(TestCase): - rf = RequestFactory() - - def test_request_and_exception(self): - "A simple exception report can be generated" - try: - request = self.rf.get('/test_view/') - raise ValueError("Can't find my keys") - except ValueError: - exc_type, exc_value, tb = sys.exc_info() - reporter = ExceptionReporter(request, exc_type, exc_value, tb) - html = reporter.get_traceback_html() - self.assertIn('

ValueError at /test_view/

', html) - self.assertIn('
Can't find my keys
', html) - self.assertIn('Request Method:', html) - self.assertIn('Request URL:', html) - self.assertIn('Exception Type:', html) - self.assertIn('Exception Value:', html) - self.assertIn('

Traceback ', html) - self.assertIn('

Request information

', html) - self.assertNotIn('

Request data not supplied

', html) - - def test_no_request(self): - "An exception report can be generated without request" - try: - raise ValueError("Can't find my keys") - except ValueError: - exc_type, exc_value, tb = sys.exc_info() - reporter = ExceptionReporter(None, exc_type, exc_value, tb) - html = reporter.get_traceback_html() - self.assertIn('

ValueError

', html) - self.assertIn('
Can't find my keys
', html) - self.assertNotIn('Request Method:', html) - self.assertNotIn('Request URL:', html) - self.assertIn('Exception Type:', html) - self.assertIn('Exception Value:', html) - self.assertIn('

Traceback ', html) - self.assertIn('

Request information

', html) - self.assertIn('

Request data not supplied

', html) - - def test_no_exception(self): - "An exception report can be generated for just a request" - request = self.rf.get('/test_view/') - reporter = ExceptionReporter(request, None, None, None) - html = reporter.get_traceback_html() - self.assertIn('

Report at /test_view/

', html) - self.assertIn('
No exception supplied
', html) - self.assertIn('Request Method:', html) - self.assertIn('Request URL:', html) - self.assertNotIn('Exception Type:', html) - self.assertNotIn('Exception Value:', html) - self.assertNotIn('

Traceback ', html) - self.assertIn('

Request information

', html) - self.assertNotIn('

Request data not supplied

', html) - - def test_request_and_message(self): - "A message can be provided in addition to a request" - request = self.rf.get('/test_view/') - reporter = ExceptionReporter(request, None, "I'm a little teapot", None) - html = reporter.get_traceback_html() - self.assertIn('

Report at /test_view/

', html) - self.assertIn('
I'm a little teapot
', html) - self.assertIn('Request Method:', html) - self.assertIn('Request URL:', html) - self.assertNotIn('Exception Type:', html) - self.assertNotIn('Exception Value:', html) - self.assertNotIn('

Traceback ', html) - self.assertIn('

Request information

', html) - self.assertNotIn('

Request data not supplied

', html) - - def test_message_only(self): - reporter = ExceptionReporter(None, None, "I'm a little teapot", None) - html = reporter.get_traceback_html() - self.assertIn('

Report

', html) - self.assertIn('
I'm a little teapot
', html) - self.assertNotIn('Request Method:', html) - self.assertNotIn('Request URL:', html) - self.assertNotIn('Exception Type:', html) - self.assertNotIn('Exception Value:', html) - self.assertNotIn('

Traceback ', html) - self.assertIn('

Request information

', html) - self.assertIn('

Request data not supplied

', html) - - -class PlainTextReportTests(TestCase): - rf = RequestFactory() - - def test_request_and_exception(self): - "A simple exception report can be generated" - try: - request = self.rf.get('/test_view/') - raise ValueError("Can't find my keys") - except ValueError: - exc_type, exc_value, tb = sys.exc_info() - reporter = ExceptionReporter(request, exc_type, exc_value, tb) - text = reporter.get_traceback_text() - self.assertIn('ValueError at /test_view/', text) - self.assertIn("Can't find my keys", text) - self.assertIn('Request Method:', text) - self.assertIn('Request URL:', text) - self.assertIn('Exception Type:', text) - self.assertIn('Exception Value:', text) - self.assertIn('Traceback:', text) - self.assertIn('Request information:', text) - self.assertNotIn('Request data not supplied', text) - - def test_no_request(self): - "An exception report can be generated without request" - try: - raise ValueError("Can't find my keys") - except ValueError: - exc_type, exc_value, tb = sys.exc_info() - reporter = ExceptionReporter(None, exc_type, exc_value, tb) - text = reporter.get_traceback_text() - self.assertIn('ValueError', text) - self.assertIn("Can't find my keys", text) - self.assertNotIn('Request Method:', text) - self.assertNotIn('Request URL:', text) - self.assertIn('Exception Type:', text) - self.assertIn('Exception Value:', text) - self.assertIn('Traceback:', text) - self.assertIn('Request data not supplied', text) - - def test_no_exception(self): - "An exception report can be generated for just a request" - request = self.rf.get('/test_view/') - reporter = ExceptionReporter(request, None, None, None) - text = reporter.get_traceback_text() - - def test_request_and_message(self): - "A message can be provided in addition to a request" - request = self.rf.get('/test_view/') - reporter = ExceptionReporter(request, None, "I'm a little teapot", None) - text = reporter.get_traceback_text() - - def test_message_only(self): - reporter = ExceptionReporter(None, None, "I'm a little teapot", None) - text = reporter.get_traceback_text() - - -class ExceptionReportTestMixin(object): - - # Mixin used in the ExceptionReporterFilterTests and - # AjaxResponseExceptionReporterFilter tests below - - breakfast_data = {'sausage-key': 'sausage-value', - 'baked-beans-key': 'baked-beans-value', - 'hash-brown-key': 'hash-brown-value', - 'bacon-key': 'bacon-value',} - - def verify_unsafe_response(self, view, check_for_vars=True, - check_for_POST_params=True): - """ - Asserts that potentially sensitive info are displayed in the response. - """ - request = self.rf.post('/some_url/', self.breakfast_data) - response = view(request) - if check_for_vars: - # All variables are shown. - self.assertContains(response, 'cooked_eggs', status_code=500) - self.assertContains(response, 'scrambled', status_code=500) - self.assertContains(response, 'sauce', status_code=500) - self.assertContains(response, 'worcestershire', status_code=500) - if check_for_POST_params: - for k, v in self.breakfast_data.items(): - # All POST parameters are shown. - self.assertContains(response, k, status_code=500) - self.assertContains(response, v, status_code=500) - - def verify_safe_response(self, view, check_for_vars=True, - check_for_POST_params=True): - """ - Asserts that certain sensitive info are not displayed in the response. - """ - request = self.rf.post('/some_url/', self.breakfast_data) - response = view(request) - if check_for_vars: - # Non-sensitive variable's name and value are shown. - self.assertContains(response, 'cooked_eggs', status_code=500) - self.assertContains(response, 'scrambled', status_code=500) - # Sensitive variable's name is shown but not its value. - self.assertContains(response, 'sauce', status_code=500) - self.assertNotContains(response, 'worcestershire', status_code=500) - if check_for_POST_params: - for k, v in self.breakfast_data.items(): - # All POST parameters' names are shown. - self.assertContains(response, k, status_code=500) - # Non-sensitive POST parameters' values are shown. - self.assertContains(response, 'baked-beans-value', status_code=500) - self.assertContains(response, 'hash-brown-value', status_code=500) - # Sensitive POST parameters' values are not shown. - self.assertNotContains(response, 'sausage-value', status_code=500) - self.assertNotContains(response, 'bacon-value', status_code=500) - - def verify_paranoid_response(self, view, check_for_vars=True, - check_for_POST_params=True): - """ - Asserts that no variables or POST parameters are displayed in the response. - """ - request = self.rf.post('/some_url/', self.breakfast_data) - response = view(request) - if check_for_vars: - # Show variable names but not their values. - self.assertContains(response, 'cooked_eggs', status_code=500) - self.assertNotContains(response, 'scrambled', status_code=500) - self.assertContains(response, 'sauce', status_code=500) - self.assertNotContains(response, 'worcestershire', status_code=500) - if check_for_POST_params: - for k, v in self.breakfast_data.items(): - # All POST parameters' names are shown. - self.assertContains(response, k, status_code=500) - # No POST parameters' values are shown. - self.assertNotContains(response, v, status_code=500) - - def verify_unsafe_email(self, view, check_for_POST_params=True): - """ - Asserts that potentially sensitive info are displayed in the email report. - """ - with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): - mail.outbox = [] # Empty outbox - request = self.rf.post('/some_url/', self.breakfast_data) - response = view(request) - self.assertEqual(len(mail.outbox), 1) - email = mail.outbox[0] - - # Frames vars are never shown in plain text email reports. - body_plain = force_text(email.body) - self.assertNotIn('cooked_eggs', body_plain) - self.assertNotIn('scrambled', body_plain) - self.assertNotIn('sauce', body_plain) - self.assertNotIn('worcestershire', body_plain) - - # Frames vars are shown in html email reports. - body_html = force_text(email.alternatives[0][0]) - self.assertIn('cooked_eggs', body_html) - self.assertIn('scrambled', body_html) - self.assertIn('sauce', body_html) - self.assertIn('worcestershire', body_html) - - if check_for_POST_params: - for k, v in self.breakfast_data.items(): - # All POST parameters are shown. - self.assertIn(k, body_plain) - self.assertIn(v, body_plain) - self.assertIn(k, body_html) - self.assertIn(v, body_html) - - def verify_safe_email(self, view, check_for_POST_params=True): - """ - Asserts that certain sensitive info are not displayed in the email report. - """ - with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): - mail.outbox = [] # Empty outbox - request = self.rf.post('/some_url/', self.breakfast_data) - response = view(request) - self.assertEqual(len(mail.outbox), 1) - email = mail.outbox[0] - - # Frames vars are never shown in plain text email reports. - body_plain = force_text(email.body) - self.assertNotIn('cooked_eggs', body_plain) - self.assertNotIn('scrambled', body_plain) - self.assertNotIn('sauce', body_plain) - self.assertNotIn('worcestershire', body_plain) - - # Frames vars are shown in html email reports. - body_html = force_text(email.alternatives[0][0]) - self.assertIn('cooked_eggs', body_html) - self.assertIn('scrambled', body_html) - self.assertIn('sauce', body_html) - self.assertNotIn('worcestershire', body_html) - - if check_for_POST_params: - for k, v in self.breakfast_data.items(): - # All POST parameters' names are shown. - self.assertIn(k, body_plain) - # Non-sensitive POST parameters' values are shown. - self.assertIn('baked-beans-value', body_plain) - self.assertIn('hash-brown-value', body_plain) - self.assertIn('baked-beans-value', body_html) - self.assertIn('hash-brown-value', body_html) - # Sensitive POST parameters' values are not shown. - self.assertNotIn('sausage-value', body_plain) - self.assertNotIn('bacon-value', body_plain) - self.assertNotIn('sausage-value', body_html) - self.assertNotIn('bacon-value', body_html) - - def verify_paranoid_email(self, view): - """ - Asserts that no variables or POST parameters are displayed in the email report. - """ - with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): - mail.outbox = [] # Empty outbox - request = self.rf.post('/some_url/', self.breakfast_data) - response = view(request) - self.assertEqual(len(mail.outbox), 1) - email = mail.outbox[0] - # Frames vars are never shown in plain text email reports. - body = force_text(email.body) - self.assertNotIn('cooked_eggs', body) - self.assertNotIn('scrambled', body) - self.assertNotIn('sauce', body) - self.assertNotIn('worcestershire', body) - for k, v in self.breakfast_data.items(): - # All POST parameters' names are shown. - self.assertIn(k, body) - # No POST parameters' values are shown. - self.assertNotIn(v, body) - - -class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin): - """ - Ensure that sensitive information can be filtered out of error reports. - Refs #14614. - """ - rf = RequestFactory() - - def test_non_sensitive_request(self): - """ - Ensure that everything (request info and frame variables) can bee seen - in the default error reports for non-sensitive requests. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(non_sensitive_view) - self.verify_unsafe_email(non_sensitive_view) - - with self.settings(DEBUG=False): - self.verify_unsafe_response(non_sensitive_view) - self.verify_unsafe_email(non_sensitive_view) - - def test_sensitive_request(self): - """ - Ensure that sensitive POST parameters and frame variables cannot be - seen in the default error reports for sensitive requests. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(sensitive_view) - self.verify_unsafe_email(sensitive_view) - - with self.settings(DEBUG=False): - self.verify_safe_response(sensitive_view) - self.verify_safe_email(sensitive_view) - - def test_paranoid_request(self): - """ - Ensure that no POST parameters and frame variables can be seen in the - default error reports for "paranoid" requests. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(paranoid_view) - self.verify_unsafe_email(paranoid_view) - - with self.settings(DEBUG=False): - self.verify_paranoid_response(paranoid_view) - self.verify_paranoid_email(paranoid_view) - - def test_custom_exception_reporter_filter(self): - """ - Ensure that it's possible to assign an exception reporter filter to - the request to bypass the one set in DEFAULT_EXCEPTION_REPORTER_FILTER. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(custom_exception_reporter_filter_view) - self.verify_unsafe_email(custom_exception_reporter_filter_view) - - with self.settings(DEBUG=False): - self.verify_unsafe_response(custom_exception_reporter_filter_view) - self.verify_unsafe_email(custom_exception_reporter_filter_view) - - def test_sensitive_method(self): - """ - Ensure that the sensitive_variables decorator works with object - methods. - Refs #18379. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(sensitive_method_view, - check_for_POST_params=False) - self.verify_unsafe_email(sensitive_method_view, - check_for_POST_params=False) - - with self.settings(DEBUG=False): - self.verify_safe_response(sensitive_method_view, - check_for_POST_params=False) - self.verify_safe_email(sensitive_method_view, - check_for_POST_params=False) - - def test_sensitive_function_arguments(self): - """ - Ensure that sensitive variables don't leak in the sensitive_variables - decorator's frame, when those variables are passed as arguments to the - decorated function. - Refs #19453. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(sensitive_args_function_caller) - self.verify_unsafe_email(sensitive_args_function_caller) - - with self.settings(DEBUG=False): - self.verify_safe_response(sensitive_args_function_caller, check_for_POST_params=False) - self.verify_safe_email(sensitive_args_function_caller, check_for_POST_params=False) - - def test_sensitive_function_keyword_arguments(self): - """ - Ensure that sensitive variables don't leak in the sensitive_variables - decorator's frame, when those variables are passed as keyword arguments - to the decorated function. - Refs #19453. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(sensitive_kwargs_function_caller) - self.verify_unsafe_email(sensitive_kwargs_function_caller) - - with self.settings(DEBUG=False): - self.verify_safe_response(sensitive_kwargs_function_caller, check_for_POST_params=False) - self.verify_safe_email(sensitive_kwargs_function_caller, check_for_POST_params=False) - - -class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin): - """ - Ensure that sensitive information can be filtered out of error reports. - - Here we specifically test the plain text 500 debug-only error page served - when it has been detected the request was sent by JS code. We don't check - for (non)existence of frames vars in the traceback information section of - the response content because we don't include them in these error pages. - Refs #14614. - """ - rf = RequestFactory(HTTP_X_REQUESTED_WITH='XMLHttpRequest') - - def test_non_sensitive_request(self): - """ - Ensure that request info can bee seen in the default error reports for - non-sensitive requests. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(non_sensitive_view, check_for_vars=False) - - with self.settings(DEBUG=False): - self.verify_unsafe_response(non_sensitive_view, check_for_vars=False) - - def test_sensitive_request(self): - """ - Ensure that sensitive POST parameters cannot be seen in the default - error reports for sensitive requests. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(sensitive_view, check_for_vars=False) - - with self.settings(DEBUG=False): - self.verify_safe_response(sensitive_view, check_for_vars=False) - - def test_paranoid_request(self): - """ - Ensure that no POST parameters can be seen in the default error reports - for "paranoid" requests. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(paranoid_view, check_for_vars=False) - - with self.settings(DEBUG=False): - self.verify_paranoid_response(paranoid_view, check_for_vars=False) - - def test_custom_exception_reporter_filter(self): - """ - Ensure that it's possible to assign an exception reporter filter to - the request to bypass the one set in DEFAULT_EXCEPTION_REPORTER_FILTER. - """ - with self.settings(DEBUG=True): - self.verify_unsafe_response(custom_exception_reporter_filter_view, - check_for_vars=False) - - with self.settings(DEBUG=False): - self.verify_unsafe_response(custom_exception_reporter_filter_view, - check_for_vars=False) diff --git a/tests/view_tests/tests/defaults.py b/tests/view_tests/tests/defaults.py deleted file mode 100644 index 5efd338d34..0000000000 --- a/tests/view_tests/tests/defaults.py +++ /dev/null @@ -1,61 +0,0 @@ -from __future__ import absolute_import, unicode_literals - -from django.contrib.contenttypes.models import ContentType -from django.test import TestCase -from django.test.utils import setup_test_template_loader, restore_template_loaders - -from ..models import Author, Article, UrlArticle - - -class DefaultsTests(TestCase): - """Test django views in django/views/defaults.py""" - fixtures = ['testdata.json'] - non_existing_urls = ['/views/non_existing_url/', # this is in urls.py - '/views/other_non_existing_url/'] # this NOT in urls.py - - def test_page_not_found(self): - "A 404 status is returned by the page_not_found view" - for url in self.non_existing_urls: - response = self.client.get(url) - self.assertEqual(response.status_code, 404) - - def test_csrf_token_in_404(self): - """ - The 404 page should have the csrf_token available in the context - """ - # See ticket #14565 - for url in self.non_existing_urls: - response = self.client.get(url) - csrf_token = response.context['csrf_token'] - self.assertNotEqual(str(csrf_token), 'NOTPROVIDED') - self.assertNotEqual(str(csrf_token), '') - - def test_server_error(self): - "The server_error view raises a 500 status" - response = self.client.get('/views/server_error/') - self.assertEqual(response.status_code, 500) - - def test_custom_templates(self): - """ - Test that 404.html and 500.html templates are picked by their respective - handler. - """ - setup_test_template_loader( - {'404.html': 'This is a test template for a 404 error.', - '500.html': 'This is a test template for a 500 error.'} - ) - try: - for code, url in ((404, '/views/non_existing_url/'), (500, '/views/server_error/')): - response = self.client.get(url) - self.assertContains(response, "test template for a %d error" % code, - status_code=code) - finally: - restore_template_loaders() - - def test_get_absolute_url_attributes(self): - "A model can set attributes on the get_absolute_url method" - self.assertTrue(getattr(UrlArticle.get_absolute_url, 'purge', False), - 'The attributes of the original get_absolute_url must be added.') - article = UrlArticle.objects.get(pk=1) - self.assertTrue(getattr(article.get_absolute_url, 'purge', False), - 'The attributes of the original get_absolute_url must be added.') diff --git a/tests/view_tests/tests/i18n.py b/tests/view_tests/tests/i18n.py deleted file mode 100644 index d186fab335..0000000000 --- a/tests/view_tests/tests/i18n.py +++ /dev/null @@ -1,221 +0,0 @@ -# -*- coding:utf-8 -*- -from __future__ import absolute_import - -import gettext -import os -from os import path - -from django.conf import settings -from django.core.urlresolvers import reverse -from django.test import LiveServerTestCase, TestCase -from django.test.utils import override_settings -from django.utils import six, unittest -from django.utils._os import upath -from django.utils.translation import override -from django.utils.text import javascript_quote - -try: - from selenium.webdriver.firefox import webdriver as firefox -except ImportError: - firefox = None - -from ..urls import locale_dir - - -class I18NTests(TestCase): - """ Tests django views in django/views/i18n.py """ - - def test_setlang(self): - """ - The set_language view can be used to change the session language. - - The user is redirected to the 'next' argument if provided. - """ - for lang_code, lang_name in settings.LANGUAGES: - post_data = dict(language=lang_code, next='/views/') - response = self.client.post('/views/i18n/setlang/', data=post_data) - self.assertRedirects(response, 'http://testserver/views/') - self.assertEqual(self.client.session['django_language'], lang_code) - - def test_setlang_unsafe_next(self): - """ - The set_language view only redirects to the 'next' argument if it is - "safe". - """ - lang_code, lang_name = settings.LANGUAGES[0] - post_data = dict(language=lang_code, next='//unsafe/redirection/') - response = self.client.post('/views/i18n/setlang/', data=post_data) - self.assertEqual(response.url, 'http://testserver/') - self.assertEqual(self.client.session['django_language'], lang_code) - - def test_setlang_reversal(self): - self.assertEqual(reverse('set_language'), '/views/i18n/setlang/') - - def test_jsi18n(self): - """The javascript_catalog can be deployed with language settings""" - for lang_code in ['es', 'fr', 'ru']: - with override(lang_code): - catalog = gettext.translation('djangojs', locale_dir, [lang_code]) - if six.PY3: - trans_txt = catalog.gettext('this is to be translated') - else: - trans_txt = catalog.ugettext('this is to be translated') - response = self.client.get('/views/jsi18n/') - # in response content must to be a line like that: - # catalog['this is to be translated'] = 'same_that_trans_txt' - # javascript_quote is used to be able to check unicode strings - self.assertContains(response, javascript_quote(trans_txt), 1) - if lang_code == 'fr': - # Message with context (msgctxt) - self.assertContains(response, "['month name\x04May'] = 'mai';", 1) - - -class JsI18NTests(TestCase): - """ - Tests django views in django/views/i18n.py that need to change - settings.LANGUAGE_CODE. - """ - - def test_jsi18n_with_missing_en_files(self): - """ - The javascript_catalog shouldn't load the fallback language in the - case that the current selected language is actually the one translated - from, and hence missing translation files completely. - - This happens easily when you're translating from English to other - languages and you've set settings.LANGUAGE_CODE to some other language - than English. - """ - with self.settings(LANGUAGE_CODE='es'): - with override('en-us'): - response = self.client.get('/views/jsi18n/') - self.assertNotContains(response, 'esto tiene que ser traducido') - - def test_jsi18n_fallback_language(self): - """ - Let's make sure that the fallback language is still working properly - in cases where the selected language cannot be found. - """ - with self.settings(LANGUAGE_CODE='fr'): - with override('fi'): - response = self.client.get('/views/jsi18n/') - self.assertContains(response, 'il faut le traduire') - - def testI18NLanguageNonEnglishDefault(self): - """ - Check if the Javascript i18n view returns an empty language catalog - if the default language is non-English, the selected language - is English and there is not 'en' translation available. See #13388, - #3594 and #13726 for more details. - """ - with self.settings(LANGUAGE_CODE='fr'): - with override('en-us'): - response = self.client.get('/views/jsi18n/') - self.assertNotContains(response, 'Choisir une heure') - - def test_nonenglish_default_english_userpref(self): - """ - Same as above with the difference that there IS an 'en' translation - available. The Javascript i18n view must return a NON empty language catalog - with the proper English translations. See #13726 for more details. - """ - extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app0'] - with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps): - with override('en-us'): - response = self.client.get('/views/jsi18n_english_translation/') - self.assertContains(response, javascript_quote('this app0 string is to be translated')) - - def testI18NLanguageNonEnglishFallback(self): - """ - Makes sure that the fallback language is still working properly - in cases where the selected language cannot be found. - """ - with self.settings(LANGUAGE_CODE='fr'): - with override('none'): - response = self.client.get('/views/jsi18n/') - self.assertContains(response, 'Choisir une heure') - - -class JsI18NTestsMultiPackage(TestCase): - """ - Tests for django views in django/views/i18n.py that need to change - settings.LANGUAGE_CODE and merge JS translation from several packages. - """ - def testI18NLanguageEnglishDefault(self): - """ - Check if the JavaScript i18n view returns a complete language catalog - if the default language is en-us, the selected language has a - translation available and a catalog composed by djangojs domain - translations of multiple Python packages is requested. See #13388, - #3594 and #13514 for more details. - """ - extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app1', 'view_tests.app2'] - with self.settings(LANGUAGE_CODE='en-us', INSTALLED_APPS=extended_apps): - with override('fr'): - response = self.client.get('/views/jsi18n_multi_packages1/') - self.assertContains(response, javascript_quote('il faut traduire cette chaîne de caractères de app1')) - - def testI18NDifferentNonEnLangs(self): - """ - Similar to above but with neither default or requested language being - English. - """ - extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app3', 'view_tests.app4'] - with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps): - with override('es-ar'): - response = self.client.get('/views/jsi18n_multi_packages2/') - self.assertContains(response, javascript_quote('este texto de app3 debe ser traducido')) - - def testI18NWithLocalePaths(self): - extended_locale_paths = settings.LOCALE_PATHS + ( - path.join(path.dirname( - path.dirname(path.abspath(upath(__file__)))), 'app3', 'locale'),) - with self.settings(LANGUAGE_CODE='es-ar', LOCALE_PATHS=extended_locale_paths): - with override('es-ar'): - response = self.client.get('/views/jsi18n/') - self.assertContains(response, - javascript_quote('este texto de app3 debe ser traducido')) - - -skip_selenium = not os.environ.get('DJANGO_SELENIUM_TESTS', False) - - -@unittest.skipIf(skip_selenium, 'Selenium tests not requested') -@unittest.skipUnless(firefox, 'Selenium not installed') -class JavascriptI18nTests(LiveServerTestCase): - urls = 'view_tests.urls' - - @classmethod - def setUpClass(cls): - cls.selenium = firefox.WebDriver() - super(JavascriptI18nTests, cls).setUpClass() - - @classmethod - def tearDownClass(cls): - cls.selenium.quit() - super(JavascriptI18nTests, cls).tearDownClass() - - @override_settings(LANGUAGE_CODE='de') - def test_javascript_gettext(self): - extended_apps = list(settings.INSTALLED_APPS) + ['view_tests'] - with self.settings(INSTALLED_APPS=extended_apps): - self.selenium.get('%s%s' % (self.live_server_url, '/jsi18n_template/')) - - elem = self.selenium.find_element_by_id("gettext") - self.assertEqual(elem.text, "Entfernen") - elem = self.selenium.find_element_by_id("ngettext_sing") - self.assertEqual(elem.text, "1 Element") - elem = self.selenium.find_element_by_id("ngettext_plur") - self.assertEqual(elem.text, "455 Elemente") - elem = self.selenium.find_element_by_id("pgettext") - self.assertEqual(elem.text, "Kann") - elem = self.selenium.find_element_by_id("npgettext_sing") - self.assertEqual(elem.text, "1 Resultat") - elem = self.selenium.find_element_by_id("npgettext_plur") - self.assertEqual(elem.text, "455 Resultate") - - def test_escaping(self): - extended_apps = list(settings.INSTALLED_APPS) + ['view_tests'] - with self.settings(INSTALLED_APPS=extended_apps): - response = self.client.get('%s%s' % (self.live_server_url, '/jsi18n_admin/')) - self.assertContains(response, '\\x04') diff --git a/tests/view_tests/tests/shortcuts.py b/tests/view_tests/tests/shortcuts.py deleted file mode 100644 index 74c556f41a..0000000000 --- a/tests/view_tests/tests/shortcuts.py +++ /dev/null @@ -1,59 +0,0 @@ -from django.conf import settings -from django.test import TestCase -from django.test.utils import override_settings - -@override_settings( - TEMPLATE_CONTEXT_PROCESSORS=('django.core.context_processors.static',), - STATIC_URL='/path/to/static/media/', -) -class ShortcutTests(TestCase): - urls = 'view_tests.generic_urls' - - def test_render_to_response(self): - response = self.client.get('/shortcuts/render_to_response/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, b'FOO.BAR..\n') - self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') - - def test_render_to_response_with_request_context(self): - response = self.client.get('/shortcuts/render_to_response/request_context/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') - self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') - - def test_render_to_response_with_content_type(self): - response = self.client.get('/shortcuts/render_to_response/content_type/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, b'FOO.BAR..\n') - self.assertEqual(response['Content-Type'], 'application/x-rendertest') - - def test_render(self): - response = self.client.get('/shortcuts/render/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') - self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') - self.assertEqual(response.context.current_app, None) - - def test_render_with_base_context(self): - response = self.client.get('/shortcuts/render/base_context/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, b'FOO.BAR..\n') - self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') - - def test_render_with_content_type(self): - response = self.client.get('/shortcuts/render/content_type/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') - self.assertEqual(response['Content-Type'], 'application/x-rendertest') - - def test_render_with_status(self): - response = self.client.get('/shortcuts/render/status/') - self.assertEqual(response.status_code, 403) - self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') - - def test_render_with_current_app(self): - response = self.client.get('/shortcuts/render/current_app/') - self.assertEqual(response.context.current_app, "foobar_app") - - def test_render_with_current_app_conflict(self): - self.assertRaises(ValueError, self.client.get, '/shortcuts/render/current_app_conflict/') diff --git a/tests/view_tests/tests/specials.py b/tests/view_tests/tests/specials.py deleted file mode 100644 index fc1dd98c0e..0000000000 --- a/tests/view_tests/tests/specials.py +++ /dev/null @@ -1,39 +0,0 @@ -# coding: utf-8 -from __future__ import unicode_literals - -from django.test import TestCase - - -class URLHandling(TestCase): - """ - Tests for URL handling in views and responses. - """ - urls = 'view_tests.generic_urls' - redirect_target = "/%E4%B8%AD%E6%96%87/target/" - - def test_combining_redirect(self): - """ - Tests that redirecting to an IRI, requiring encoding before we use it - in an HTTP response, is handled correctly. In this case the arg to - HttpRedirect is ASCII but the current request path contains non-ASCII - characters so this test ensures the creation of the full path with a - base non-ASCII part is handled correctly. - """ - response = self.client.get('/中文/') - self.assertRedirects(response, self.redirect_target) - - def test_nonascii_redirect(self): - """ - Tests that a non-ASCII argument to HttpRedirect is handled properly. - """ - response = self.client.get('/nonascii_redirect/') - self.assertRedirects(response, self.redirect_target) - - def test_permanent_nonascii_redirect(self): - """ - Tests that a non-ASCII argument to HttpPermanentRedirect is handled - properly. - """ - response = self.client.get('/permanent_nonascii_redirect/') - self.assertRedirects(response, self.redirect_target, status_code=301) - diff --git a/tests/view_tests/tests/static.py b/tests/view_tests/tests/static.py deleted file mode 100644 index bdd9fbfc0b..0000000000 --- a/tests/view_tests/tests/static.py +++ /dev/null @@ -1,120 +0,0 @@ -from __future__ import absolute_import - -import mimetypes -from os import path -import unittest - -from django.conf.urls.static import static -from django.http import HttpResponseNotModified -from django.test import TestCase -from django.test.utils import override_settings -from django.utils.http import http_date -from django.views.static import was_modified_since - -from .. import urls -from ..urls import media_dir - - -@override_settings(DEBUG=True) -class StaticTests(TestCase): - """Tests django views in django/views/static.py""" - - prefix = 'site_media' - - def test_serve(self): - "The static view can serve static media" - media_files = ['file.txt', 'file.txt.gz'] - for filename in media_files: - response = self.client.get('/views/%s/%s' % (self.prefix, filename)) - response_content = b''.join(response) - file_path = path.join(media_dir, filename) - with open(file_path, 'rb') as fp: - self.assertEqual(fp.read(), response_content) - self.assertEqual(len(response_content), int(response['Content-Length'])) - self.assertEqual(mimetypes.guess_type(file_path)[1], response.get('Content-Encoding', None)) - - def test_unknown_mime_type(self): - response = self.client.get('/views/%s/file.unknown' % self.prefix) - self.assertEqual('application/octet-stream', response['Content-Type']) - - def test_copes_with_empty_path_component(self): - file_name = 'file.txt' - response = self.client.get('/views/%s//%s' % (self.prefix, file_name)) - response_content = b''.join(response) - with open(path.join(media_dir, file_name), 'rb') as fp: - self.assertEqual(fp.read(), response_content) - - def test_is_modified_since(self): - file_name = 'file.txt' - response = self.client.get('/views/%s/%s' % (self.prefix, file_name), - HTTP_IF_MODIFIED_SINCE='Thu, 1 Jan 1970 00:00:00 GMT') - response_content = b''.join(response) - with open(path.join(media_dir, file_name), 'rb') as fp: - self.assertEqual(fp.read(), response_content) - - def test_not_modified_since(self): - file_name = 'file.txt' - response = self.client.get( - '/views/%s/%s' % (self.prefix, file_name), - HTTP_IF_MODIFIED_SINCE='Mon, 18 Jan 2038 05:14:07 GMT' - # This is 24h before max Unix time. Remember to fix Django and - # update this test well before 2038 :) - ) - self.assertTrue(isinstance(response, HttpResponseNotModified)) - - def test_invalid_if_modified_since(self): - """Handle bogus If-Modified-Since values gracefully - - Assume that a file is modified since an invalid timestamp as per RFC - 2616, section 14.25. - """ - file_name = 'file.txt' - invalid_date = 'Mon, 28 May 999999999999 28:25:26 GMT' - response = self.client.get('/views/%s/%s' % (self.prefix, file_name), - HTTP_IF_MODIFIED_SINCE=invalid_date) - response_content = b''.join(response) - with open(path.join(media_dir, file_name), 'rb') as fp: - self.assertEqual(fp.read(), response_content) - self.assertEqual(len(response_content), - int(response['Content-Length'])) - - def test_invalid_if_modified_since2(self): - """Handle even more bogus If-Modified-Since values gracefully - - Assume that a file is modified since an invalid timestamp as per RFC - 2616, section 14.25. - """ - file_name = 'file.txt' - invalid_date = ': 1291108438, Wed, 20 Oct 2010 14:05:00 GMT' - response = self.client.get('/views/%s/%s' % (self.prefix, file_name), - HTTP_IF_MODIFIED_SINCE=invalid_date) - response_content = b''.join(response) - with open(path.join(media_dir, file_name), 'rb') as fp: - self.assertEqual(fp.read(), response_content) - self.assertEqual(len(response_content), - int(response['Content-Length'])) - - -class StaticHelperTest(StaticTests): - """ - Test case to make sure the static URL pattern helper works as expected - """ - def setUp(self): - super(StaticHelperTest, self).setUp() - self._old_views_urlpatterns = urls.urlpatterns[:] - urls.urlpatterns += static('/media/', document_root=media_dir) - - def tearDown(self): - super(StaticHelperTest, self).tearDown() - urls.urlpatterns = self._old_views_urlpatterns - - -class StaticUtilsTests(unittest.TestCase): - def test_was_modified_since_fp(self): - """ - Test that a floating point mtime does not disturb was_modified_since. - (#18675) - """ - mtime = 1343416141.107817 - header = http_date(mtime) - self.assertFalse(was_modified_since(header, mtime)) diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py new file mode 100644 index 0000000000..b44cd88abe --- /dev/null +++ b/tests/view_tests/tests/test_debug.py @@ -0,0 +1,559 @@ +# -*- coding: utf-8 -*- +# This coding header is significant for tests, as the debug view is parsing +# files to search for such a header to decode the source file content +from __future__ import absolute_import, unicode_literals + +import inspect +import os +import sys + +from django.core import mail +from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.urlresolvers import reverse +from django.test import TestCase, RequestFactory +from django.test.utils import (override_settings, setup_test_template_loader, + restore_template_loaders) +from django.utils.encoding import force_text +from django.views.debug import ExceptionReporter + +from .. import BrokenException, except_args +from ..views import (sensitive_view, non_sensitive_view, paranoid_view, + custom_exception_reporter_filter_view, sensitive_method_view, + sensitive_args_function_caller, sensitive_kwargs_function_caller) + + +@override_settings(DEBUG=True, TEMPLATE_DEBUG=True) +class DebugViewTests(TestCase): + urls = "view_tests.urls" + + def test_files(self): + response = self.client.get('/raises/') + self.assertEqual(response.status_code, 500) + + data = { + 'file_data.txt': SimpleUploadedFile('file_data.txt', b'haha'), + } + response = self.client.post('/raises/', data) + self.assertContains(response, 'file_data.txt', status_code=500) + self.assertNotContains(response, 'haha', status_code=500) + + def test_403(self): + # Ensure no 403.html template exists to test the default case. + setup_test_template_loader({}) + try: + response = self.client.get('/views/raises403/') + self.assertContains(response, '

403 Forbidden

', status_code=403) + finally: + restore_template_loaders() + + def test_403_template(self): + # Set up a test 403.html template. + setup_test_template_loader( + {'403.html': 'This is a test template for a 403 Forbidden error.'} + ) + try: + response = self.client.get('/views/raises403/') + self.assertContains(response, 'test template', status_code=403) + finally: + restore_template_loaders() + + def test_404(self): + response = self.client.get('/views/raises404/') + self.assertEqual(response.status_code, 404) + + def test_view_exceptions(self): + for n in range(len(except_args)): + self.assertRaises(BrokenException, self.client.get, + reverse('view_exception', args=(n,))) + + def test_template_exceptions(self): + for n in range(len(except_args)): + try: + self.client.get(reverse('template_exception', args=(n,))) + except Exception: + raising_loc = inspect.trace()[-1][-2][0].strip() + self.assertFalse(raising_loc.find('raise BrokenException') == -1, + "Failed to find 'raise BrokenException' in last frame of traceback, instead found: %s" % + raising_loc) + + def test_template_loader_postmortem(self): + response = self.client.get(reverse('raises_template_does_not_exist')) + template_path = os.path.join('templates', 'i_dont_exist.html') + self.assertContains(response, template_path, status_code=500) + + +class ExceptionReporterTests(TestCase): + rf = RequestFactory() + + def test_request_and_exception(self): + "A simple exception report can be generated" + try: + request = self.rf.get('/test_view/') + raise ValueError("Can't find my keys") + except ValueError: + exc_type, exc_value, tb = sys.exc_info() + reporter = ExceptionReporter(request, exc_type, exc_value, tb) + html = reporter.get_traceback_html() + self.assertIn('

ValueError at /test_view/

', html) + self.assertIn('
Can't find my keys
', html) + self.assertIn('Request Method:', html) + self.assertIn('Request URL:', html) + self.assertIn('Exception Type:', html) + self.assertIn('Exception Value:', html) + self.assertIn('

Traceback ', html) + self.assertIn('

Request information

', html) + self.assertNotIn('

Request data not supplied

', html) + + def test_no_request(self): + "An exception report can be generated without request" + try: + raise ValueError("Can't find my keys") + except ValueError: + exc_type, exc_value, tb = sys.exc_info() + reporter = ExceptionReporter(None, exc_type, exc_value, tb) + html = reporter.get_traceback_html() + self.assertIn('

ValueError

', html) + self.assertIn('
Can't find my keys
', html) + self.assertNotIn('Request Method:', html) + self.assertNotIn('Request URL:', html) + self.assertIn('Exception Type:', html) + self.assertIn('Exception Value:', html) + self.assertIn('

Traceback ', html) + self.assertIn('

Request information

', html) + self.assertIn('

Request data not supplied

', html) + + def test_no_exception(self): + "An exception report can be generated for just a request" + request = self.rf.get('/test_view/') + reporter = ExceptionReporter(request, None, None, None) + html = reporter.get_traceback_html() + self.assertIn('

Report at /test_view/

', html) + self.assertIn('
No exception supplied
', html) + self.assertIn('Request Method:', html) + self.assertIn('Request URL:', html) + self.assertNotIn('Exception Type:', html) + self.assertNotIn('Exception Value:', html) + self.assertNotIn('

Traceback ', html) + self.assertIn('

Request information

', html) + self.assertNotIn('

Request data not supplied

', html) + + def test_request_and_message(self): + "A message can be provided in addition to a request" + request = self.rf.get('/test_view/') + reporter = ExceptionReporter(request, None, "I'm a little teapot", None) + html = reporter.get_traceback_html() + self.assertIn('

Report at /test_view/

', html) + self.assertIn('
I'm a little teapot
', html) + self.assertIn('Request Method:', html) + self.assertIn('Request URL:', html) + self.assertNotIn('Exception Type:', html) + self.assertNotIn('Exception Value:', html) + self.assertNotIn('

Traceback ', html) + self.assertIn('

Request information

', html) + self.assertNotIn('

Request data not supplied

', html) + + def test_message_only(self): + reporter = ExceptionReporter(None, None, "I'm a little teapot", None) + html = reporter.get_traceback_html() + self.assertIn('

Report

', html) + self.assertIn('
I'm a little teapot
', html) + self.assertNotIn('Request Method:', html) + self.assertNotIn('Request URL:', html) + self.assertNotIn('Exception Type:', html) + self.assertNotIn('Exception Value:', html) + self.assertNotIn('

Traceback ', html) + self.assertIn('

Request information

', html) + self.assertIn('

Request data not supplied

', html) + + +class PlainTextReportTests(TestCase): + rf = RequestFactory() + + def test_request_and_exception(self): + "A simple exception report can be generated" + try: + request = self.rf.get('/test_view/') + raise ValueError("Can't find my keys") + except ValueError: + exc_type, exc_value, tb = sys.exc_info() + reporter = ExceptionReporter(request, exc_type, exc_value, tb) + text = reporter.get_traceback_text() + self.assertIn('ValueError at /test_view/', text) + self.assertIn("Can't find my keys", text) + self.assertIn('Request Method:', text) + self.assertIn('Request URL:', text) + self.assertIn('Exception Type:', text) + self.assertIn('Exception Value:', text) + self.assertIn('Traceback:', text) + self.assertIn('Request information:', text) + self.assertNotIn('Request data not supplied', text) + + def test_no_request(self): + "An exception report can be generated without request" + try: + raise ValueError("Can't find my keys") + except ValueError: + exc_type, exc_value, tb = sys.exc_info() + reporter = ExceptionReporter(None, exc_type, exc_value, tb) + text = reporter.get_traceback_text() + self.assertIn('ValueError', text) + self.assertIn("Can't find my keys", text) + self.assertNotIn('Request Method:', text) + self.assertNotIn('Request URL:', text) + self.assertIn('Exception Type:', text) + self.assertIn('Exception Value:', text) + self.assertIn('Traceback:', text) + self.assertIn('Request data not supplied', text) + + def test_no_exception(self): + "An exception report can be generated for just a request" + request = self.rf.get('/test_view/') + reporter = ExceptionReporter(request, None, None, None) + text = reporter.get_traceback_text() + + def test_request_and_message(self): + "A message can be provided in addition to a request" + request = self.rf.get('/test_view/') + reporter = ExceptionReporter(request, None, "I'm a little teapot", None) + text = reporter.get_traceback_text() + + def test_message_only(self): + reporter = ExceptionReporter(None, None, "I'm a little teapot", None) + text = reporter.get_traceback_text() + + +class ExceptionReportTestMixin(object): + + # Mixin used in the ExceptionReporterFilterTests and + # AjaxResponseExceptionReporterFilter tests below + + breakfast_data = {'sausage-key': 'sausage-value', + 'baked-beans-key': 'baked-beans-value', + 'hash-brown-key': 'hash-brown-value', + 'bacon-key': 'bacon-value',} + + def verify_unsafe_response(self, view, check_for_vars=True, + check_for_POST_params=True): + """ + Asserts that potentially sensitive info are displayed in the response. + """ + request = self.rf.post('/some_url/', self.breakfast_data) + response = view(request) + if check_for_vars: + # All variables are shown. + self.assertContains(response, 'cooked_eggs', status_code=500) + self.assertContains(response, 'scrambled', status_code=500) + self.assertContains(response, 'sauce', status_code=500) + self.assertContains(response, 'worcestershire', status_code=500) + if check_for_POST_params: + for k, v in self.breakfast_data.items(): + # All POST parameters are shown. + self.assertContains(response, k, status_code=500) + self.assertContains(response, v, status_code=500) + + def verify_safe_response(self, view, check_for_vars=True, + check_for_POST_params=True): + """ + Asserts that certain sensitive info are not displayed in the response. + """ + request = self.rf.post('/some_url/', self.breakfast_data) + response = view(request) + if check_for_vars: + # Non-sensitive variable's name and value are shown. + self.assertContains(response, 'cooked_eggs', status_code=500) + self.assertContains(response, 'scrambled', status_code=500) + # Sensitive variable's name is shown but not its value. + self.assertContains(response, 'sauce', status_code=500) + self.assertNotContains(response, 'worcestershire', status_code=500) + if check_for_POST_params: + for k, v in self.breakfast_data.items(): + # All POST parameters' names are shown. + self.assertContains(response, k, status_code=500) + # Non-sensitive POST parameters' values are shown. + self.assertContains(response, 'baked-beans-value', status_code=500) + self.assertContains(response, 'hash-brown-value', status_code=500) + # Sensitive POST parameters' values are not shown. + self.assertNotContains(response, 'sausage-value', status_code=500) + self.assertNotContains(response, 'bacon-value', status_code=500) + + def verify_paranoid_response(self, view, check_for_vars=True, + check_for_POST_params=True): + """ + Asserts that no variables or POST parameters are displayed in the response. + """ + request = self.rf.post('/some_url/', self.breakfast_data) + response = view(request) + if check_for_vars: + # Show variable names but not their values. + self.assertContains(response, 'cooked_eggs', status_code=500) + self.assertNotContains(response, 'scrambled', status_code=500) + self.assertContains(response, 'sauce', status_code=500) + self.assertNotContains(response, 'worcestershire', status_code=500) + if check_for_POST_params: + for k, v in self.breakfast_data.items(): + # All POST parameters' names are shown. + self.assertContains(response, k, status_code=500) + # No POST parameters' values are shown. + self.assertNotContains(response, v, status_code=500) + + def verify_unsafe_email(self, view, check_for_POST_params=True): + """ + Asserts that potentially sensitive info are displayed in the email report. + """ + with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): + mail.outbox = [] # Empty outbox + request = self.rf.post('/some_url/', self.breakfast_data) + response = view(request) + self.assertEqual(len(mail.outbox), 1) + email = mail.outbox[0] + + # Frames vars are never shown in plain text email reports. + body_plain = force_text(email.body) + self.assertNotIn('cooked_eggs', body_plain) + self.assertNotIn('scrambled', body_plain) + self.assertNotIn('sauce', body_plain) + self.assertNotIn('worcestershire', body_plain) + + # Frames vars are shown in html email reports. + body_html = force_text(email.alternatives[0][0]) + self.assertIn('cooked_eggs', body_html) + self.assertIn('scrambled', body_html) + self.assertIn('sauce', body_html) + self.assertIn('worcestershire', body_html) + + if check_for_POST_params: + for k, v in self.breakfast_data.items(): + # All POST parameters are shown. + self.assertIn(k, body_plain) + self.assertIn(v, body_plain) + self.assertIn(k, body_html) + self.assertIn(v, body_html) + + def verify_safe_email(self, view, check_for_POST_params=True): + """ + Asserts that certain sensitive info are not displayed in the email report. + """ + with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): + mail.outbox = [] # Empty outbox + request = self.rf.post('/some_url/', self.breakfast_data) + response = view(request) + self.assertEqual(len(mail.outbox), 1) + email = mail.outbox[0] + + # Frames vars are never shown in plain text email reports. + body_plain = force_text(email.body) + self.assertNotIn('cooked_eggs', body_plain) + self.assertNotIn('scrambled', body_plain) + self.assertNotIn('sauce', body_plain) + self.assertNotIn('worcestershire', body_plain) + + # Frames vars are shown in html email reports. + body_html = force_text(email.alternatives[0][0]) + self.assertIn('cooked_eggs', body_html) + self.assertIn('scrambled', body_html) + self.assertIn('sauce', body_html) + self.assertNotIn('worcestershire', body_html) + + if check_for_POST_params: + for k, v in self.breakfast_data.items(): + # All POST parameters' names are shown. + self.assertIn(k, body_plain) + # Non-sensitive POST parameters' values are shown. + self.assertIn('baked-beans-value', body_plain) + self.assertIn('hash-brown-value', body_plain) + self.assertIn('baked-beans-value', body_html) + self.assertIn('hash-brown-value', body_html) + # Sensitive POST parameters' values are not shown. + self.assertNotIn('sausage-value', body_plain) + self.assertNotIn('bacon-value', body_plain) + self.assertNotIn('sausage-value', body_html) + self.assertNotIn('bacon-value', body_html) + + def verify_paranoid_email(self, view): + """ + Asserts that no variables or POST parameters are displayed in the email report. + """ + with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): + mail.outbox = [] # Empty outbox + request = self.rf.post('/some_url/', self.breakfast_data) + response = view(request) + self.assertEqual(len(mail.outbox), 1) + email = mail.outbox[0] + # Frames vars are never shown in plain text email reports. + body = force_text(email.body) + self.assertNotIn('cooked_eggs', body) + self.assertNotIn('scrambled', body) + self.assertNotIn('sauce', body) + self.assertNotIn('worcestershire', body) + for k, v in self.breakfast_data.items(): + # All POST parameters' names are shown. + self.assertIn(k, body) + # No POST parameters' values are shown. + self.assertNotIn(v, body) + + +class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin): + """ + Ensure that sensitive information can be filtered out of error reports. + Refs #14614. + """ + rf = RequestFactory() + + def test_non_sensitive_request(self): + """ + Ensure that everything (request info and frame variables) can bee seen + in the default error reports for non-sensitive requests. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(non_sensitive_view) + self.verify_unsafe_email(non_sensitive_view) + + with self.settings(DEBUG=False): + self.verify_unsafe_response(non_sensitive_view) + self.verify_unsafe_email(non_sensitive_view) + + def test_sensitive_request(self): + """ + Ensure that sensitive POST parameters and frame variables cannot be + seen in the default error reports for sensitive requests. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(sensitive_view) + self.verify_unsafe_email(sensitive_view) + + with self.settings(DEBUG=False): + self.verify_safe_response(sensitive_view) + self.verify_safe_email(sensitive_view) + + def test_paranoid_request(self): + """ + Ensure that no POST parameters and frame variables can be seen in the + default error reports for "paranoid" requests. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(paranoid_view) + self.verify_unsafe_email(paranoid_view) + + with self.settings(DEBUG=False): + self.verify_paranoid_response(paranoid_view) + self.verify_paranoid_email(paranoid_view) + + def test_custom_exception_reporter_filter(self): + """ + Ensure that it's possible to assign an exception reporter filter to + the request to bypass the one set in DEFAULT_EXCEPTION_REPORTER_FILTER. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(custom_exception_reporter_filter_view) + self.verify_unsafe_email(custom_exception_reporter_filter_view) + + with self.settings(DEBUG=False): + self.verify_unsafe_response(custom_exception_reporter_filter_view) + self.verify_unsafe_email(custom_exception_reporter_filter_view) + + def test_sensitive_method(self): + """ + Ensure that the sensitive_variables decorator works with object + methods. + Refs #18379. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(sensitive_method_view, + check_for_POST_params=False) + self.verify_unsafe_email(sensitive_method_view, + check_for_POST_params=False) + + with self.settings(DEBUG=False): + self.verify_safe_response(sensitive_method_view, + check_for_POST_params=False) + self.verify_safe_email(sensitive_method_view, + check_for_POST_params=False) + + def test_sensitive_function_arguments(self): + """ + Ensure that sensitive variables don't leak in the sensitive_variables + decorator's frame, when those variables are passed as arguments to the + decorated function. + Refs #19453. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(sensitive_args_function_caller) + self.verify_unsafe_email(sensitive_args_function_caller) + + with self.settings(DEBUG=False): + self.verify_safe_response(sensitive_args_function_caller, check_for_POST_params=False) + self.verify_safe_email(sensitive_args_function_caller, check_for_POST_params=False) + + def test_sensitive_function_keyword_arguments(self): + """ + Ensure that sensitive variables don't leak in the sensitive_variables + decorator's frame, when those variables are passed as keyword arguments + to the decorated function. + Refs #19453. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(sensitive_kwargs_function_caller) + self.verify_unsafe_email(sensitive_kwargs_function_caller) + + with self.settings(DEBUG=False): + self.verify_safe_response(sensitive_kwargs_function_caller, check_for_POST_params=False) + self.verify_safe_email(sensitive_kwargs_function_caller, check_for_POST_params=False) + + +class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin): + """ + Ensure that sensitive information can be filtered out of error reports. + + Here we specifically test the plain text 500 debug-only error page served + when it has been detected the request was sent by JS code. We don't check + for (non)existence of frames vars in the traceback information section of + the response content because we don't include them in these error pages. + Refs #14614. + """ + rf = RequestFactory(HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + def test_non_sensitive_request(self): + """ + Ensure that request info can bee seen in the default error reports for + non-sensitive requests. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(non_sensitive_view, check_for_vars=False) + + with self.settings(DEBUG=False): + self.verify_unsafe_response(non_sensitive_view, check_for_vars=False) + + def test_sensitive_request(self): + """ + Ensure that sensitive POST parameters cannot be seen in the default + error reports for sensitive requests. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(sensitive_view, check_for_vars=False) + + with self.settings(DEBUG=False): + self.verify_safe_response(sensitive_view, check_for_vars=False) + + def test_paranoid_request(self): + """ + Ensure that no POST parameters can be seen in the default error reports + for "paranoid" requests. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(paranoid_view, check_for_vars=False) + + with self.settings(DEBUG=False): + self.verify_paranoid_response(paranoid_view, check_for_vars=False) + + def test_custom_exception_reporter_filter(self): + """ + Ensure that it's possible to assign an exception reporter filter to + the request to bypass the one set in DEFAULT_EXCEPTION_REPORTER_FILTER. + """ + with self.settings(DEBUG=True): + self.verify_unsafe_response(custom_exception_reporter_filter_view, + check_for_vars=False) + + with self.settings(DEBUG=False): + self.verify_unsafe_response(custom_exception_reporter_filter_view, + check_for_vars=False) diff --git a/tests/view_tests/tests/test_defaults.py b/tests/view_tests/tests/test_defaults.py new file mode 100644 index 0000000000..5efd338d34 --- /dev/null +++ b/tests/view_tests/tests/test_defaults.py @@ -0,0 +1,61 @@ +from __future__ import absolute_import, unicode_literals + +from django.contrib.contenttypes.models import ContentType +from django.test import TestCase +from django.test.utils import setup_test_template_loader, restore_template_loaders + +from ..models import Author, Article, UrlArticle + + +class DefaultsTests(TestCase): + """Test django views in django/views/defaults.py""" + fixtures = ['testdata.json'] + non_existing_urls = ['/views/non_existing_url/', # this is in urls.py + '/views/other_non_existing_url/'] # this NOT in urls.py + + def test_page_not_found(self): + "A 404 status is returned by the page_not_found view" + for url in self.non_existing_urls: + response = self.client.get(url) + self.assertEqual(response.status_code, 404) + + def test_csrf_token_in_404(self): + """ + The 404 page should have the csrf_token available in the context + """ + # See ticket #14565 + for url in self.non_existing_urls: + response = self.client.get(url) + csrf_token = response.context['csrf_token'] + self.assertNotEqual(str(csrf_token), 'NOTPROVIDED') + self.assertNotEqual(str(csrf_token), '') + + def test_server_error(self): + "The server_error view raises a 500 status" + response = self.client.get('/views/server_error/') + self.assertEqual(response.status_code, 500) + + def test_custom_templates(self): + """ + Test that 404.html and 500.html templates are picked by their respective + handler. + """ + setup_test_template_loader( + {'404.html': 'This is a test template for a 404 error.', + '500.html': 'This is a test template for a 500 error.'} + ) + try: + for code, url in ((404, '/views/non_existing_url/'), (500, '/views/server_error/')): + response = self.client.get(url) + self.assertContains(response, "test template for a %d error" % code, + status_code=code) + finally: + restore_template_loaders() + + def test_get_absolute_url_attributes(self): + "A model can set attributes on the get_absolute_url method" + self.assertTrue(getattr(UrlArticle.get_absolute_url, 'purge', False), + 'The attributes of the original get_absolute_url must be added.') + article = UrlArticle.objects.get(pk=1) + self.assertTrue(getattr(article.get_absolute_url, 'purge', False), + 'The attributes of the original get_absolute_url must be added.') diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py new file mode 100644 index 0000000000..d186fab335 --- /dev/null +++ b/tests/view_tests/tests/test_i18n.py @@ -0,0 +1,221 @@ +# -*- coding:utf-8 -*- +from __future__ import absolute_import + +import gettext +import os +from os import path + +from django.conf import settings +from django.core.urlresolvers import reverse +from django.test import LiveServerTestCase, TestCase +from django.test.utils import override_settings +from django.utils import six, unittest +from django.utils._os import upath +from django.utils.translation import override +from django.utils.text import javascript_quote + +try: + from selenium.webdriver.firefox import webdriver as firefox +except ImportError: + firefox = None + +from ..urls import locale_dir + + +class I18NTests(TestCase): + """ Tests django views in django/views/i18n.py """ + + def test_setlang(self): + """ + The set_language view can be used to change the session language. + + The user is redirected to the 'next' argument if provided. + """ + for lang_code, lang_name in settings.LANGUAGES: + post_data = dict(language=lang_code, next='/views/') + response = self.client.post('/views/i18n/setlang/', data=post_data) + self.assertRedirects(response, 'http://testserver/views/') + self.assertEqual(self.client.session['django_language'], lang_code) + + def test_setlang_unsafe_next(self): + """ + The set_language view only redirects to the 'next' argument if it is + "safe". + """ + lang_code, lang_name = settings.LANGUAGES[0] + post_data = dict(language=lang_code, next='//unsafe/redirection/') + response = self.client.post('/views/i18n/setlang/', data=post_data) + self.assertEqual(response.url, 'http://testserver/') + self.assertEqual(self.client.session['django_language'], lang_code) + + def test_setlang_reversal(self): + self.assertEqual(reverse('set_language'), '/views/i18n/setlang/') + + def test_jsi18n(self): + """The javascript_catalog can be deployed with language settings""" + for lang_code in ['es', 'fr', 'ru']: + with override(lang_code): + catalog = gettext.translation('djangojs', locale_dir, [lang_code]) + if six.PY3: + trans_txt = catalog.gettext('this is to be translated') + else: + trans_txt = catalog.ugettext('this is to be translated') + response = self.client.get('/views/jsi18n/') + # in response content must to be a line like that: + # catalog['this is to be translated'] = 'same_that_trans_txt' + # javascript_quote is used to be able to check unicode strings + self.assertContains(response, javascript_quote(trans_txt), 1) + if lang_code == 'fr': + # Message with context (msgctxt) + self.assertContains(response, "['month name\x04May'] = 'mai';", 1) + + +class JsI18NTests(TestCase): + """ + Tests django views in django/views/i18n.py that need to change + settings.LANGUAGE_CODE. + """ + + def test_jsi18n_with_missing_en_files(self): + """ + The javascript_catalog shouldn't load the fallback language in the + case that the current selected language is actually the one translated + from, and hence missing translation files completely. + + This happens easily when you're translating from English to other + languages and you've set settings.LANGUAGE_CODE to some other language + than English. + """ + with self.settings(LANGUAGE_CODE='es'): + with override('en-us'): + response = self.client.get('/views/jsi18n/') + self.assertNotContains(response, 'esto tiene que ser traducido') + + def test_jsi18n_fallback_language(self): + """ + Let's make sure that the fallback language is still working properly + in cases where the selected language cannot be found. + """ + with self.settings(LANGUAGE_CODE='fr'): + with override('fi'): + response = self.client.get('/views/jsi18n/') + self.assertContains(response, 'il faut le traduire') + + def testI18NLanguageNonEnglishDefault(self): + """ + Check if the Javascript i18n view returns an empty language catalog + if the default language is non-English, the selected language + is English and there is not 'en' translation available. See #13388, + #3594 and #13726 for more details. + """ + with self.settings(LANGUAGE_CODE='fr'): + with override('en-us'): + response = self.client.get('/views/jsi18n/') + self.assertNotContains(response, 'Choisir une heure') + + def test_nonenglish_default_english_userpref(self): + """ + Same as above with the difference that there IS an 'en' translation + available. The Javascript i18n view must return a NON empty language catalog + with the proper English translations. See #13726 for more details. + """ + extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app0'] + with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps): + with override('en-us'): + response = self.client.get('/views/jsi18n_english_translation/') + self.assertContains(response, javascript_quote('this app0 string is to be translated')) + + def testI18NLanguageNonEnglishFallback(self): + """ + Makes sure that the fallback language is still working properly + in cases where the selected language cannot be found. + """ + with self.settings(LANGUAGE_CODE='fr'): + with override('none'): + response = self.client.get('/views/jsi18n/') + self.assertContains(response, 'Choisir une heure') + + +class JsI18NTestsMultiPackage(TestCase): + """ + Tests for django views in django/views/i18n.py that need to change + settings.LANGUAGE_CODE and merge JS translation from several packages. + """ + def testI18NLanguageEnglishDefault(self): + """ + Check if the JavaScript i18n view returns a complete language catalog + if the default language is en-us, the selected language has a + translation available and a catalog composed by djangojs domain + translations of multiple Python packages is requested. See #13388, + #3594 and #13514 for more details. + """ + extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app1', 'view_tests.app2'] + with self.settings(LANGUAGE_CODE='en-us', INSTALLED_APPS=extended_apps): + with override('fr'): + response = self.client.get('/views/jsi18n_multi_packages1/') + self.assertContains(response, javascript_quote('il faut traduire cette chaîne de caractères de app1')) + + def testI18NDifferentNonEnLangs(self): + """ + Similar to above but with neither default or requested language being + English. + """ + extended_apps = list(settings.INSTALLED_APPS) + ['view_tests.app3', 'view_tests.app4'] + with self.settings(LANGUAGE_CODE='fr', INSTALLED_APPS=extended_apps): + with override('es-ar'): + response = self.client.get('/views/jsi18n_multi_packages2/') + self.assertContains(response, javascript_quote('este texto de app3 debe ser traducido')) + + def testI18NWithLocalePaths(self): + extended_locale_paths = settings.LOCALE_PATHS + ( + path.join(path.dirname( + path.dirname(path.abspath(upath(__file__)))), 'app3', 'locale'),) + with self.settings(LANGUAGE_CODE='es-ar', LOCALE_PATHS=extended_locale_paths): + with override('es-ar'): + response = self.client.get('/views/jsi18n/') + self.assertContains(response, + javascript_quote('este texto de app3 debe ser traducido')) + + +skip_selenium = not os.environ.get('DJANGO_SELENIUM_TESTS', False) + + +@unittest.skipIf(skip_selenium, 'Selenium tests not requested') +@unittest.skipUnless(firefox, 'Selenium not installed') +class JavascriptI18nTests(LiveServerTestCase): + urls = 'view_tests.urls' + + @classmethod + def setUpClass(cls): + cls.selenium = firefox.WebDriver() + super(JavascriptI18nTests, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + cls.selenium.quit() + super(JavascriptI18nTests, cls).tearDownClass() + + @override_settings(LANGUAGE_CODE='de') + def test_javascript_gettext(self): + extended_apps = list(settings.INSTALLED_APPS) + ['view_tests'] + with self.settings(INSTALLED_APPS=extended_apps): + self.selenium.get('%s%s' % (self.live_server_url, '/jsi18n_template/')) + + elem = self.selenium.find_element_by_id("gettext") + self.assertEqual(elem.text, "Entfernen") + elem = self.selenium.find_element_by_id("ngettext_sing") + self.assertEqual(elem.text, "1 Element") + elem = self.selenium.find_element_by_id("ngettext_plur") + self.assertEqual(elem.text, "455 Elemente") + elem = self.selenium.find_element_by_id("pgettext") + self.assertEqual(elem.text, "Kann") + elem = self.selenium.find_element_by_id("npgettext_sing") + self.assertEqual(elem.text, "1 Resultat") + elem = self.selenium.find_element_by_id("npgettext_plur") + self.assertEqual(elem.text, "455 Resultate") + + def test_escaping(self): + extended_apps = list(settings.INSTALLED_APPS) + ['view_tests'] + with self.settings(INSTALLED_APPS=extended_apps): + response = self.client.get('%s%s' % (self.live_server_url, '/jsi18n_admin/')) + self.assertContains(response, '\\x04') diff --git a/tests/view_tests/tests/test_shortcuts.py b/tests/view_tests/tests/test_shortcuts.py new file mode 100644 index 0000000000..74c556f41a --- /dev/null +++ b/tests/view_tests/tests/test_shortcuts.py @@ -0,0 +1,59 @@ +from django.conf import settings +from django.test import TestCase +from django.test.utils import override_settings + +@override_settings( + TEMPLATE_CONTEXT_PROCESSORS=('django.core.context_processors.static',), + STATIC_URL='/path/to/static/media/', +) +class ShortcutTests(TestCase): + urls = 'view_tests.generic_urls' + + def test_render_to_response(self): + response = self.client.get('/shortcuts/render_to_response/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'FOO.BAR..\n') + self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') + + def test_render_to_response_with_request_context(self): + response = self.client.get('/shortcuts/render_to_response/request_context/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') + + def test_render_to_response_with_content_type(self): + response = self.client.get('/shortcuts/render_to_response/content_type/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'FOO.BAR..\n') + self.assertEqual(response['Content-Type'], 'application/x-rendertest') + + def test_render(self): + response = self.client.get('/shortcuts/render/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') + self.assertEqual(response.context.current_app, None) + + def test_render_with_base_context(self): + response = self.client.get('/shortcuts/render/base_context/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'FOO.BAR..\n') + self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') + + def test_render_with_content_type(self): + response = self.client.get('/shortcuts/render/content_type/') + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response['Content-Type'], 'application/x-rendertest') + + def test_render_with_status(self): + response = self.client.get('/shortcuts/render/status/') + self.assertEqual(response.status_code, 403) + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') + + def test_render_with_current_app(self): + response = self.client.get('/shortcuts/render/current_app/') + self.assertEqual(response.context.current_app, "foobar_app") + + def test_render_with_current_app_conflict(self): + self.assertRaises(ValueError, self.client.get, '/shortcuts/render/current_app_conflict/') diff --git a/tests/view_tests/tests/test_specials.py b/tests/view_tests/tests/test_specials.py new file mode 100644 index 0000000000..fc1dd98c0e --- /dev/null +++ b/tests/view_tests/tests/test_specials.py @@ -0,0 +1,39 @@ +# coding: utf-8 +from __future__ import unicode_literals + +from django.test import TestCase + + +class URLHandling(TestCase): + """ + Tests for URL handling in views and responses. + """ + urls = 'view_tests.generic_urls' + redirect_target = "/%E4%B8%AD%E6%96%87/target/" + + def test_combining_redirect(self): + """ + Tests that redirecting to an IRI, requiring encoding before we use it + in an HTTP response, is handled correctly. In this case the arg to + HttpRedirect is ASCII but the current request path contains non-ASCII + characters so this test ensures the creation of the full path with a + base non-ASCII part is handled correctly. + """ + response = self.client.get('/中文/') + self.assertRedirects(response, self.redirect_target) + + def test_nonascii_redirect(self): + """ + Tests that a non-ASCII argument to HttpRedirect is handled properly. + """ + response = self.client.get('/nonascii_redirect/') + self.assertRedirects(response, self.redirect_target) + + def test_permanent_nonascii_redirect(self): + """ + Tests that a non-ASCII argument to HttpPermanentRedirect is handled + properly. + """ + response = self.client.get('/permanent_nonascii_redirect/') + self.assertRedirects(response, self.redirect_target, status_code=301) + diff --git a/tests/view_tests/tests/test_static.py b/tests/view_tests/tests/test_static.py new file mode 100644 index 0000000000..bdd9fbfc0b --- /dev/null +++ b/tests/view_tests/tests/test_static.py @@ -0,0 +1,120 @@ +from __future__ import absolute_import + +import mimetypes +from os import path +import unittest + +from django.conf.urls.static import static +from django.http import HttpResponseNotModified +from django.test import TestCase +from django.test.utils import override_settings +from django.utils.http import http_date +from django.views.static import was_modified_since + +from .. import urls +from ..urls import media_dir + + +@override_settings(DEBUG=True) +class StaticTests(TestCase): + """Tests django views in django/views/static.py""" + + prefix = 'site_media' + + def test_serve(self): + "The static view can serve static media" + media_files = ['file.txt', 'file.txt.gz'] + for filename in media_files: + response = self.client.get('/views/%s/%s' % (self.prefix, filename)) + response_content = b''.join(response) + file_path = path.join(media_dir, filename) + with open(file_path, 'rb') as fp: + self.assertEqual(fp.read(), response_content) + self.assertEqual(len(response_content), int(response['Content-Length'])) + self.assertEqual(mimetypes.guess_type(file_path)[1], response.get('Content-Encoding', None)) + + def test_unknown_mime_type(self): + response = self.client.get('/views/%s/file.unknown' % self.prefix) + self.assertEqual('application/octet-stream', response['Content-Type']) + + def test_copes_with_empty_path_component(self): + file_name = 'file.txt' + response = self.client.get('/views/%s//%s' % (self.prefix, file_name)) + response_content = b''.join(response) + with open(path.join(media_dir, file_name), 'rb') as fp: + self.assertEqual(fp.read(), response_content) + + def test_is_modified_since(self): + file_name = 'file.txt' + response = self.client.get('/views/%s/%s' % (self.prefix, file_name), + HTTP_IF_MODIFIED_SINCE='Thu, 1 Jan 1970 00:00:00 GMT') + response_content = b''.join(response) + with open(path.join(media_dir, file_name), 'rb') as fp: + self.assertEqual(fp.read(), response_content) + + def test_not_modified_since(self): + file_name = 'file.txt' + response = self.client.get( + '/views/%s/%s' % (self.prefix, file_name), + HTTP_IF_MODIFIED_SINCE='Mon, 18 Jan 2038 05:14:07 GMT' + # This is 24h before max Unix time. Remember to fix Django and + # update this test well before 2038 :) + ) + self.assertTrue(isinstance(response, HttpResponseNotModified)) + + def test_invalid_if_modified_since(self): + """Handle bogus If-Modified-Since values gracefully + + Assume that a file is modified since an invalid timestamp as per RFC + 2616, section 14.25. + """ + file_name = 'file.txt' + invalid_date = 'Mon, 28 May 999999999999 28:25:26 GMT' + response = self.client.get('/views/%s/%s' % (self.prefix, file_name), + HTTP_IF_MODIFIED_SINCE=invalid_date) + response_content = b''.join(response) + with open(path.join(media_dir, file_name), 'rb') as fp: + self.assertEqual(fp.read(), response_content) + self.assertEqual(len(response_content), + int(response['Content-Length'])) + + def test_invalid_if_modified_since2(self): + """Handle even more bogus If-Modified-Since values gracefully + + Assume that a file is modified since an invalid timestamp as per RFC + 2616, section 14.25. + """ + file_name = 'file.txt' + invalid_date = ': 1291108438, Wed, 20 Oct 2010 14:05:00 GMT' + response = self.client.get('/views/%s/%s' % (self.prefix, file_name), + HTTP_IF_MODIFIED_SINCE=invalid_date) + response_content = b''.join(response) + with open(path.join(media_dir, file_name), 'rb') as fp: + self.assertEqual(fp.read(), response_content) + self.assertEqual(len(response_content), + int(response['Content-Length'])) + + +class StaticHelperTest(StaticTests): + """ + Test case to make sure the static URL pattern helper works as expected + """ + def setUp(self): + super(StaticHelperTest, self).setUp() + self._old_views_urlpatterns = urls.urlpatterns[:] + urls.urlpatterns += static('/media/', document_root=media_dir) + + def tearDown(self): + super(StaticHelperTest, self).tearDown() + urls.urlpatterns = self._old_views_urlpatterns + + +class StaticUtilsTests(unittest.TestCase): + def test_was_modified_since_fp(self): + """ + Test that a floating point mtime does not disturb was_modified_since. + (#18675) + """ + mtime = 1343416141.107817 + header = http_date(mtime) + self.assertFalse(was_modified_since(header, mtime)) -- cgit v1.3