From c98a96e990ef4bc22f979c1681a878d78c5eab98 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:54:52 -0700 Subject: [PATCH] Test skeleton Not done, still needs to be fixed up. Getting some initial data in place --- src/registrar/tests/common.py | 126 ++++++++++++++++----- src/registrar/tests/test_reports.py | 50 ++++++++ src/registrar/utility/model_annotations.py | 2 +- 3 files changed, 148 insertions(+), 30 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 4edfbe680..2528df2c9 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -3,6 +3,7 @@ import logging from contextlib import contextmanager import random +import boto3_mocking # type: ignore from string import ascii_uppercase import uuid from django.test import TestCase @@ -29,6 +30,7 @@ from registrar.models import ( FederalAgency, UserPortfolioPermission, Portfolio, + PortfolioInvitation, ) from epplibwrapper import ( commands, @@ -39,6 +41,7 @@ from epplibwrapper import ( ErrorCode, responses, ) +from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices from registrar.models.user_domain_role import UserDomainRole from registrar.models.utility.contact_error import ContactError, ContactErrorCodes @@ -196,6 +199,7 @@ class GenericTestHelper(TestCase): self.assertEqual(expected_sort_order, returned_sort_order) + @classmethod def _mock_user_request_for_factory(self, request): """Adds sessionmiddleware when using factory to associate session information""" middleware = SessionMiddleware(lambda req: req) @@ -529,8 +533,11 @@ class AuditedAdminMockData: class MockDb(TestCase): @classmethod + @boto3_mocking.patching @less_console_noise_decorator def sharedSetUp(cls): + cls.mock_client_class = MagicMock() + cls.mock_client = cls.mock_client_class.return_value username = "test_user" first_name = "First" last_name = "Last" @@ -540,6 +547,27 @@ class MockDb(TestCase): cls.user = get_user_model().objects.create( username=username, first_name=first_name, last_name=last_name, email=email, title=title, phone=phone ) + cls.meoward_user = get_user_model().objects.create( + username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com" + ) + cls.lebowski_user = get_user_model().objects.create( + username="big_lebowski", first_name="big", last_name="lebowski", email="big_lebowski@dude.co" + ) + cls.tired_user = get_user_model().objects.create( + username="ministry_of_bedtime", first_name="tired", last_name="sleepy", email="tired_sleepy@igorville.gov" + ) + # Custom superuser and staff so that these do not conflict with what may be defined on what implements this. + cls.custom_superuser = create_superuser( + username="cold_superuser", first_name="cold", last_name="icy", email="icy_superuser@igorville.gov" + ) + cls.custom_staffuser = create_user( + username="warm_staff", first_name="warm", last_name="cozy", email="cozy_staffuser@igorville.gov" + ) + + cls.federal_agency_1, _ = FederalAgency.objects.get_or_create(agency="World War I Centennial Commission") + cls.federal_agency_2, _ = FederalAgency.objects.get_or_create(agency="Armed Forces Retirement Home") + + cls.portfolio_1, _ = Portfolio.objects.get_or_create(creator=cls.custom_superuser, federal_agency=cls.federal_agency_1) current_date = get_time_aware_date(datetime(2024, 4, 2)) # Create start and end dates using timedelta @@ -547,9 +575,6 @@ class MockDb(TestCase): cls.end_date = current_date + timedelta(days=2) cls.start_date = current_date - timedelta(days=2) - cls.federal_agency_1, _ = FederalAgency.objects.get_or_create(agency="World War I Centennial Commission") - cls.federal_agency_2, _ = FederalAgency.objects.get_or_create(agency="Armed Forces Retirement Home") - cls.domain_1, _ = Domain.objects.get_or_create( name="cdomain1.gov", state=Domain.State.READY, first_ready=get_time_aware_date(datetime(2024, 4, 2)) ) @@ -596,9 +621,11 @@ class MockDb(TestCase): federal_agency=cls.federal_agency_1, federal_type="executive", is_election_board=False, + portfolio=cls.portfolio_1, ) cls.domain_information_2, _ = DomainInformation.objects.get_or_create( - creator=cls.user, domain=cls.domain_2, generic_org_type="interstate", is_election_board=True + creator=cls.user, domain=cls.domain_2, generic_org_type="interstate", is_election_board=True, + portfolio=cls.portfolio_1, ) cls.domain_information_3, _ = DomainInformation.objects.get_or_create( creator=cls.user, @@ -671,14 +698,6 @@ class MockDb(TestCase): is_election_board=False, ) - cls.meoward_user = get_user_model().objects.create( - username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com" - ) - - cls.lebowski_user = get_user_model().objects.create( - username="big_lebowski", first_name="big", last_name="lebowski", email="big_lebowski@dude.co" - ) - _, created = UserDomainRole.objects.get_or_create( user=cls.meoward_user, domain=cls.domain_1, role=UserDomainRole.Roles.MANAGER ) @@ -723,6 +742,49 @@ class MockDb(TestCase): email="squeaker@rocks.com", domain=cls.domain_10, status=DomainInvitation.DomainInvitationStatus.INVITED ) + # Add portfolio invitations + retrieve + with boto3_mocking.clients.handler_for("sesv2", cls.mock_client_class): + portfolio_invitation_1, _ = PortfolioInvitation.objects.get_or_create( + email=cls.meoward_user.email, + portfolio=cls.portfolio_1, + roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER], + additional_permissions=[UserPortfolioPermissionChoices.EDIT_MEMBERS] + ) + + portfolio_invitation_2, _ = PortfolioInvitation.objects.get_or_create( + email=cls.lebowski_user.email, portfolio=cls.portfolio_1, + roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER], + additional_permissions=[UserPortfolioPermissionChoices.VIEW_MEMBERS] + ) + + portfolio_invitation_3, _ = PortfolioInvitation.objects.get_or_create( + email=cls.tired_user.email, portfolio=cls.portfolio_1, + roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER], + additional_permissions=[UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS] + ) + + portfolio_invitation_4, _ = PortfolioInvitation.objects.get_or_create( + email=cls.custom_superuser.email, portfolio=cls.portfolio_1, + roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN], + additional_permissions=[ + UserPortfolioPermissionChoices.VIEW_MEMBERS, + UserPortfolioPermissionChoices.EDIT_MEMBERS, + UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS, + UserPortfolioPermissionChoices.EDIT_REQUESTS + ] + ) + + portfolio_invitation_5, _ = PortfolioInvitation.objects.get_or_create( + email=cls.custom_staffuser.email, portfolio=cls.portfolio_1, + roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN] + ) + + portfolio_invitation_1.retrieve() + portfolio_invitation_2.retrieve() + portfolio_invitation_3.retrieve() + portfolio_invitation_4.retrieve() + portfolio_invitation_5.retrieve() + with less_console_noise(): cls.domain_request_1 = completed_domain_request( status=DomainRequest.DomainRequestStatus.STARTED, @@ -731,10 +793,12 @@ class MockDb(TestCase): cls.domain_request_2 = completed_domain_request( status=DomainRequest.DomainRequestStatus.IN_REVIEW, name="city2.gov", + portfolio=cls.portfolio_1, ) cls.domain_request_3 = completed_domain_request( status=DomainRequest.DomainRequestStatus.STARTED, name="city3.gov", + portfolio=cls.portfolio_1, ) cls.domain_request_4 = completed_domain_request( status=DomainRequest.DomainRequestStatus.STARTED, @@ -749,6 +813,7 @@ class MockDb(TestCase): cls.domain_request_6 = completed_domain_request( status=DomainRequest.DomainRequestStatus.STARTED, name="city6.gov", + portfolio=cls.portfolio_1, ) cls.domain_request_3.submit() cls.domain_request_4.submit() @@ -797,6 +862,7 @@ class MockDb(TestCase): UserPortfolioPermission.objects.all().delete() User.objects.all().delete() DomainInvitation.objects.all().delete() + PortfolioInvitation.objects.all().delete() cls.federal_agency_1.delete() cls.federal_agency_2.delete() @@ -837,17 +903,18 @@ def mock_user(): return mock_user -def create_superuser(): +def create_superuser(**kwargs): + """Creates a analyst user with is_staff=True and the group full_access_group""" User = get_user_model() p = "adminpass" user = User.objects.create_user( - username="superuser", - email="admin@example.com", - first_name="first", - last_name="last", - is_staff=True, - password=p, - phone="8003111234", + username=kwargs.get("username", "superuser"), + email=kwargs.get("email", "admin@example.com"), + first_name=kwargs.get("first_name", "first"), + last_name=kwargs.get("last_name", "last"), + is_staff=kwargs.get("is_staff", True), + password=kwargs.get("password", p), + phone=kwargs.get("phone", "8003111234"), ) # Retrieve the group or create it if it doesn't exist group, _ = UserGroup.objects.get_or_create(name="full_access_group") @@ -856,18 +923,19 @@ def create_superuser(): return user -def create_user(): +def create_user(**kwargs): + """Creates a analyst user with is_staff=True and the group cisa_analysts_group""" User = get_user_model() p = "userpass" user = User.objects.create_user( - username="staffuser", - email="staff@example.com", - first_name="first", - last_name="last", - is_staff=True, - title="title", - password=p, - phone="8003111234", + username=kwargs.get("username", "staffuser"), + email=kwargs.get("email", "staff@example.com"), + first_name=kwargs.get("first_name", "first"), + last_name=kwargs.get("last_name", "last"), + is_staff=kwargs.get("is_staff", True), + title=kwargs.get("title", "title"), + password=kwargs.get("password", p), + phone=kwargs.get("phone", "8003111234"), ) # Retrieve the group or create it if it doesn't exist group, _ = UserGroup.objects.get_or_create(name="cisa_analysts_group") diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index ae1b3b1c1..d16937985 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -22,6 +22,7 @@ from registrar.utility.csv_export import ( DomainRequestExport, DomainRequestGrowth, DomainRequestDataFull, + MemberExport, get_default_start_date, get_default_end_date, ) @@ -42,7 +43,9 @@ from .common import ( get_wsgi_request_object, less_console_noise, get_time_aware_date, + GenericTestHelper, ) +from .common import GenericTestHelper from waffle.testutils import override_flag @@ -794,6 +797,53 @@ class ExportDataTest(MockDbForIndividualTests, MockEppLib): self.assertEqual(csv_content, expected_content) +class MemberExportTest(MockDbForIndividualTests, MockEppLib): + + def setUp(self): + super().setUp() + self.factory = RequestFactory() + + @override_flag("organization_feature", active=True) + @override_flag("organization_members", active=True) + @less_console_noise_decorator + def test_member_export(self): + """Tests the member export report""" + # Create a request and add the user to the request + request = self.factory.get("/") + request.user = self.user + + # Add portfolio to session + request = GenericTestHelper._mock_user_request_for_factory(request) + request.session["portfolio"] = self.portfolio_1 + + # Create a CSV file in memory + csv_file = StringIO() + # Call the export function + MemberExport.export_data_to_csv(csv_file, request=request) + # Reset the CSV file's position to the beginning + csv_file.seek(0) + # Read the content into a variable + csv_content = csv_file.read() + print("what is the csv content?") + print(csv_content) + expected_content = ( + # Header + "Email,Organization admin,Invited by,Invitation date,Last active,Domain requests," + "Member management,Domain management,Number of domains,Domains\n" + # Content + "meoward@rocks.com,False,Unknown,Unknown,Invited,None,Manager,False,0,\n" + "big_lebowski@dude.co,False,Unknown,Unknown,Invited,None,Viewer,False,0,\n" + "tired_sleepy@igorville.gov,False,Unknown,Unknown,Invited,Viewer,None,False,0,\n" + "icy_superuser@igorville.gov,True,Unknown,Unknown,Invited,Viewer Requester,Manager,False,0,\n" + "cozy_staffuser@igorville.gov,True,Unknown,Unknown,Invited,Viewer Requester,None,False,0,\n" + ) + # Normalize line endings and remove commas, + # spaces and leading/trailing whitespace + csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip() + expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip() + self.assertEqual(csv_content, expected_content) + + class HelperFunctions(MockDbForSharedTests): """This asserts that 1=1. Its limited usefulness lies in making sure the helper methods stay healthy.""" diff --git a/src/registrar/utility/model_annotations.py b/src/registrar/utility/model_annotations.py index 077da9553..1fda52b25 100644 --- a/src/registrar/utility/model_annotations.py +++ b/src/registrar/utility/model_annotations.py @@ -386,7 +386,7 @@ class PortfolioInvitationModelAnnotation(BaseModelAnnotation): .order_by("action_time") .values("display_date")[:1] ), - Value("Invalid date"), + Value("Unknown"), output_field=TextField(), )