From 4de99074566a233cd765c8e41a0e9f5047296ac9 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sun, 5 Nov 2023 09:25:50 -0800 Subject: [PATCH] Adds migration tests --- .../migrations/0183_auto_20231105_1607.py | 5 +- bookwyrm/models/user.py | 10 +- bookwyrm/tests/migrations/test_0183.py | 126 ++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 bookwyrm/tests/migrations/test_0183.py diff --git a/bookwyrm/migrations/0183_auto_20231105_1607.py b/bookwyrm/migrations/0183_auto_20231105_1607.py index 390b56a9f..2716a0737 100644 --- a/bookwyrm/migrations/0183_auto_20231105_1607.py +++ b/bookwyrm/migrations/0183_auto_20231105_1607.py @@ -8,7 +8,10 @@ def erase_deleted_user_data(apps, schema_editor): """Retroactively clear user data""" for user in User.get_permanently_deleted_users(): user.erase_user_data() - user.save(broadcast=False) + user.save( + broadcast=False, + update_fields=["email", "avatar", "preview_image", "summary", "name"], + ) user.erase_user_statuses(broadcast=False) diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py index 017db31d3..43df39291 100644 --- a/bookwyrm/models/user.py +++ b/bookwyrm/models/user.py @@ -408,10 +408,18 @@ class User(OrderedCollectionPageMixin, AbstractUser): self.erase_user_statuses() # skip the logic in this class's save() - super().save(*args, **kwargs) + super().save( + *args, + update_fields=["email", "avatar", "preview_image", "summary", "name"], + **kwargs, + ) def erase_user_data(self): """Wipe a user's custom data""" + if not self.is_permanently_deleted: + raise IntegrityError( + "Attempted to delete user data for improperly deleted user" + ) # mangle email address self.email = f"{uuid4()}@deleted.user" diff --git a/bookwyrm/tests/migrations/test_0183.py b/bookwyrm/tests/migrations/test_0183.py new file mode 100644 index 000000000..8c8f27d5a --- /dev/null +++ b/bookwyrm/tests/migrations/test_0183.py @@ -0,0 +1,126 @@ +""" testing migrations """ +import json +from unittest.mock import patch + +from django.apps import apps +from django.test import TestCase +from django.db.migrations.executor import MigrationExecutor +from django.db import connection +import responses + +from bookwyrm import models +from bookwyrm.management.commands import initdb +from bookwyrm.settings import USE_HTTPS, DOMAIN + +# pylint: disable=missing-class-docstring +# pylint: disable=missing-function-docstring +class EraseDeletedUserDataMigration(TestCase): + + migrate_from = "0182_auto_20231027_1122" + migrate_to = "0183_auto_20231105_1607" + + # pylint: disable=invalid-name + def setUp(self): + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): + self.active_user = models.User.objects.create_user( + f"activeuser@{DOMAIN}", + "activeuser@activeuser.activeuser", + "activeuserword", + local=True, + localname="active", + name="a name", + ) + self.inactive_user = models.User.objects.create_user( + f"inactiveuser@{DOMAIN}", + "inactiveuser@inactiveuser.inactiveuser", + "inactiveuserword", + local=True, + localname="inactive", + is_active=False, + deactivation_reason="self_deactivation", + name="name name", + ) + self.deleted_user = models.User.objects.create_user( + f"deleteduser@{DOMAIN}", + "deleteduser@deleteduser.deleteduser", + "deleteduserword", + local=True, + localname="deleted", + is_active=False, + deactivation_reason="self_deletion", + name="cool name", + ) + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ), patch("bookwyrm.activitystreams.add_status_task.delay"): + self.active_status = models.Status.objects.create( + user=self.active_user, content="don't delete me" + ) + self.inactive_status = models.Status.objects.create( + user=self.inactive_user, content="also don't delete me" + ) + self.deleted_status = models.Status.objects.create( + user=self.deleted_user, content="yes, delete me" + ) + + initdb.init_groups() + initdb.init_permissions() + + assert ( + self.migrate_from and self.migrate_to + ), "TestCase '{}' must define migrate_from and migrate_to properties".format( + type(self).__name__ + ) + self.migrate_from = [("bookwyrm", self.migrate_from)] + self.migrate_to = [("bookwyrm", self.migrate_to)] + executor = MigrationExecutor(connection) + old_apps = executor.loader.project_state(self.migrate_from).apps + + # Reverse to the original migration + executor.migrate(self.migrate_from) + + self.setUpBeforeMigration(old_apps) + + # Run the migration to test + executor = MigrationExecutor(connection) + executor.loader.build_graph() # reload. + with patch("bookwyrm.activitystreams.remove_status_task.delay"): + executor.migrate(self.migrate_to) + + self.apps = executor.loader.project_state(self.migrate_to).apps + + def setUpBeforeMigration(self, apps): + pass + + def test_user_data_deleted(self): + """Make sure that only the right data was deleted""" + self.active_user.refresh_from_db() + self.inactive_user.refresh_from_db() + self.deleted_user.refresh_from_db() + self.active_status.refresh_from_db() + self.inactive_status.refresh_from_db() + self.deleted_status.refresh_from_db() + + self.assertTrue(self.active_user.is_active) + self.assertEqual(self.active_user.name, "a name") + self.assertNotEqual(self.deleted_user.email, "activeuser@activeuser.activeuser") + self.assertFalse(self.active_status.deleted) + self.assertEqual(self.active_status.content, "don't delete me") + + self.assertFalse(self.inactive_user.is_active) + self.assertEqual(self.inactive_user.name, "name name") + self.assertNotEqual( + self.deleted_user.email, "inactiveuser@inactiveuser.inactiveuser" + ) + self.assertFalse(self.inactive_status.deleted) + self.assertEqual(self.inactive_status.content, "also don't delete me") + + self.assertFalse(self.deleted_user.is_active) + self.assertIsNone(self.deleted_user.name) + self.assertNotEqual( + self.deleted_user.email, "deleteduser@deleteduser.deleteduser" + ) + self.assertTrue(self.deleted_status.deleted) + self.assertIsNone(self.deleted_status.content)