diff --git a/tcms_tenants/management/commands/remove_stale_tenant_comments.py b/tcms_tenants/management/commands/remove_stale_tenant_comments.py index 239566c..46f440c 100644 --- a/tcms_tenants/management/commands/remove_stale_tenant_comments.py +++ b/tcms_tenants/management/commands/remove_stale_tenant_comments.py @@ -3,28 +3,22 @@ # Licensed under GNU Affero General Public License v3 or later (AGPLv3+) # https://www.gnu.org/licenses/agpl-3.0.html -from django.core.management.base import BaseCommand from django.utils import timezone from django_tenants.utils import get_tenant_model, tenant_context -from django_comments.models import Comment +from django_comments.management.commands.delete_stale_comments import ( + Command as DeleteStaleCommentsCommand, +) -class Command(BaseCommand): + +class Command(DeleteStaleCommentsCommand): help = ( "Remove comments for which the related objects don't exist anymore! " "Works on all tenants!" ) def add_arguments(self, parser): - parser.add_argument( - "-y", - "--yes", - default="x", - action="store_const", - const="y", - dest="answer", - help="Automatically confirm deletion", - ) + super().add_arguments(parser) parser.add_argument( "--dry-run", action="store_true", @@ -33,13 +27,17 @@ def add_arguments(self, parser): ) def handle(self, *args, **kwargs): - answer = kwargs["answer"] dry_run = kwargs["dry_run"] output = None if kwargs["verbosity"]: output = self.stdout + if dry_run: + # the upstream command reports each match before prompting, so + # refusing to delete turns it into a dry run + kwargs["answer"] = "n" + for tenant in get_tenant_model().objects.all(): if output: output.write( @@ -48,30 +46,8 @@ def handle(self, *args, **kwargs): ) with tenant_context(tenant): - for comment in Comment.objects.all(): - if not comment.object_pk or comment.content_object is None: - if output: - output.write( - f"Comment `{comment}' to non-existing " - f"`{comment.content_type.model}' with PK " - f"`{comment.object_pk}'" - ) - - if dry_run: - continue - - while answer not in "yn": - answer = input("Do you wish to delete? [yN] ") - if not answer: - answer = "x" - continue - answer = answer[0].lower() - - if answer == "y": - comment.delete() - - if output: - output.write(f"Deleted comment `{comment}'") + # upstream removes comments whose related object is gone + super().handle(*args, **kwargs) if output: output.write( diff --git a/tcms_tenants/tests/test_remove_stale_tenant_comments.py b/tcms_tenants/tests/test_remove_stale_tenant_comments.py index 9766471..1d9866b 100644 --- a/tcms_tenants/tests/test_remove_stale_tenant_comments.py +++ b/tcms_tenants/tests/test_remove_stale_tenant_comments.py @@ -56,11 +56,38 @@ def add_comments(cls, parent_obj): stale.object_pk = str(int(parent_obj.pk) + cls.pk_offset) stale.save() - stale2 = add_comment( - [parent_obj], "comment with object_pk=''", cls.tenant.owner - )[0] - stale2.object_pk = "" - stale2.save() + def test_dry_run_removes_nothing(self): + tenants = tenants_with_schema() + self.assertGreaterEqual(len(tenants), 1) + + out = StringIO() + call_command( + "remove_stale_tenant_comments", + answer="y", + dry_run=True, + verbosity=1, + stdout=out, + ) + + output = out.getvalue() + + for tenant in tenants: + with tenant_context(tenant): + self.assertEqual( + Comment.objects.count(), self.comments_per_model * 2 * 3 + ) + self.assertTrue( + Comment.objects.annotate( + object_pk_as_int=Cast("object_pk", IntegerField()) + ) + .filter(object_pk_as_int__gt=self.pk_offset) + .exists() + ) + + self.assertIn( + f"for tenant '{tenant.schema_name}' === dry run: True ===", + output, + ) def test_removes_only_comments_which_are_attached_to_missing_objects(self): tenants = tenants_with_schema() @@ -69,13 +96,14 @@ def test_removes_only_comments_which_are_attached_to_missing_objects(self): for tenant in tenants: with tenant_context(tenant): self.assertTrue( - Comment.objects.exclude(object_pk="") - .annotate(object_pk_as_int=Cast("object_pk", IntegerField())) + Comment.objects.annotate( + object_pk_as_int=Cast("object_pk", IntegerField()) + ) .filter(object_pk_as_int__gt=self.pk_offset) .exists() ) self.assertEqual( - Comment.objects.count(), self.comments_per_model * 3 * 3 + Comment.objects.count(), self.comments_per_model * 2 * 3 ) out = StringIO() @@ -92,8 +120,9 @@ def test_removes_only_comments_which_are_attached_to_missing_objects(self): with tenant_context(tenant): self.assertEqual(Comment.objects.count(), self.comments_per_model * 3) self.assertFalse( - Comment.objects.exclude(object_pk="") - .annotate(object_pk_as_int=Cast("object_pk", IntegerField())) + Comment.objects.annotate( + object_pk_as_int=Cast("object_pk", IntegerField()) + ) .filter(object_pk_as_int__gt=self.pk_offset) .exists() )