Merge pull request #1636 from cisagov/dk/1571-contact-patches

Issue 1571: contact patches when contact has multiple joins
This commit is contained in:
dave-kennedy-ecs 2024-01-17 17:30:17 -05:00 committed by GitHub
commit 715c59194b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 602 additions and 49 deletions

View file

@ -7,7 +7,7 @@ from phonenumber_field.formfields import PhoneNumberField # type: ignore
from django import forms
from django.core.validators import RegexValidator, MaxLengthValidator
from django.utils.safestring import mark_safe
from django.db.models.fields.related import ForeignObjectRel, OneToOneField
from django.db.models.fields.related import ForeignObjectRel
from api.views import DOMAIN_API_MESSAGES
@ -96,39 +96,10 @@ class RegistrarFormSet(forms.BaseFormSet):
"""
raise NotImplementedError
def has_more_than_one_join(self, db_obj, rel, related_name):
"""Helper for finding whether an object is joined more than once."""
# threshold is the number of related objects that are acceptable
# when determining if related objects exist. threshold is 0 for most
# relationships. if the relationship is related_name, we know that
# there is already exactly 1 acceptable relationship (the one we are
# attempting to delete), so the threshold is 1
threshold = 1 if rel == related_name else 0
# Raise a KeyError if rel is not a defined field on the db_obj model
# This will help catch any errors in reverse_join config on forms
if rel not in [field.name for field in db_obj._meta.get_fields()]:
raise KeyError(f"{rel} is not a defined field on the {db_obj._meta.model_name} model.")
# if attr rel in db_obj is not None, then test if reference object(s) exist
if getattr(db_obj, rel) is not None:
field = db_obj._meta.get_field(rel)
if isinstance(field, OneToOneField):
# if the rel field is a OneToOne field, then we have already
# determined that the object exists (is not None)
return True
elif isinstance(field, ForeignObjectRel):
# if the rel field is a ManyToOne or ManyToMany, then we need
# to determine if the count of related objects is greater than
# the threshold
return getattr(db_obj, rel).count() > threshold
return False
def _to_database(
self,
obj: DomainApplication,
join: str,
reverse_joins: list,
should_delete: Callable,
pre_update: Callable,
pre_create: Callable,
@ -165,15 +136,21 @@ class RegistrarFormSet(forms.BaseFormSet):
# matching database object exists, update it
if db_obj is not None and cleaned:
if should_delete(cleaned):
if any(self.has_more_than_one_join(db_obj, rel, related_name) for rel in reverse_joins):
if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name):
# Remove the specific relationship without deleting the object
getattr(db_obj, related_name).remove(self.application)
else:
# If there are no other relationships, delete the object
db_obj.delete()
else:
pre_update(db_obj, cleaned)
db_obj.save()
if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name):
# create a new db_obj and disconnect existing one
getattr(db_obj, related_name).remove(self.application)
kwargs = pre_create(db_obj, cleaned)
getattr(obj, join).create(**kwargs)
else:
pre_update(db_obj, cleaned)
db_obj.save()
# no matching database object, create it
# make sure not to create a database object if cleaned has 'delete' attribute
@ -351,13 +328,18 @@ class AboutYourOrganizationForm(RegistrarForm):
class AuthorizingOfficialForm(RegistrarForm):
JOIN = "authorizing_official"
def to_database(self, obj):
if not self.is_valid():
return
contact = getattr(obj, "authorizing_official", None)
if contact is not None:
if contact is not None and not contact.has_more_than_one_join("authorizing_official"):
# if contact exists in the database and is not joined to other entities
super().to_database(contact)
else:
# no contact exists OR contact exists which is joined also to other entities;
# in either case, create a new contact and update it
contact = Contact()
super().to_database(contact)
obj.authorizing_official = contact
@ -411,7 +393,7 @@ class BaseCurrentSitesFormSet(RegistrarFormSet):
def to_database(self, obj: DomainApplication):
# If we want to test against multiple joins for a website object, replace the empty array
# and change the JOIN in the models to allow for reverse references
self._to_database(obj, self.JOIN, [], self.should_delete, self.pre_update, self.pre_create)
self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
@classmethod
def from_database(cls, obj):
@ -470,7 +452,7 @@ class BaseAlternativeDomainFormSet(RegistrarFormSet):
def to_database(self, obj: DomainApplication):
# If we want to test against multiple joins for a website object, replace the empty array and
# change the JOIN in the models to allow for reverse references
self._to_database(obj, self.JOIN, [], self.should_delete, self.pre_update, self.pre_create)
self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
@classmethod
def on_fetch(cls, query):
@ -549,13 +531,18 @@ class PurposeForm(RegistrarForm):
class YourContactForm(RegistrarForm):
JOIN = "submitter"
def to_database(self, obj):
if not self.is_valid():
return
contact = getattr(obj, "submitter", None)
if contact is not None:
if contact is not None and not contact.has_more_than_one_join("submitted_applications"):
# if contact exists in the database and is not joined to other entities
super().to_database(contact)
else:
# no contact exists OR contact exists which is joined also to other entities;
# in either case, create a new contact and update it
contact = Contact()
super().to_database(contact)
obj.submitter = contact
@ -708,20 +695,10 @@ class BaseOtherContactsFormSet(RegistrarFormSet):
must co-exist.
Also, other_contacts have db relationships to multiple db objects. When attempting
to delete an other_contact from an application, those db relationships must be
tested and handled; this is configured with REVERSE_JOINS, which is an array of
strings representing the relationships between contact model and other models.
tested and handled.
"""
JOIN = "other_contacts"
REVERSE_JOINS = [
"user",
"authorizing_official",
"submitted_applications",
"contact_applications",
"information_authorizing_official",
"submitted_applications_information",
"contact_applications_information",
]
def get_deletion_widget(self):
return forms.HiddenInput(attrs={"class": "deletion"})
@ -753,7 +730,7 @@ class BaseOtherContactsFormSet(RegistrarFormSet):
return cleaned
def to_database(self, obj: DomainApplication):
self._to_database(obj, self.JOIN, self.REVERSE_JOINS, self.should_delete, self.pre_update, self.pre_create)
self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
@classmethod
def from_database(cls, obj):

