mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-05 17:28:31 +02:00
Merge pull request #1636 from cisagov/dk/1571-contact-patches
Issue 1571: contact patches when contact has multiple joins
This commit is contained in:
commit
715c59194b
6 changed files with 602 additions and 49 deletions
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue