Skip to content

Commit

Permalink
Merge pull request #3775 from GSA-TTS/main
Browse files Browse the repository at this point in the history
  • Loading branch information
jadudm authored Apr 27, 2024
2 parents 12ead60 + 581ca83 commit 109d677
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 11 deletions.
2 changes: 1 addition & 1 deletion backend/audit/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def test_invalid_ein(self):

with self.assertRaisesRegex(
exceptions.ValidationError,
"does not match",
"is not valid under any of the given schemas",
msg=f"ValidationError not raised with EIN = {bad_ein}",
):
validate(instance, schema)
Expand Down
2 changes: 2 additions & 0 deletions backend/audit/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ def validate_use_of_gsa_migration_keyword(general_information, is_data_migration
if not is_data_migration and settings.GSA_MIGRATION in [
general_information.get("auditee_email", ""),
general_information.get("auditor_email", ""),
general_information.get("ein", ""),
general_information.get("auditor_ein", ""),
]:
raise ValidationError(
_(f"{settings.GSA_MIGRATION} not permitted outside of migrations"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,49 @@ def track_transformations(
)


def xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration(general_information):
"""Replaces empty or invalid auditor EIN with GSA Migration keyword"""
# Transformation recorded.
if not (
general_information.get("auditor_ein")
and re.match(
settings.EMPLOYER_IDENTIFICATION_NUMBER,
general_information.get("auditor_ein"),
)
):
track_transformations(
"AUDITOR_EIN",
general_information.get("auditor_ein"),
"auditor_ein",
settings.GSA_MIGRATION,
"xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration",
)
general_information["auditor_ein"] = settings.GSA_MIGRATION

return general_information


def xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration(general_information):
"""Replaces empty or invalid auditee EIN with GSA Migration keyword"""
# Transformation recorded.
if not (
general_information.get("ein")
and re.match(
settings.EMPLOYER_IDENTIFICATION_NUMBER, general_information.get("ein")
)
):
track_transformations(
"EIN",
general_information.get("ein"),
"auditee_ein",
settings.GSA_MIGRATION,
"xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration",
)
general_information["ein"] = settings.GSA_MIGRATION

return general_information


def general_information(audit_header):
"""Generates general information JSON."""
xform_update_multiple_eins_flag(audit_header)
Expand All @@ -349,6 +392,8 @@ def general_information(audit_header):
xform_audit_type,
xform_replace_empty_auditor_email,
xform_replace_empty_auditee_email,
xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration,
xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration,
]

for transform in transformations:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime, timedelta
from unittest.mock import patch
from django.conf import settings
from django.test import SimpleTestCase

Expand All @@ -13,6 +14,8 @@
xform_entity_type,
xform_replace_empty_auditor_email,
xform_replace_empty_auditee_email,
xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration,
xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration,
)
from .exception_utils import (
DataMigrationError,
Expand Down Expand Up @@ -261,3 +264,69 @@ def test_missing_auditee_email(self):
input_data = {}
expected_output = {"auditee_email": settings.GSA_MIGRATION}
self.assertEqual(xform_replace_empty_auditee_email(input_data), expected_output)


class TestXformReplaceEmptyOrInvalidEins(SimpleTestCase):
def test_auditor_ein_valid(self):
"""Test that valid auditor EIN is not replaced."""
info = {"auditor_ein": "123456789"}
result = xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration(info)
self.assertEqual(result["auditor_ein"], "123456789")

def test_auditor_ein_invalid_replaced(self):
"""Test that invalid auditor EIN is replaced."""
info = {"auditor_ein": "invalid_ein"}
with patch(
"census_historical_migration.sac_general_lib.general_information.track_transformations"
) as mock_track:
result = xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration(info)
mock_track.assert_called_once_with(
"AUDITOR_EIN",
"invalid_ein",
"auditor_ein",
settings.GSA_MIGRATION,
"xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration",
)
self.assertEqual(result["auditor_ein"], settings.GSA_MIGRATION)

def test_auditor_ein_empty_replaced(self):
"""Test that empty auditor EIN is replaced."""
info = {"auditor_ein": ""}
with patch(
"census_historical_migration.sac_general_lib.general_information.track_transformations"
) as mock_track:
result = xform_replace_empty_or_invalid_auditor_ein_with_gsa_migration(info)
mock_track.assert_called_once()
self.assertEqual(result["auditor_ein"], settings.GSA_MIGRATION)

def test_auditee_ein_valid(self):
"""Test that valid auditee EIN is not replaced."""
info = {"ein": "123456789"}
result = xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration(info)
self.assertEqual(result["ein"], "123456789")

def test_auditee_ein_invalid_replaced(self):
"""Test that invalid auditee EIN is replaced."""
info = {"ein": "invalid_ein"}
with patch(
"census_historical_migration.sac_general_lib.general_information.track_transformations"
) as mock_track:
result = xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration(info)
mock_track.assert_called_once_with(
"EIN",
"invalid_ein",
"auditee_ein",
settings.GSA_MIGRATION,
"xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration",
)
self.assertEqual(result["ein"], settings.GSA_MIGRATION)

def test_auditee_ein_empty_replaced(self):
"""Test that empty auditee EIN is replaced."""
info = {"ein": ""}
with patch(
"census_historical_migration.sac_general_lib.general_information.track_transformations"
) as mock_track:
result = xform_replace_empty_or_invalid_auditee_ein_with_gsa_migration(info)
mock_track.assert_called_once()
self.assertEqual(result["ein"], settings.GSA_MIGRATION)
1 change: 1 addition & 0 deletions backend/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@
REGEX_RD_EXTENSION = r"^RD[0-9]?$"
REGEX_THREE_DIGIT_EXTENSION = r"^[0-9]{3}[A-Za-z]{0,1}$"
REGEX_U_EXTENSION = r"^U[0-9]{2}$"
EMPLOYER_IDENTIFICATION_NUMBER = r"^[0-9]{9}$"
GSA_MIGRATION = "GSA_MIGRATION" # There is a copy of `GSA_MIGRATION` in Base.libsonnet. If you change it here, change it there too.
GSA_MIGRATION_INT = -999999999
# A copy of theses constants exists in schema/source/base/Base.libsonnet
Expand Down
24 changes: 20 additions & 4 deletions backend/schemas/output/sections/GeneralInformation.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,16 @@
"type": "string"
},
"auditor_ein": {
"pattern": "^[0-9]{9}$",
"type": "string"
"oneOf": [
{
"pattern": "^[0-9]{9}$",
"type": "string"
},
{
"const": "GSA_MIGRATION",
"type": "string"
}
]
},
"auditor_ein_not_an_ssn_attestation": {
"type": "boolean"
Expand Down Expand Up @@ -294,8 +302,16 @@
"type": "string"
},
"ein": {
"pattern": "^[0-9]{9}$",
"type": "string"
"oneOf": [
{
"pattern": "^[0-9]{9}$",
"type": "string"
},
{
"const": "GSA_MIGRATION",
"type": "string"
}
]
},
"ein_not_an_ssn_attestation": {
"type": "boolean"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,16 @@
"type": "string"
},
"auditor_ein": {
"pattern": "^[0-9]{9}$",
"type": "string"
"oneOf": [
{
"pattern": "^[0-9]{9}$",
"type": "string"
},
{
"const": "GSA_MIGRATION",
"type": "string"
}
]
},
"auditor_ein_not_an_ssn_attestation": {
"type": "boolean"
Expand Down Expand Up @@ -404,8 +412,16 @@
"type": "string"
},
"ein": {
"pattern": "^[0-9]{9}$",
"type": "string"
"oneOf": [
{
"pattern": "^[0-9]{9}$",
"type": "string"
},
{
"const": "GSA_MIGRATION",
"type": "string"
}
]
},
"ein_not_an_ssn_attestation": {
"type": "boolean"
Expand Down
1 change: 1 addition & 0 deletions backend/schemas/source/base/Base.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ local Compound = {
maxLength: 500,
},
EmployerIdentificationNumber: Types.string {
# A python version of these regexes also exists in settings.py
pattern: '^[0-9]{9}$',
},
UniqueEntityIdentifier: {
Expand Down
18 changes: 16 additions & 2 deletions backend/schemas/source/sections/GeneralInformation.schema.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ Typechecks fields, but allows for empty data as well. Contains conditional check

// Auditee information
auditee_uei: Base.Compound.UniqueEntityIdentifier,
ein: Base.Compound.EmployerIdentificationNumber,
ein: {
oneOf: [
Base.Compound.EmployerIdentificationNumber,
Types.string {
const: Base.Const.GSA_MIGRATION,
},
],
},
ein_not_an_ssn_attestation: Types.boolean,
auditee_name: Types.string {
maxLength: 100,
Expand Down Expand Up @@ -58,7 +65,14 @@ Typechecks fields, but allows for empty data as well. Contains conditional check
},

// Auditor information
auditor_ein: Base.Compound.EmployerIdentificationNumber,
auditor_ein: {
oneOf: [
Base.Compound.EmployerIdentificationNumber,
Types.string {
const: Base.Const.GSA_MIGRATION,
},
],
},
auditor_ein_not_an_ssn_attestation: Types.boolean,
auditor_firm_name: Types.string {
maxLength: 100,
Expand Down

0 comments on commit 109d677

Please sign in to comment.