View file

@ -210,6 +210,8 @@ class ContactForm(forms.ModelForm):
class AuthorizingOfficialContactForm(ContactForm):
"""Form for updating authorizing official contacts."""
JOIN = "authorizing_official"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -230,6 +232,29 @@ class AuthorizingOfficialContactForm(ContactForm):
self.fields["email"].error_messages = {
"required": "Enter an email address in the required format, like name@example.com."
}
self.domainInfo = None
def set_domain_info(self, domainInfo):
"""Set the domain information for the form.
The form instance is associated with the contact itself. In order to access the associated
domain information object, this needs to be set in the form by the view."""
self.domainInfo = domainInfo
def save(self, commit=True):
"""Override the save() method of the BaseModelForm."""
# Get the Contact object from the db for the Authorizing Official
db_ao = Contact.objects.get(id=self.instance.id)
if self.domainInfo and db_ao.has_more_than_one_join("information_authorizing_official"):
# Handle the case where the domain information object is available and the AO Contact
# has more than one joined object.
# In this case, create a new Contact, and update the new Contact with form data.
# Then associate with domain information object as the authorizing_official
data = dict(self.cleaned_data.items())
self.domainInfo.authorizing_official = Contact.objects.create(**data)
self.domainInfo.save()
else:
super().save()
class DomainSecurityEmailForm(forms.Form):

View file

@ -54,6 +54,47 @@ class Contact(TimeStampedModel):
db_index=True,
)
def _get_all_relations(self):
"""Returns an array of all fields which are relations"""
return [f.name for f in self._meta.get_fields() if f.is_relation]
def has_more_than_one_join(self, expected_relation):
"""Helper for finding whether an object is joined more than once.
expected_relation is the one relation with one expected join"""
# all_relations is the list of all_relations (from contact) to be checked for existing joins
all_relations = self._get_all_relations()
return any(self._has_more_than_one_join_per_relation(rel, expected_relation) for rel in all_relations)
def _has_more_than_one_join_per_relation(self, relation, expected_relation):
"""Helper for finding whether an object is joined more than once."""
# threshold is the number of related objects that are acceptable
# when determining if related objects exist. threshold is 0 for most
# relationships. if the relationship is expected_relation, we know that
# there is already exactly 1 acceptable relationship (the one we are
# attempting to delete), so the threshold is 1
threshold = 1 if relation == expected_relation else 0
# Raise a KeyError if rel is not a defined field on the db_obj model
# This will help catch any errors in relation passed.
if relation not in [field.name for field in self._meta.get_fields()]:
raise KeyError(f"{relation} is not a defined field on the {self._meta.model_name} model.")
# if attr rel in db_obj is not None, then test if reference object(s) exist
if getattr(self, relation) is not None:
field = self._meta.get_field(relation)
if isinstance(field, models.OneToOneField):
# if the rel field is a OneToOne field, then we have already
# determined that the object exists (is not None)
# so return True unless the relation being tested is the expected_relation
is_not_expected_relation = relation != expected_relation
return is_not_expected_relation
elif isinstance(field, models.ForeignObjectRel):
# if the rel field is a ManyToOne or ManyToMany, then we need
# to determine if the count of related objects is greater than
# the threshold
return getattr(self, relation).count() > threshold
return False
def get_formatted_name(self):
"""Returns the contact's name in Western order."""
names = [n for n in [self.first_name, self.middle_name, self.last_name] if n]

