1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
|
from unittest import mock
from django.apps.registry import Apps, apps
from django.contrib.contenttypes import management as contenttypes_management
from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.test import TestCase, modify_settings
from django.test.utils import captured_stdout
from .models import ModelWithNullFKToSite, Post
@modify_settings(INSTALLED_APPS={"append": ["empty_models", "no_models"]})
class RemoveStaleContentTypesTests(TestCase):
# Speed up tests by avoiding retrieving ContentTypes for all test apps.
available_apps = [
"contenttypes_tests",
"empty_models",
"no_models",
"django.contrib.contenttypes",
]
@classmethod
def setUpTestData(cls):
with captured_stdout():
call_command(
"remove_stale_contenttypes",
interactive=False,
include_stale_apps=True,
verbosity=2,
)
cls.before_count = ContentType.objects.count()
cls.content_type = ContentType.objects.create(
app_label="contenttypes_tests", model="Fake"
)
def setUp(self):
self.app_config = apps.get_app_config("contenttypes_tests")
def test_interactive_true_with_dependent_objects(self):
"""
interactive mode (the default) deletes stale content types and warns of
dependent objects.
"""
post = Post.objects.create(title="post", content_type=self.content_type)
# A related object is needed to show that a custom collector with
# can_fast_delete=False is needed.
ModelWithNullFKToSite.objects.create(post=post)
with mock.patch("builtins.input", return_value="yes"):
with captured_stdout() as stdout:
call_command("remove_stale_contenttypes", verbosity=2, stdout=stdout)
self.assertEqual(Post.objects.count(), 0)
output = stdout.getvalue()
self.assertIn("- Content type for contenttypes_tests.Fake", output)
self.assertIn("- 1 contenttypes_tests.Post object(s)", output)
self.assertIn("- 1 contenttypes_tests.ModelWithNullFKToSite", output)
self.assertIn("Deleting stale content type", output)
self.assertEqual(ContentType.objects.count(), self.before_count)
def test_interactive_true_without_dependent_objects(self):
"""
interactive mode deletes stale content types even if there aren't any
dependent objects.
"""
with mock.patch("builtins.input", return_value="yes"):
with captured_stdout() as stdout:
call_command("remove_stale_contenttypes", verbosity=2)
self.assertIn("Deleting stale content type", stdout.getvalue())
self.assertEqual(ContentType.objects.count(), self.before_count)
def test_interactive_false(self):
"""non-interactive mode deletes stale content types."""
with captured_stdout() as stdout:
call_command("remove_stale_contenttypes", interactive=False, verbosity=2)
self.assertIn("Deleting stale content type", stdout.getvalue())
self.assertEqual(ContentType.objects.count(), self.before_count)
def test_unavailable_content_type_model(self):
"""A ContentType isn't created if the model isn't available."""
apps = Apps()
with self.assertNumQueries(0):
contenttypes_management.create_contenttypes(
self.app_config, interactive=False, verbosity=0, apps=apps
)
self.assertEqual(ContentType.objects.count(), self.before_count + 1)
@modify_settings(INSTALLED_APPS={"remove": ["empty_models"]})
def test_contenttypes_removed_in_installed_apps_without_models(self):
ContentType.objects.create(app_label="empty_models", model="Fake 1")
ContentType.objects.create(app_label="no_models", model="Fake 2")
with (
mock.patch("builtins.input", return_value="yes"),
captured_stdout() as stdout,
):
call_command("remove_stale_contenttypes", verbosity=2)
self.assertNotIn(
"Deleting stale content type 'empty_models | Fake 1'",
stdout.getvalue(),
)
self.assertIn(
"Deleting stale content type 'no_models | Fake 2'",
stdout.getvalue(),
)
self.assertEqual(ContentType.objects.count(), self.before_count + 1)
@modify_settings(INSTALLED_APPS={"remove": ["empty_models"]})
def test_contenttypes_removed_for_apps_not_in_installed_apps(self):
ContentType.objects.create(app_label="empty_models", model="Fake 1")
ContentType.objects.create(app_label="no_models", model="Fake 2")
with (
mock.patch("builtins.input", return_value="yes"),
captured_stdout() as stdout,
):
call_command(
"remove_stale_contenttypes", include_stale_apps=True, verbosity=2
)
self.assertIn(
"Deleting stale content type 'empty_models | Fake 1'",
stdout.getvalue(),
)
self.assertIn(
"Deleting stale content type 'no_models | Fake 2'",
stdout.getvalue(),
)
self.assertEqual(ContentType.objects.count(), self.before_count)
|