diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index 63ef641d8..da493b8cf 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -12,11 +12,12 @@ class Command(ScriptTemplate): def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" - filter_condition = { - "verification_type__isnull": True - } + filter_condition = {"verification_type__isnull": True} self.mass_populate_field(User, filter_condition, ["verification_type"]) - + def populate_field(self, field_to_update): """Defines how we update the verification_type field""" field_to_update.set_user_verification_type() + logger.info( + f"{TerminalColors.OKCYAN}Updating {field_to_update} => {field_to_update.verification_type}{TerminalColors.OKCYAN}" + ) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 305be8d8d..1d411e403 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -65,7 +65,7 @@ class ScriptTemplate(BaseCommand): """ def mass_populate_field(self, sender, filter_conditions, fields_to_update): - """Loops through each valid "sender" object - specified by filter_conditions - and + """Loops through each valid "sender" object - specified by filter_conditions - and updates fields defined by fields_to_update using populate_function. You must define populate_field before you can use this function. diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 45532f8ea..eb21971f8 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -158,13 +158,18 @@ class User(AbstractUser): Given pre-existing data from TransitionDomain, VerifiedByStaff, and DomainInvitation, set the verification "type" defined in VerificationTypeChoices. """ + email_or_username = self.email or self.username retrieved = DomainInvitation.DomainInvitationStatus.RETRIEVED - verification_type = self.get_verification_type_from_email(self.email, invitation_status=retrieved) + verification_type = self.get_verification_type_from_email(email_or_username, invitation_status=retrieved) # An existing user may have been invited to a domain after they got verified. # We need to check for this condition. if verification_type == User.VerificationTypeChoices.INVITED: - invitation = DomainInvitation.objects.filter(email=self.email, status=retrieved).order_by("created_at").first() + invitation = ( + DomainInvitation.objects.filter(email=email_or_username, status=retrieved) + .order_by("created_at") + .first() + ) # If you joined BEFORE the oldest invitation was created, then you were verified normally. # (See logic in get_verification_type_from_email) diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index 26161b272..68b7f04c9 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -14,8 +14,9 @@ from registrar.models import ( TransitionDomain, DomainInformation, UserDomainRole, + VerifiedByStaff, + PublicContact, ) -from registrar.models.public_contact import PublicContact from django.core.management import call_command from unittest.mock import patch, call @@ -25,6 +26,94 @@ from .common import MockEppLib, less_console_noise, completed_domain_request from api.tests.common import less_console_noise_decorator +class TestPopulateVerificationType(MockEppLib): + """Tests for the populate_organization_type script""" + + def setUp(self): + """Creates a fake domain object""" + super().setUp() + + # Get the domain requests + self.domain_request_1 = completed_domain_request( + name="lasers.gov", + generic_org_type=DomainRequest.OrganizationChoices.FEDERAL, + is_election_board=True, + status=DomainRequest.DomainRequestStatus.IN_REVIEW, + ) + + # Approve the request + self.domain_request_1.approve() + + # Get the domains + self.domain_1 = Domain.objects.get(name="lasers.gov") + + # Get users + self.regular_user, _ = User.objects.get_or_create(username="testuser@igormail.gov") + + vip, _ = VerifiedByStaff.objects.get_or_create(email="vipuser@igormail.gov") + self.verified_by_staff_user, _ = User.objects.get_or_create(username="vipuser@igormail.gov") + + grandfathered, _ = TransitionDomain.objects.get_or_create( + username="grandpa@igormail.gov", domain_name=self.domain_1.name + ) + self.grandfathered_user, _ = User.objects.get_or_create(username="grandpa@igormail.gov") + + invited, _ = DomainInvitation.objects.get_or_create(email="invited@igormail.gov", domain=self.domain_1) + self.invited_user, _ = User.objects.get_or_create(username="invited@igormail.gov") + + self.untouched_user, _ = User.objects.get_or_create( + username="iaminvincible@igormail.gov", verification_type=User.VerificationTypeChoices.GRANDFATHERED + ) + + def tearDown(self): + """Deletes all DB objects related to migrations""" + super().tearDown() + + # Delete domains and related information + Domain.objects.all().delete() + DomainInformation.objects.all().delete() + DomainRequest.objects.all().delete() + User.objects.all().delete() + Contact.objects.all().delete() + Website.objects.all().delete() + + @less_console_noise_decorator + def run_populate_verification_type(self): + """ + This method executes the populate_organization_type command. + + The 'call_command' function from Django's management framework is then used to + execute the populate_organization_type command with the specified arguments. + """ + with patch( + "registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa + return_value=True, + ): + call_command("populate_verification_type") + + @less_console_noise_decorator + def test_verification_type_script_populates_data(self): + """Ensures that the verification type script actually populates data""" + + # Run the script + self.run_populate_verification_type() + + # Scripts don't work as we'd expect in our test environment, we need to manually + # trigger the refresh event + self.regular_user.refresh_from_db() + self.grandfathered_user.refresh_from_db() + self.invited_user.refresh_from_db() + self.verified_by_staff_user.refresh_from_db() + self.untouched_user.refresh_from_db() + + # Test all users + self.assertEqual(self.regular_user.verification_type, User.VerificationTypeChoices.REGULAR) + self.assertEqual(self.grandfathered_user.verification_type, User.VerificationTypeChoices.GRANDFATHERED) + self.assertEqual(self.invited_user.verification_type, User.VerificationTypeChoices.INVITED) + self.assertEqual(self.verified_by_staff_user.verification_type, User.VerificationTypeChoices.VERIFIED_BY_STAFF) + self.assertEqual(self.untouched_user.verification_type, User.VerificationTypeChoices.GRANDFATHERED) + + class TestPopulateOrganizationType(MockEppLib): """Tests for the populate_organization_type script"""