diff --git a/tests/csrf_tests/tests.py b/tests/csrf_tests/tests.py index 956cff11d9..899e0f6ddd 100644 --- a/tests/csrf_tests/tests.py +++ b/tests/csrf_tests/tests.py @@ -1,3 +1,4 @@ +import logging import re from django.conf import settings @@ -55,6 +56,21 @@ class CsrfFunctionTestMixin: actual = _unmask_cipher_token(masked_secret) self.assertEqual(actual, secret) + def assertForbiddenReason( + self, response, logger_cm, reason, levelno=logging.WARNING + ): + self.assertEqual( + records_len := len(logger_cm.records), + 1, + f"Unexpected number of records for {logger_cm=} in {levelno=} (expected 1, " + f"got {records_len}).", + ) + record = logger_cm.records[0] + self.assertEqual(record.getMessage(), "Forbidden (%s): " % reason) + self.assertEqual(record.levelno, levelno) + self.assertEqual(record.status_code, 403) + self.assertEqual(response.status_code, 403) + class CsrfFunctionTests(CsrfFunctionTestMixin, SimpleTestCase): def test_unmask_cipher_token(self): @@ -345,8 +361,7 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): mw.process_request(req) with self.assertLogs("django.security.csrf", "WARNING") as cm: resp = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(403, resp.status_code) - self.assertEqual(cm.records[0].getMessage(), "Forbidden (%s): " % expected) + self.assertForbiddenReason(resp, cm, expected) def test_no_csrf_cookie(self): """ @@ -371,9 +386,8 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): mw.process_request(req) with self.assertLogs("django.security.csrf", "WARNING") as cm: resp = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(403, resp.status_code) self.assertEqual(resp["Content-Type"], "text/html; charset=utf-8") - self.assertEqual(cm.records[0].getMessage(), "Forbidden (%s): " % expected) + self.assertForbiddenReason(resp, cm, expected) def test_csrf_cookie_bad_or_missing_token(self): """ @@ -478,18 +492,12 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): mw = CsrfViewMiddleware(post_form_view) with self.assertLogs("django.security.csrf", "WARNING") as cm: resp = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(403, resp.status_code) - self.assertEqual( - cm.records[0].getMessage(), "Forbidden (%s): " % REASON_NO_CSRF_COOKIE - ) + self.assertForbiddenReason(resp, cm, REASON_NO_CSRF_COOKIE) req = self._get_request(method="DELETE") with self.assertLogs("django.security.csrf", "WARNING") as cm: resp = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(403, resp.status_code) - self.assertEqual( - cm.records[0].getMessage(), "Forbidden (%s): " % REASON_NO_CSRF_COOKIE - ) + self.assertForbiddenReason(resp, cm, REASON_NO_CSRF_COOKIE) def test_put_and_delete_allowed(self): """ @@ -873,11 +881,7 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): mw.process_request(req) with self.assertLogs("django.security.csrf", "WARNING") as cm: resp = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(resp.status_code, 403) - self.assertEqual( - cm.records[0].getMessage(), - "Forbidden (%s): " % REASON_CSRF_TOKEN_MISSING, - ) + self.assertForbiddenReason(resp, cm, REASON_CSRF_TOKEN_MISSING) def test_reading_post_data_raises_os_error(self): """ @@ -902,9 +906,8 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): self.assertIs(mw._origin_verified(req), False) with self.assertLogs("django.security.csrf", "WARNING") as cm: response = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(response.status_code, 403) msg = REASON_BAD_ORIGIN % req.META["HTTP_ORIGIN"] - self.assertEqual(cm.records[0].getMessage(), "Forbidden (%s): " % msg) + self.assertForbiddenReason(response, cm, msg) @override_settings(ALLOWED_HOSTS=["www.example.com"]) def test_bad_origin_null_origin(self): @@ -917,9 +920,8 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): self.assertIs(mw._origin_verified(req), False) with self.assertLogs("django.security.csrf", "WARNING") as cm: response = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(response.status_code, 403) msg = REASON_BAD_ORIGIN % req.META["HTTP_ORIGIN"] - self.assertEqual(cm.records[0].getMessage(), "Forbidden (%s): " % msg) + self.assertForbiddenReason(response, cm, msg) @override_settings(ALLOWED_HOSTS=["www.example.com"]) def test_bad_origin_bad_protocol(self): @@ -933,9 +935,8 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): self.assertIs(mw._origin_verified(req), False) with self.assertLogs("django.security.csrf", "WARNING") as cm: response = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(response.status_code, 403) msg = REASON_BAD_ORIGIN % req.META["HTTP_ORIGIN"] - self.assertEqual(cm.records[0].getMessage(), "Forbidden (%s): " % msg) + self.assertForbiddenReason(response, cm, msg) @override_settings( ALLOWED_HOSTS=["www.example.com"], @@ -960,9 +961,8 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): self.assertIs(mw._origin_verified(req), False) with self.assertLogs("django.security.csrf", "WARNING") as cm: response = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(response.status_code, 403) msg = REASON_BAD_ORIGIN % req.META["HTTP_ORIGIN"] - self.assertEqual(cm.records[0].getMessage(), "Forbidden (%s): " % msg) + self.assertForbiddenReason(response, cm, msg) self.assertEqual(mw.allowed_origins_exact, {"http://no-match.com"}) self.assertEqual( mw.allowed_origin_subdomains, @@ -986,9 +986,8 @@ class CsrfViewMiddlewareTestMixin(CsrfFunctionTestMixin): self.assertIs(mw._origin_verified(req), False) with self.assertLogs("django.security.csrf", "WARNING") as cm: response = mw.process_view(req, post_form_view, (), {}) - self.assertEqual(response.status_code, 403) msg = REASON_BAD_ORIGIN % req.META["HTTP_ORIGIN"] - self.assertEqual(cm.records[0].getMessage(), "Forbidden (%s): " % msg) + self.assertForbiddenReason(response, cm, msg) @override_settings(ALLOWED_HOSTS=["www.example.com"]) def test_good_origin_insecure(self): diff --git a/tests/logging_tests/tests.py b/tests/logging_tests/tests.py index 86c207882f..870a31948c 100644 --- a/tests/logging_tests/tests.py +++ b/tests/logging_tests/tests.py @@ -94,6 +94,28 @@ class DefaultLoggingTests( class LoggingAssertionMixin: + + def assertLogRecord( + self, + logger_cm, + level, + msg, + status_code, + exc_class=None, + ): + self.assertEqual( + records_len := len(logger_cm.records), + 1, + f"Wrong number of calls for {logger_cm=} in {level=} (expected 1, got " + f"{records_len}).", + ) + record = logger_cm.records[0] + self.assertEqual(record.getMessage(), msg) + self.assertEqual(record.status_code, status_code) + if exc_class: + self.assertIsNotNone(record.exc_info) + self.assertEqual(record.exc_info[0], exc_class) + def assertLogsRequest( self, url, level, msg, status_code, logger="django.request", exc_class=None ): @@ -102,17 +124,7 @@ class LoggingAssertionMixin: self.client.get(url) except views.UncaughtException: pass - self.assertEqual( - len(cm.records), - 1, - "Wrong number of calls for logger %r in %r level." % (logger, level), - ) - record = cm.records[0] - self.assertEqual(record.getMessage(), msg) - self.assertEqual(record.status_code, status_code) - if exc_class: - self.assertIsNotNone(record.exc_info) - self.assertEqual(record.exc_info[0], exc_class) + self.assertLogRecord(cm, level, msg, status_code, exc_class) @override_settings(DEBUG=True, ROOT_URLCONF="logging_tests.urls") @@ -135,6 +147,14 @@ class HandlerLoggingTests( msg="Not Found: /does_not_exist/", ) + async def test_async_page_not_found_warning(self): + logger = "django.request" + level = "WARNING" + with self.assertLogs(logger, level) as cm: + await self.async_client.get("/does_not_exist/") + + self.assertLogRecord(cm, level, "Not Found: /does_not_exist/", 404) + def test_page_not_found_raised(self): self.assertLogsRequest( url="/does_not_exist_raised/",