View file

@ -691,8 +691,12 @@ class TestContact(TestCase):
self.user, _ = User.objects.get_or_create(email=self.email, first_name="Jeff", last_name="Lebowski")
self.contact, _ = Contact.objects.get_or_create(user=self.user)
self.contact_as_ao, _ = Contact.objects.get_or_create(email="newguy@igorville.gov")
self.application = DomainApplication.objects.create(creator=self.user, authorizing_official=self.contact_as_ao)
def tearDown(self):
super().tearDown()
DomainApplication.objects.all().delete()
Contact.objects.all().delete()
User.objects.all().delete()
@ -766,3 +770,12 @@ class TestContact(TestCase):
# Updating the contact's email does not propagate
self.assertEqual(self.invalid_contact.email, "joey.baloney@diaperville.com")
self.assertEqual(self.invalid_user.email, "intern@igorville.gov")
def test_has_more_than_one_join(self):
"""Test the Contact model method, has_more_than_one_join"""
# test for a contact which has one user defined
self.assertFalse(self.contact.has_more_than_one_join("user"))
self.assertTrue(self.contact.has_more_than_one_join("authorizing_official"))
# test for a contact which is assigned as an authorizing official on an application
self.assertFalse(self.contact_as_ao.has_more_than_one_join("authorizing_official"))
self.assertTrue(self.contact_as_ao.has_more_than_one_join("submitted_applications"))

View file

