speed up tests by avoiding retrieving contenttypes for all test apps interactive mode the default deletes stale content types and warns of dependent objects a related object is needed to show that a custom collector with canfastdeletefalse is needed interactive mode deletes stale content types even if there aren t any dependent objects noninteractive mode deletes stale content types with capturedstdout as stdout callcommandremovestalecontenttypes interactivefalse verbosity2 self assertindeleting stale content type stdout getvalue self assertequalcontenttype objects count self beforecount def testunavailablecontenttypemodelself speed up tests by avoiding retrieving contenttypes for all test apps interactive mode the default deletes stale content types and warns of dependent objects a related object is needed to show that a custom collector with can_fast_delete false is needed interactive mode deletes stale content types even if there aren t any dependent objects non interactive mode deletes stale content types a contenttype isn t created if the model isn t available
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):
    
    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):
        
        post = Post.objects.create(title="post", content_type=self.content_type)
        
        
        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):
        
        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):
        
        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):
        
        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)