Skip to content

Commit acbe655

Browse files
committed
[4.2.x] Refs #26688 -- Added tests for log_response() internal helper.
Backport of 8970468 from main.
1 parent dc365ca commit acbe655

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

tests/logging_tests/tests.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
from contextlib import contextmanager
33
from io import StringIO
4+
from unittest import TestCase
45

56
from admin_scripts.tests import AdminScriptTestCase
67

@@ -9,6 +10,7 @@
910
from django.core.exceptions import DisallowedHost, PermissionDenied, SuspiciousOperation
1011
from django.core.files.temp import NamedTemporaryFile
1112
from django.core.management import color
13+
from django.http import HttpResponse
1214
from django.http.multipartparser import MultiPartParserError
1315
from django.test import RequestFactory, SimpleTestCase, override_settings
1416
from django.test.utils import LoggingCaptureMixin
@@ -19,6 +21,7 @@
1921
RequireDebugFalse,
2022
RequireDebugTrue,
2123
ServerFormatter,
24+
log_response,
2225
)
2326
from django.views.debug import ExceptionReporter
2427

@@ -646,3 +649,121 @@ def patch_django_server_logger():
646649
self.assertRegex(
647650
logger_output.getvalue(), r"^\[[/:,\w\s\d]+\] %s\n" % log_msg
648651
)
652+
653+
654+
class LogResponseRealLoggerTests(TestCase):
655+
request = RequestFactory().get("/test-path/")
656+
657+
def assertResponseLogged(self, logger_cm, msg, levelno, status_code, request):
658+
self.assertEqual(
659+
records_len := len(logger_cm.records),
660+
1,
661+
f"Unexpected number of records for {logger_cm=} in {levelno=} (expected 1, "
662+
f"got {records_len}).",
663+
)
664+
record = logger_cm.records[0]
665+
self.assertEqual(record.getMessage(), msg)
666+
self.assertEqual(record.levelno, levelno)
667+
self.assertEqual(record.status_code, status_code)
668+
self.assertEqual(record.request, request)
669+
670+
def test_missing_response_raises_attribute_error(self):
671+
with self.assertRaises(AttributeError):
672+
log_response("No response provided", response=None, request=self.request)
673+
674+
def test_missing_request_logs_with_none(self):
675+
response = HttpResponse(status=403)
676+
with self.assertLogs("django.request", level="INFO") as cm:
677+
log_response(msg := "Missing request", response=response, request=None)
678+
self.assertResponseLogged(cm, msg, logging.WARNING, 403, request=None)
679+
680+
def test_logs_5xx_as_error(self):
681+
response = HttpResponse(status=508)
682+
with self.assertLogs("django.request", level="ERROR") as cm:
683+
log_response(
684+
msg := "Server error occurred", response=response, request=self.request
685+
)
686+
self.assertResponseLogged(cm, msg, logging.ERROR, 508, self.request)
687+
688+
def test_logs_4xx_as_warning(self):
689+
response = HttpResponse(status=418)
690+
with self.assertLogs("django.request", level="WARNING") as cm:
691+
log_response(
692+
msg := "This is a teapot!", response=response, request=self.request
693+
)
694+
self.assertResponseLogged(cm, msg, logging.WARNING, 418, self.request)
695+
696+
def test_logs_2xx_as_info(self):
697+
response = HttpResponse(status=201)
698+
with self.assertLogs("django.request", level="INFO") as cm:
699+
log_response(msg := "OK response", response=response, request=self.request)
700+
self.assertResponseLogged(cm, msg, logging.INFO, 201, self.request)
701+
702+
def test_custom_log_level(self):
703+
response = HttpResponse(status=403)
704+
with self.assertLogs("django.request", level="DEBUG") as cm:
705+
log_response(
706+
msg := "Debug level log",
707+
response=response,
708+
request=self.request,
709+
level="debug",
710+
)
711+
self.assertResponseLogged(cm, msg, logging.DEBUG, 403, self.request)
712+
713+
def test_logs_only_once_per_response(self):
714+
response = HttpResponse(status=500)
715+
with self.assertLogs("django.request", level="ERROR") as cm:
716+
log_response("First log", response=response, request=self.request)
717+
log_response("Second log", response=response, request=self.request)
718+
self.assertResponseLogged(cm, "First log", logging.ERROR, 500, self.request)
719+
720+
def test_exc_info_output(self):
721+
response = HttpResponse(status=500)
722+
try:
723+
raise ValueError("Simulated failure")
724+
except ValueError as exc:
725+
with self.assertLogs("django.request", level="ERROR") as cm:
726+
log_response(
727+
"With exception",
728+
response=response,
729+
request=self.request,
730+
exception=exc,
731+
)
732+
self.assertResponseLogged(
733+
cm, "With exception", logging.ERROR, 500, self.request
734+
)
735+
self.assertIn("ValueError", "\n".join(cm.output)) # Stack trace included
736+
737+
def test_format_args_are_applied(self):
738+
response = HttpResponse(status=500)
739+
with self.assertLogs("django.request", level="ERROR") as cm:
740+
log_response(
741+
"Something went wrong: %s (%d)",
742+
"DB error",
743+
42,
744+
response=response,
745+
request=self.request,
746+
)
747+
msg = "Something went wrong: DB error (42)"
748+
self.assertResponseLogged(cm, msg, logging.ERROR, 500, self.request)
749+
750+
def test_logs_with_custom_logger(self):
751+
handler = logging.StreamHandler(log_stream := StringIO())
752+
handler.setFormatter(logging.Formatter("%(levelname)s:%(name)s:%(message)s"))
753+
754+
custom_logger = logging.getLogger("my.custom.logger")
755+
custom_logger.setLevel(logging.DEBUG)
756+
custom_logger.addHandler(handler)
757+
self.addCleanup(custom_logger.removeHandler, handler)
758+
759+
response = HttpResponse(status=404)
760+
log_response(
761+
msg := "Handled by custom logger",
762+
response=response,
763+
request=self.request,
764+
logger=custom_logger,
765+
)
766+
767+
self.assertEqual(
768+
f"WARNING:my.custom.logger:{msg}", log_stream.getvalue().strip()
769+
)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy