@@ -94,7 +94,6 @@ def test_django_logger_debug(self):
94
94
95
95
96
96
class LoggingAssertionMixin:
97
-
98
97
def assertLogRecord(
99
98
self,
100
99
logger_cm,
@@ -147,6 +146,14 @@ def test_page_not_found_warning(self):
147
146
msg="Not Found: /does_not_exist/",
148
147
)
149
148
149
+
def test_control_chars_escaped(self):
150
+
self.assertLogsRequest(
151
+
url="/%1B[1;31mNOW IN RED!!!1B[0m/",
152
+
level="WARNING",
153
+
status_code=404,
154
+
msg=r"Not Found: /\x1b[1;31mNOW IN RED!!!1B[0m/",
155
+
)
156
+
150
157
async def test_async_page_not_found_warning(self):
151
158
logger = "django.request"
152
159
level = "WARNING"
@@ -155,6 +162,16 @@ async def test_async_page_not_found_warning(self):
155
162
156
163
self.assertLogRecord(cm, level, "Not Found: /does_not_exist/", 404)
157
164
165
+
async def test_async_control_chars_escaped(self):
166
+
logger = "django.request"
167
+
level = "WARNING"
168
+
with self.assertLogs(logger, level) as cm:
169
+
await self.async_client.get(r"/%1B[1;31mNOW IN RED!!!1B[0m/")
170
+
171
+
self.assertLogRecord(
172
+
cm, level, r"Not Found: /\x1b[1;31mNOW IN RED!!!1B[0m/", 404
173
+
)
174
+
158
175
def test_page_not_found_raised(self):
159
176
self.assertLogsRequest(
160
177
url="/does_not_exist_raised/",
@@ -686,6 +703,7 @@ def assertResponseLogged(self, logger_cm, msg, levelno, status_code, request):
686
703
self.assertEqual(record.levelno, levelno)
687
704
self.assertEqual(record.status_code, status_code)
688
705
self.assertEqual(record.request, request)
706
+
return record
689
707
690
708
def test_missing_response_raises_attribute_error(self):
691
709
with self.assertRaises(AttributeError):
@@ -787,3 +805,62 @@ def test_logs_with_custom_logger(self):
787
805
self.assertEqual(
788
806
f"WARNING:my.custom.logger:{msg}", log_stream.getvalue().strip()
789
807
)
808
+
809
+
def test_unicode_escape_escaping(self):
810
+
test_cases = [
811
+
# Control characters.
812
+
("line\nbreak", "line\\nbreak"),
813
+
("carriage\rreturn", "carriage\\rreturn"),
814
+
("tab\tseparated", "tab\\tseparated"),
815
+
("formfeed\f", "formfeed\\x0c"),
816
+
("bell\a", "bell\\x07"),
817
+
("multi\nline\ntext", "multi\\nline\\ntext"),
818
+
# Slashes.
819
+
("slash\\test", "slash\\\\test"),
820
+
("back\\slash", "back\\\\slash"),
821
+
# Quotes.
822
+
('quote"test"', 'quote"test"'),
823
+
("quote'test'", "quote'test'"),
824
+
# Accented, composed characters, emojis and symbols.
825
+
("café", "caf\\xe9"),
826
+
("e\u0301", "e\\u0301"), # e + combining acute
827
+
("smile🙂", "smile\\U0001f642"),
828
+
("weird ☃️", "weird \\u2603\\ufe0f"),
829
+
# Non-Latin alphabets.
830
+
("Привет", "\\u041f\\u0440\\u0438\\u0432\\u0435\\u0442"),
831
+
("你好", "\\u4f60\\u597d"),
832
+
# ANSI escape sequences.
833
+
("escape\x1b[31mred\x1b[0m", "escape\\x1b[31mred\\x1b[0m"),
834
+
(
835
+
"/\x1b[1;31mCAUTION!!YOU ARE PWNED\x1b[0m/",
836
+
"/\\x1b[1;31mCAUTION!!YOU ARE PWNED\\x1b[0m/",
837
+
),
838
+
(
839
+
"/\r\n\r\n1984-04-22 INFO Listening on 0.0.0.0:8080\r\n\r\n",
840
+
"/\\r\\n\\r\\n1984-04-22 INFO Listening on 0.0.0.0:8080\\r\\n\\r\\n",
841
+
),
842
+
# Plain safe input.
843
+
("normal-path", "normal-path"),
844
+
("slash/colon:", "slash/colon:"),
845
+
# Non strings.
846
+
(0, "0"),
847
+
([1, 2, 3], "[1, 2, 3]"),
848
+
({"test": "🙂"}, "{'test': '🙂'}"),
849
+
]
850
+
851
+
msg = "Test message: %s"
852
+
for case, expected in test_cases:
853
+
with self.assertLogs("django.request", level="ERROR") as cm:
854
+
with self.subTest(case=case):
855
+
response = HttpResponse(status=318)
856
+
log_response(msg, case, response=response, level="error")
857
+
858
+
record = self.assertResponseLogged(
859
+
cm,
860
+
msg % expected,
861
+
levelno=logging.ERROR,
862
+
status_code=318,
863
+
request=None,
864
+
)
865
+
# Log record is always a single line.
866
+
self.assertEqual(len(record.getMessage().splitlines()), 1)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4