diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 3d9a147a2..be7065403 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -97,6 +97,11 @@ def less_console_noise(output_stream=None): output_stream.close() +def get_time_aware_date(date=datetime(2023, 11, 1)): + """Returns a time aware date""" + return timezone.make_aware(date) + + class GenericTestHelper(TestCase): """A helper class that contains various helper functions for TestCases""" @@ -532,11 +537,9 @@ class MockDb(TestCase): username=username, first_name=first_name, last_name=last_name, email=email ) - # Create a time-aware current date - current_datetime = timezone.now() - # Extract the date part - current_date = current_datetime.date() + current_date = get_time_aware_date(datetime(2024, 4, 2)) # Create start and end dates using timedelta + self.end_date = current_date + timedelta(days=2) self.start_date = current_date - timedelta(days=2) @@ -544,22 +547,22 @@ class MockDb(TestCase): self.federal_agency_2, _ = FederalAgency.objects.get_or_create(agency="Armed Forces Retirement Home") self.domain_1, _ = Domain.objects.get_or_create( - name="cdomain1.gov", state=Domain.State.READY, first_ready=timezone.now() + name="cdomain1.gov", state=Domain.State.READY, first_ready=get_time_aware_date(datetime(2024, 4, 2)) ) self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED) self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD) self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN) self.domain_5, _ = Domain.objects.get_or_create( - name="bdomain5.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2023, 11, 1)) + name="bdomain5.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(2023, 11, 1)) ) self.domain_6, _ = Domain.objects.get_or_create( - name="bdomain6.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(1980, 10, 16)) + name="bdomain6.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(1980, 10, 16)) ) self.domain_7, _ = Domain.objects.get_or_create( - name="xdomain7.gov", state=Domain.State.DELETED, deleted=timezone.now() + name="xdomain7.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(2024, 4, 2)) ) self.domain_8, _ = Domain.objects.get_or_create( - name="sdomain8.gov", state=Domain.State.DELETED, deleted=timezone.now() + name="sdomain8.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(2024, 4, 2)) ) # We use timezone.make_aware to sync to server time a datetime object with the current date (using date.today()) # and a specific time (using datetime.min.time()). @@ -567,19 +570,19 @@ class MockDb(TestCase): self.domain_9, _ = Domain.objects.get_or_create( name="zdomain9.gov", state=Domain.State.DELETED, - deleted=timezone.make_aware(datetime.combine(date.today() - timedelta(days=1), datetime.min.time())), + deleted=get_time_aware_date(datetime(2024, 4, 1)), ) # ready tomorrow self.domain_10, _ = Domain.objects.get_or_create( name="adomain10.gov", state=Domain.State.READY, - first_ready=timezone.make_aware(datetime.combine(date.today() + timedelta(days=1), datetime.min.time())), + first_ready=get_time_aware_date(datetime(2024, 4, 3)), ) self.domain_11, _ = Domain.objects.get_or_create( - name="cdomain11.gov", state=Domain.State.READY, first_ready=timezone.now() + name="cdomain11.gov", state=Domain.State.READY, first_ready=get_time_aware_date(datetime(2024, 4, 2)) ) self.domain_12, _ = Domain.objects.get_or_create( - name="zdomain12.gov", state=Domain.State.READY, first_ready=timezone.now() + name="zdomain12.gov", state=Domain.State.READY, first_ready=get_time_aware_date(datetime(2024, 4, 2)) ) self.domain_information_1, _ = DomainInformation.objects.get_or_create( @@ -716,23 +719,31 @@ class MockDb(TestCase): with less_console_noise(): self.domain_request_1 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.STARTED, name="city1.gov" + status=DomainRequest.DomainRequestStatus.STARTED, + name="city1.gov", ) self.domain_request_2 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.IN_REVIEW, name="city2.gov" + status=DomainRequest.DomainRequestStatus.IN_REVIEW, + name="city2.gov", ) self.domain_request_3 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.STARTED, name="city3.gov" + status=DomainRequest.DomainRequestStatus.STARTED, + name="city3.gov", ) self.domain_request_4 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.STARTED, name="city4.gov" + status=DomainRequest.DomainRequestStatus.STARTED, + name="city4.gov", ) self.domain_request_5 = completed_domain_request( - status=DomainRequest.DomainRequestStatus.APPROVED, name="city5.gov" + status=DomainRequest.DomainRequestStatus.APPROVED, + name="city5.gov", ) self.domain_request_3.submit() - self.domain_request_3.save() self.domain_request_4.submit() + + self.domain_request_3.submission_date = get_time_aware_date(datetime(2024, 4, 2)) + self.domain_request_4.submission_date = get_time_aware_date(datetime(2024, 4, 2)) + self.domain_request_3.save() self.domain_request_4.save() def tearDown(self): @@ -873,6 +884,7 @@ def completed_domain_request( if organization_type: domain_request_kwargs["organization_type"] = organization_type + domain_request, _ = DomainRequest.objects.get_or_create(**domain_request_kwargs) if has_other_contacts: diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 98c5df0ac..e85c2fc5e 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -2164,7 +2164,6 @@ class TestDomainRequestAdmin(MockEppLib): self.assertContains(response, "Yes, select ineligible status") def test_readonly_when_restricted_creator(self): - self.maxDiff = None with less_console_noise(): domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) with boto3_mocking.clients.handler_for("sesv2", self.mock_client): diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index d918dda92..4f308b2b6 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -22,9 +22,8 @@ from django.conf import settings from botocore.exceptions import ClientError import boto3_mocking from registrar.utility.s3_bucket import S3ClientError, S3ClientErrorCodes # type: ignore -from datetime import datetime from django.utils import timezone -from .common import MockDb, MockEppLib, less_console_noise +from .common import MockDb, MockEppLib, less_console_noise, get_time_aware_date class CsvReportsTest(MockDb): @@ -201,9 +200,9 @@ class ExportDataTest(MockDb, MockEppLib): def tearDown(self): super().tearDown() - def test_export_domains_to_writer_security_emails(self): + def test_export_domains_to_writer_security_emails_and_first_ready(self): """Test that export_domains_to_writer returns the - expected security email""" + expected security email and first_ready value""" with less_console_noise(): # Add security email information @@ -215,6 +214,11 @@ class ExportDataTest(MockDb, MockEppLib): self.domain_2.security_contact # Invoke setter self.domain_3.security_contact + + # Add a first ready date on the first domain. Leaving the others blank. + self.domain_1.first_ready = get_default_start_date() + self.domain_1.save() + # Create a CSV file in memory csv_file = StringIO() writer = csv.writer(csv_file) @@ -231,6 +235,7 @@ class ExportDataTest(MockDb, MockEppLib): "Security contact email", "Status", "Expiration date", + "First ready on", ] sort_fields = ["domain__name"] filter_condition = { @@ -240,7 +245,7 @@ class ExportDataTest(MockDb, MockEppLib): Domain.State.ON_HOLD, ], } - self.maxDiff = None + # Call the export functions write_csv_for_domains( writer, @@ -259,13 +264,14 @@ class ExportDataTest(MockDb, MockEppLib): # sorted alphabetially by domain name expected_content = ( "Domain name,Domain type,Agency,Organization name,City,State,AO," - "AO email,Security contact email,Status,Expiration date\n" - "adomain10.gov,Federal,Armed Forces Retirement Home,Ready\n" - "adomain2.gov,Interstate,(blank),Dns needed\n" - "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" - "ddomain3.gov,Federal,Armed Forces Retirement Home,security@mail.gov,On hold,2023-11-15\n" - "defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,(blank),Ready\n" - "zdomain12.govInterstateReady\n" + "AO email,Security contact email,Status,Expiration date, First ready on\n" + "adomain10.gov,Federal,Armed Forces Retirement Home,Ready,(blank),2024-04-03\n" + "adomain2.gov,Interstate,(blank),Dns needed,(blank),(blank)\n" + "cdomain11.gov,Federal-Executive,WorldWarICentennialCommission,Ready,(blank),2024-04-02\n" + "ddomain3.gov,Federal,Armed Forces Retirement Home,security@mail.gov,On hold,2023-11-15,(blank)\n" + "defaultsecurity.gov,Federal - Executive,World War I Centennial Commission," + "(blank),Ready,(blank),2023-11-01\n" + "zdomain12.govInterstateReady,(blank),2024-04-02\n" ) # Normalize line endings and remove commas, # spaces and leading/trailing whitespace @@ -470,18 +476,18 @@ class ExportDataTest(MockDb, MockEppLib): # Read the content into a variable csv_content = csv_file.read() - # We expect READY domains first, created between today-2 and today+2, sorted by created_at then name - # and DELETED domains deleted between today-2 and today+2, sorted by deleted then name + # We expect READY domains first, created between day-2 and day+2, sorted by created_at then name + # and DELETED domains deleted between day-2 and day+2, sorted by deleted then name expected_content = ( "Domain name,Domain type,Agency,Organization name,City," "State,Status,Expiration date\n" - "cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,\n" - "adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,\n" - "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" - "zdomain12.govInterstateReady\n" - "zdomain9.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" - "sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" - "xdomain7.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" + "cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,(blank)\n" + "adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,(blank)\n" + "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady(blank)\n" + "zdomain12.govInterstateReady(blank)\n" + "zdomain9.gov,Federal,ArmedForcesRetirementHome,Deleted,(blank)\n" + "sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,(blank)\n" + "xdomain7.gov,FederalArmedForcesRetirementHome,Deleted,(blank)\n" ) # Normalize line endings and remove commas, @@ -526,7 +532,7 @@ class ExportDataTest(MockDb, MockEppLib): Domain.State.ON_HOLD, ], } - self.maxDiff = None + # Call the export functions write_csv_for_domains( writer, @@ -548,14 +554,14 @@ class ExportDataTest(MockDb, MockEppLib): "Organization name,City,State,AO,AO email," "Security contact email,Domain manager 1,DM1 status,Domain manager 2,DM2 status," "Domain manager 3,DM3 status,Domain manager 4,DM4 status\n" - "adomain10.gov,Ready,,Federal,Armed Forces Retirement Home,,,, , ,squeaker@rocks.com, I\n" - "adomain2.gov,Dns needed,,Interstate,,,,, , , ,meoward@rocks.com, R,squeaker@rocks.com, I\n" - "cdomain11.govReadyFederal-ExecutiveWorldWarICentennialCommissionmeoward@rocks.comR\n" - "cdomain1.gov,Ready,,Federal - Executive,World War I Centennial Commission,,," + "adomain10.gov,Ready,(blank),Federal,Armed Forces Retirement Home,,,, , ,squeaker@rocks.com, I\n" + "adomain2.gov,Dns needed,(blank),Interstate,,,,, , , ,meoward@rocks.com, R,squeaker@rocks.com, I\n" + "cdomain11.govReady,(blank),Federal-ExecutiveWorldWarICentennialCommissionmeoward@rocks.comR\n" + "cdomain1.gov,Ready,(blank),Federal - Executive,World War I Centennial Commission,,," ", , , ,meoward@rocks.com,R,info@example.com,R,big_lebowski@dude.co,R," "woofwardthethird@rocks.com,I\n" - "ddomain3.gov,On hold,,Federal,Armed Forces Retirement Home,,,, , , ,,\n" - "zdomain12.govReadyInterstatemeoward@rocks.comR\n" + "ddomain3.gov,On hold,(blank),Federal,Armed Forces Retirement Home,,,, , , ,,\n" + "zdomain12.gov,Ready,(blank),Interstate,meoward@rocks.com,R\n" ) # Normalize line endings and remove commas, # spaces and leading/trailing whitespace @@ -580,7 +586,7 @@ class ExportDataTest(MockDb, MockEppLib): csv_file.seek(0) # Read the content into a variable csv_content = csv_file.read() - self.maxDiff = None + # We expect the READY domain names with the domain managers: Their counts, and listing at end_date. expected_content = ( "MANAGED DOMAINS COUNTS AT START DATE\n" @@ -623,7 +629,7 @@ class ExportDataTest(MockDb, MockEppLib): csv_file.seek(0) # Read the content into a variable csv_content = csv_file.read() - self.maxDiff = None + # We expect the READY domain names with the domain managers: Their counts, and listing at end_date. expected_content = ( "UNMANAGED DOMAINS AT START DATE\n" @@ -698,7 +704,7 @@ class HelperFunctions(MockDb): """This asserts that 1=1. Its limited usefulness lies in making sure the helper methods stay healthy.""" def test_get_default_start_date(self): - expected_date = timezone.make_aware(datetime(2023, 11, 1)) + expected_date = get_time_aware_date() actual_date = get_default_start_date() self.assertEqual(actual_date, expected_date) diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index 8787f9e74..283c884f9 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -96,7 +96,8 @@ def parse_row_for_domain( FIELDS = { "Domain name": domain.name, "Status": domain.get_state_display(), - "Expiration date": domain.expiration_date, + "First ready on": domain.first_ready or "(blank)", + "Expiration date": domain.expiration_date or "(blank)", "Domain type": domain_type, "Agency": domain_info.federal_agency, "Organization name": domain_info.organization_name, @@ -106,7 +107,6 @@ def parse_row_for_domain( "AO email": domain_info.authorizing_official.email if domain_info.authorizing_official else " ", "Security contact email": security_email, "Created at": domain.created_at, - "First ready": domain.first_ready, "Deleted": domain.deleted, } @@ -378,13 +378,17 @@ def write_csv_for_requests( def export_data_type_to_csv(csv_file): - """All domains report with extra columns""" + """ + All domains report with extra columns. + This maps to the "All domain metadata" button. + """ writer = csv.writer(csv_file) # define columns to include in export columns = [ "Domain name", "Status", + "First ready on", "Expiration date", "Domain type", "Agency",