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