@ -1306,6 +1306,440 @@ class DomainApplicationTests(TestWithUser, WebTest):
# Enter the first name ...
self.assertContains(response, "Enter the first name / given name of this contact.")
def test_edit_other_contact_in_place(self):
"""When you:
1. edit an existing contact which is not joined to another model,
2. then submit,
The application is linked to the existing contact, and the existing contact updated."""
# Populate the database with a domain application that
# has 1 "other contact" assigned to it
# We'll do it from scratch
ao, _ = Contact.objects.get_or_create(
first_name="Testy",
last_name="Tester",
title="Chief Tester",
email="testy@town.com",
phone="(201) 555 5555",
)
you, _ = Contact.objects.get_or_create(
first_name="Testy you",
last_name="Tester you",
title="Admin Tester",
email="testy-admin@town.com",
phone="(201) 555 5556",
)
other, _ = Contact.objects.get_or_create(
first_name="Testy2",
last_name="Tester2",
title="Another Tester",
email="testy2@town.com",
phone="(201) 555 5557",
)
application, _ = DomainApplication.objects.get_or_create(
organization_type="federal",
federal_type="executive",
purpose="Purpose of the site",
anything_else="No",
is_policy_acknowledged=True,
organization_name="Testorg",
address_line1="address 1",
state_territory="NY",
zipcode="10002",
authorizing_official=ao,
submitter=you,
creator=self.user,
status="started",
)
application.other_contacts.add(other)
# other_contact_pk is the initial pk of the other contact. set it before update
# to be able to verify after update that the same contact object is in place
other_contact_pk = other.id
# prime the form by visiting /edit
self.app.get(reverse("edit-application", kwargs={"id": application.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
# and then setting the cookie on each request.
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("application:other_contacts"))
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
# Minimal check to ensure the form is loaded
self.assertEqual(other_contacts_form["other_contacts-0-first_name"].value, "Testy2")
# update the first name of the contact
other_contacts_form["other_contacts-0-first_name"] = "Testy3"
# Submit the updated form
other_contacts_form.submit()
application.refresh_from_db()
# assert that the Other Contact is updated "in place"
other_contact = application.other_contacts.all()[0]
self.assertEquals(other_contact_pk, other_contact.id)
self.assertEquals("Testy3", other_contact.first_name)
def test_edit_other_contact_creates_new(self):
"""When you:
1. edit an existing contact which IS joined to another model,
2. then submit,
The application is linked to a new contact, and the new contact is updated."""
# Populate the database with a domain application that
# has 1 "other contact" assigned to it, the other contact is also
# the authorizing official initially
# We'll do it from scratch
ao, _ = Contact.objects.get_or_create(
first_name="Testy",
last_name="Tester",
title="Chief Tester",
email="testy@town.com",
phone="(201) 555 5555",
)
you, _ = Contact.objects.get_or_create(
first_name="Testy you",
last_name="Tester you",
title="Admin Tester",
email="testy-admin@town.com",
phone="(201) 555 5556",
)
application, _ = DomainApplication.objects.get_or_create(
organization_type="federal",
federal_type="executive",
purpose="Purpose of the site",
anything_else="No",
is_policy_acknowledged=True,
organization_name="Testorg",
address_line1="address 1",
state_territory="NY",
zipcode="10002",
authorizing_official=ao,
submitter=you,
creator=self.user,
status="started",
)
application.other_contacts.add(ao)
# other_contact_pk is the initial pk of the other contact. set it before update
# to be able to verify after update that the ao contact is still in place
# and not updated, and that the new contact has a new id
other_contact_pk = ao.id
# prime the form by visiting /edit
self.app.get(reverse("edit-application", kwargs={"id": application.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
# and then setting the cookie on each request.
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("application:other_contacts"))
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
# Minimal check to ensure the form is loaded
self.assertEqual(other_contacts_form["other_contacts-0-first_name"].value, "Testy")
# update the first name of the contact
other_contacts_form["other_contacts-0-first_name"] = "Testy2"
# Submit the updated form
other_contacts_form.submit()
application.refresh_from_db()
# assert that other contact info is updated, and that a new Contact
# is created for the other contact
other_contact = application.other_contacts.all()[0]
self.assertNotEquals(other_contact_pk, other_contact.id)
self.assertEquals("Testy2", other_contact.first_name)
# assert that the authorizing official is not updated
authorizing_official = application.authorizing_official
self.assertEquals("Testy", authorizing_official.first_name)
def test_edit_authorizing_official_in_place(self):
"""When you:
1. edit an authorizing official which is not joined to another model,
2. then submit,
The application is linked to the existing ao, and the ao updated."""
# Populate the database with a domain application that
# has an authorizing_official (ao)
# We'll do it from scratch
ao, _ = Contact.objects.get_or_create(
first_name="Testy",
last_name="Tester",
title="Chief Tester",
email="testy@town.com",
phone="(201) 555 5555",
)
application, _ = DomainApplication.objects.get_or_create(
organization_type="federal",
federal_type="executive",
purpose="Purpose of the site",
anything_else="No",
is_policy_acknowledged=True,
organization_name="Testorg",
address_line1="address 1",
state_territory="NY",
zipcode="10002",
authorizing_official=ao,
creator=self.user,
status="started",
)
# ao_pk is the initial pk of the Authorizing Official. set it before update
# to be able to verify after update that the same Contact object is in place
ao_pk = ao.id
# prime the form by visiting /edit
self.app.get(reverse("edit-application", kwargs={"id": application.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
# and then setting the cookie on each request.
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
ao_page = self.app.get(reverse("application:authorizing_official"))
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
ao_form = ao_page.forms[0]
# Minimal check to ensure the form is loaded
self.assertEqual(ao_form["authorizing_official-first_name"].value, "Testy")
# update the first name of the contact
ao_form["authorizing_official-first_name"] = "Testy2"
# Submit the updated form
ao_form.submit()
application.refresh_from_db()
# assert AO is updated "in place"
updated_ao = application.authorizing_official
self.assertEquals(ao_pk, updated_ao.id)
self.assertEquals("Testy2", updated_ao.first_name)
def test_edit_authorizing_official_creates_new(self):
"""When you:
1. edit an existing authorizing official which IS joined to another model,
2. then submit,
The application is linked to a new Contact, and the new Contact is updated."""
# Populate the database with a domain application that
# has authorizing official assigned to it, the authorizing offical is also
# an other contact initially
# We'll do it from scratch
ao, _ = Contact.objects.get_or_create(
first_name="Testy",
last_name="Tester",
title="Chief Tester",
email="testy@town.com",
phone="(201) 555 5555",
)
application, _ = DomainApplication.objects.get_or_create(
organization_type="federal",
federal_type="executive",
purpose="Purpose of the site",
anything_else="No",
is_policy_acknowledged=True,
organization_name="Testorg",
address_line1="address 1",
state_territory="NY",
zipcode="10002",
authorizing_official=ao,
creator=self.user,
status="started",
)
application.other_contacts.add(ao)
# ao_pk is the initial pk of the authorizing official. set it before update
# to be able to verify after update that the other contact is still in place
# and not updated, and that the new ao has a new id
ao_pk = ao.id
# prime the form by visiting /edit
self.app.get(reverse("edit-application", kwargs={"id": application.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
# and then setting the cookie on each request.
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
ao_page = self.app.get(reverse("application:authorizing_official"))
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
ao_form = ao_page.forms[0]
# Minimal check to ensure the form is loaded
self.assertEqual(ao_form["authorizing_official-first_name"].value, "Testy")
# update the first name of the contact
ao_form["authorizing_official-first_name"] = "Testy2"
# Submit the updated form
ao_form.submit()
application.refresh_from_db()
# assert that the other contact is not updated
other_contacts = application.other_contacts.all()
other_contact = other_contacts[0]
self.assertEquals(ao_pk, other_contact.id)
self.assertEquals("Testy", other_contact.first_name)
# assert that the authorizing official is updated
authorizing_official = application.authorizing_official
self.assertEquals("Testy2", authorizing_official.first_name)
def test_edit_submitter_in_place(self):
"""When you:
1. edit a submitter (your contact) which is not joined to another model,
2. then submit,
The application is linked to the existing submitter, and the submitter updated."""
# Populate the database with a domain application that
# has a submitter
# We'll do it from scratch
you, _ = Contact.objects.get_or_create(
first_name="Testy",
last_name="Tester",
title="Chief Tester",
email="testy@town.com",
phone="(201) 555 5555",
)
application, _ = DomainApplication.objects.get_or_create(
organization_type="federal",
federal_type="executive",
purpose="Purpose of the site",
anything_else="No",
is_policy_acknowledged=True,
organization_name="Testorg",
address_line1="address 1",
state_territory="NY",
zipcode="10002",
submitter=you,
creator=self.user,
status="started",
)
# submitter_pk is the initial pk of the submitter. set it before update
# to be able to verify after update that the same contact object is in place
submitter_pk = you.id
# prime the form by visiting /edit
self.app.get(reverse("edit-application", kwargs={"id": application.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
# and then setting the cookie on each request.
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
your_contact_page = self.app.get(reverse("application:your_contact"))
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
your_contact_form = your_contact_page.forms[0]
# Minimal check to ensure the form is loaded
self.assertEqual(your_contact_form["your_contact-first_name"].value, "Testy")
# update the first name of the contact
your_contact_form["your_contact-first_name"] = "Testy2"
# Submit the updated form
your_contact_form.submit()
application.refresh_from_db()
updated_submitter = application.submitter
self.assertEquals(submitter_pk, updated_submitter.id)
self.assertEquals("Testy2", updated_submitter.first_name)
def test_edit_submitter_creates_new(self):
"""When you:
1. edit an existing your contact which IS joined to another model,
2. then submit,
The application is linked to a new Contact, and the new Contact is updated."""
# Populate the database with a domain application that
# has submitter assigned to it, the submitter is also
# an other contact initially
# We'll do it from scratch
submitter, _ = Contact.objects.get_or_create(
first_name="Testy",
last_name="Tester",
title="Chief Tester",
email="testy@town.com",
phone="(201) 555 5555",
)
application, _ = DomainApplication.objects.get_or_create(
organization_type="federal",
federal_type="executive",
purpose="Purpose of the site",
anything_else="No",
is_policy_acknowledged=True,
organization_name="Testorg",
address_line1="address 1",
state_territory="NY",
zipcode="10002",
submitter=submitter,
creator=self.user,
status="started",
)
application.other_contacts.add(submitter)
# submitter_pk is the initial pk of the your contact. set it before update
# to be able to verify after update that the other contact is still in place
# and not updated, and that the new submitter has a new id
submitter_pk = submitter.id
# prime the form by visiting /edit
self.app.get(reverse("edit-application", kwargs={"id": application.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
# and then setting the cookie on each request.
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
your_contact_page = self.app.get(reverse("application:your_contact"))
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
your_contact_form = your_contact_page.forms[0]
# Minimal check to ensure the form is loaded
self.assertEqual(your_contact_form["your_contact-first_name"].value, "Testy")
# update the first name of the contact
your_contact_form["your_contact-first_name"] = "Testy2"
# Submit the updated form
your_contact_form.submit()
application.refresh_from_db()
# assert that the other contact is not updated
other_contacts = application.other_contacts.all()
other_contact = other_contacts[0]
self.assertEquals(submitter_pk, other_contact.id)
self.assertEquals("Testy", other_contact.first_name)
# assert that the submitter is updated
submitter = application.submitter
self.assertEquals("Testy2", submitter.first_name)
def test_application_about_your_organiztion_interstate(self):
"""Special districts have to answer an additional question."""
intro_page = self.app.get(reverse("application:"))
@ -2686,6 +3120,65 @@ class TestDomainAuthorizingOfficial(TestDomainOverview):
page = self.app.get(reverse("domain-authorizing-official", kwargs={"pk": self.domain.id}))
self.assertContains(page, "Testy")
def test_domain_edit_authorizing_official_in_place(self):
"""When editing an authorizing official for domain information and AO is not
joined to any other objects"""
self.domain_information.authorizing_official = Contact(
first_name="Testy", last_name="Tester", title="CIO", email="nobody@igorville.gov"
)
self.domain_information.authorizing_official.save()
self.domain_information.save()
ao_page = self.app.get(reverse("domain-authorizing-official", kwargs={"pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
ao_form = ao_page.forms[0]
self.assertEqual(ao_form["first_name"].value, "Testy")
ao_form["first_name"] = "Testy2"
# ao_pk is the initial pk of the authorizing official. set it before update
# to be able to verify after update that the same contact object is in place
ao_pk = self.domain_information.authorizing_official.id
ao_form.submit()
# refresh domain information
self.domain_information.refresh_from_db()
self.assertEqual("Testy2", self.domain_information.authorizing_official.first_name)
self.assertEqual(ao_pk, self.domain_information.authorizing_official.id)
def test_domain_edit_authorizing_official_creates_new(self):
"""When editing an authorizing official for domain information and AO IS
joined to another object"""
# set AO and Other Contact to the same Contact object
self.domain_information.authorizing_official = Contact(
first_name="Testy", last_name="Tester", title="CIO", email="nobody@igorville.gov"
)
self.domain_information.authorizing_official.save()
self.domain_information.save()
self.domain_information.other_contacts.add(self.domain_information.authorizing_official)
self.domain_information.save()
# load the Authorizing Official in the web form
ao_page = self.app.get(reverse("domain-authorizing-official", kwargs={"pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
ao_form = ao_page.forms[0]
# verify the first name is "Testy" and then change it to "Testy2"
self.assertEqual(ao_form["first_name"].value, "Testy")
ao_form["first_name"] = "Testy2"
# ao_pk is the initial pk of the authorizing official. set it before update
# to be able to verify after update that the same contact object is in place
ao_pk = self.domain_information.authorizing_official.id
ao_form.submit()
# refresh domain information
self.domain_information.refresh_from_db()
# assert that AO information is updated, and that the AO is a new Contact
self.assertEqual("Testy2", self.domain_information.authorizing_official.first_name)
self.assertNotEqual(ao_pk, self.domain_information.authorizing_official.id)
# assert that the Other Contact information is not updated and that the Other Contact
# is the original Contact object
other_contact = self.domain_information.other_contacts.all()[0]
self.assertEqual("Testy", other_contact.first_name)
self.assertEqual(ao_pk, other_contact.id)
class TestDomainOrganization(TestDomainOverview):
def test_domain_org_name_address(self):

View file

@ -222,6 +222,10 @@ class DomainAuthorizingOfficialView(DomainFormBaseView):
def form_valid(self, form):
"""The form is valid, save the authorizing official."""
# Set the domain information in the form so that it can be accessible
# to associate a new Contact as authorizing official, if new Contact is needed
# in the save() method
form.set_domain_info(self.object.domain_info)
form.save()
messages.success(self.request, "The authorizing official for this domain has been updated.")