1
0
mirror of https://github.com/django/django.git synced 2025-06-08 13:09:13 +00:00
django/tests/tasks/test_utils.py
2024-09-27 11:27:46 +01:00

179 lines
6.6 KiB
Python

import datetime
import hashlib
import optparse
import subprocess
from typing import List
from unittest.mock import Mock
from django.core.exceptions import ImproperlyConfigured
from django.tasks import utils
from django.tasks.exceptions import InvalidTaskError
from django.test import SimpleTestCase
from . import tasks as test_tasks
class IsGlobalFunctionTestCase(SimpleTestCase):
def test_builtin(self):
self.assertFalse(utils.is_global_function(any))
self.assertFalse(utils.is_global_function(isinstance))
def test_from_module(self):
self.assertTrue(utils.is_global_function(subprocess.run))
self.assertTrue(utils.is_global_function(subprocess.check_output))
self.assertTrue(utils.is_global_function(test_tasks.noop_task.func))
def test_private_function(self):
def private_function():
pass
self.assertFalse(utils.is_global_function(private_function))
def test_coroutine(self):
self.assertTrue(utils.is_global_function(test_tasks.noop_task_async.func))
def test_method(self):
self.assertFalse(utils.is_global_function(self.test_method))
self.assertFalse(utils.is_global_function(self.setUp))
def test_lambda(self):
self.assertFalse(utils.is_global_function(lambda: True))
def test_uninitialised_method(self):
# This import has to be here, so the module is loaded during the test
from . import is_global_function_fixture
self.assertTrue(is_global_function_fixture.really_global_function)
self.assertIsNotNone(is_global_function_fixture.inner_func_is_global_function)
self.assertFalse(is_global_function_fixture.inner_func_is_global_function)
class IsJSONSerializableTestCase(SimpleTestCase):
def test_serializable(self):
for example in [123, 12.3, "123", {"123": 456}, [], None]:
with self.subTest(example):
self.assertTrue(utils.is_json_serializable(example))
def test_not_serializable(self):
for example in [
self,
any,
datetime.datetime.now(),
]:
with self.subTest(example):
self.assertFalse(utils.is_json_serializable(example))
class JSONNormalizeTestCase(SimpleTestCase):
def test_round_trip(self):
self.assertEqual(utils.json_normalize({}), {})
self.assertEqual(utils.json_normalize([]), [])
self.assertEqual(utils.json_normalize(()), [])
self.assertEqual(utils.json_normalize({"foo": ()}), {"foo": []})
def test_encode_error(self):
for example in [self, any, datetime.datetime.now()]:
with self.subTest(example):
self.assertFalse(utils.is_json_serializable(example))
self.assertRaises(TypeError, utils.json_normalize, example)
class RetryTestCase(SimpleTestCase):
def test_retry(self):
sentinel = Mock(side_effect=ValueError(""))
with self.assertRaises(ValueError):
utils.retry()(sentinel)()
self.assertEqual(sentinel.call_count, 3)
def test_keeps_return_value(self):
self.assertTrue(utils.retry()(lambda: True)())
self.assertFalse(utils.retry()(lambda: False)())
class ExceptionSerializationTestCase(SimpleTestCase):
def test_serialize_exceptions(self):
for exc in [
ValueError(10),
SyntaxError("Wrong"),
ImproperlyConfigured("It's wrong"),
InvalidTaskError(""),
SystemExit(),
]:
with self.subTest(exc):
data = utils.exception_to_dict(exc)
self.assertEqual(utils.json_normalize(data), data)
self.assertEqual(
set(data.keys()), {"exc_type", "exc_args", "exc_traceback"}
)
exception = utils.exception_from_dict(data)
self.assertIsInstance(exception, type(exc))
self.assertEqual(exception.args, exc.args)
# Check that the exception traceback contains a minimal traceback
msg = str(exc.args[0]) if exc.args else ""
traceback = data["exc_traceback"]
self.assertIn(exc.__class__.__name__, traceback)
self.assertIn(msg, traceback)
def test_serialize_full_traceback(self):
try:
# Using optparse to generate an error because:
# - it's pure python
# - it's easy to trip down
# - it's unlikely to change ever
optparse.OptionParser(option_list=[1]) # type: ignore
except Exception as e:
traceback = utils.exception_to_dict(e)["exc_traceback"]
# The test is willingly fuzzy to ward against changes in the
# traceback formatting
self.assertIn("traceback", traceback.lower())
self.assertIn("line", traceback.lower())
self.assertIn(optparse.__file__, traceback)
self.assertTrue(
traceback.endswith("TypeError: not an Option instance: 1\n")
)
def test_serialize_traceback_from_c_module(self):
try:
# Same as test_serialize_full_traceback, but uses hashlib
# because it's in C, not in Python
hashlib.md5(1) # type: ignore
except Exception as e:
traceback = utils.exception_to_dict(e)["exc_traceback"]
self.assertIn("traceback", traceback.lower())
self.assertTrue(
traceback.endswith(
"TypeError: object supporting the buffer API required\n"
)
)
self.assertIn("hashlib.md5(1)", traceback)
def test_cannot_deserialize_non_exception(self):
serialized_exceptions: List[utils.SerializedExceptionDict] = [
{
"exc_type": "subprocess.check_output",
"exc_args": ["exit", "1"],
"exc_traceback": "",
},
{"exc_type": "True", "exc_args": [], "exc_traceback": ""},
{"exc_type": "math.pi", "exc_args": [], "exc_traceback": ""},
{"exc_type": __name__, "exc_args": [], "exc_traceback": ""},
{
"exc_type": utils.get_module_path(type(self)),
"exc_args": [],
"exc_traceback": "",
},
{
"exc_type": utils.get_module_path(Mock),
"exc_args": [],
"exc_traceback": "",
},
]
for data in serialized_exceptions:
with self.subTest(data):
with self.assertRaises((TypeError, ImportError)):
utils.exception_from_dict(data)