From 7a3e1bcb2c94960de8c0cddfb814d6136dcc92b6 Mon Sep 17 00:00:00 2001 From: Seamus Johnston Date: Thu, 25 May 2023 13:53:02 -0500 Subject: [PATCH] Create a DraftDomain model for requested domains --- src/api/views.py | 10 +-- src/registrar/fixtures.py | 8 ++- src/registrar/forms/application_wizard.py | 12 ++-- .../management/commands/load_domains_data.py | 12 +--- ...ainapplication_approved_domain_and_more.py | 66 +++++++++++++++++++ src/registrar/models/__init__.py | 3 + src/registrar/models/domain_application.py | 24 +++++-- src/registrar/models/domain_information.py | 20 +++--- src/registrar/models/draft_domain.py | 22 +++++++ src/registrar/models/utility/domain_helper.py | 64 ++++++++++++++++++ src/registrar/models/website.py | 32 --------- src/registrar/tests/test_emails.py | 4 +- src/registrar/tests/test_models.py | 58 +++------------- src/registrar/tests/test_models_domain.py | 54 +++++++++++++++ src/registrar/tests/test_views.py | 7 +- 15 files changed, 272 insertions(+), 124 deletions(-) create mode 100644 src/registrar/migrations/0022_draftdomain_domainapplication_approved_domain_and_more.py create mode 100644 src/registrar/models/draft_domain.py create mode 100644 src/registrar/models/utility/domain_helper.py create mode 100644 src/registrar/tests/test_models_domain.py diff --git a/src/api/views.py b/src/api/views.py index ab68a42d3..e19e060ef 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -37,7 +37,7 @@ def _domains(): Fetch a file from DOMAIN_FILE_URL, parse the CSV for the domain, lowercase everything and return the list. """ - Domain = apps.get_model("registrar.Domain") + DraftDomain = apps.get_model("registrar.DraftDomain") # 5 second timeout file_contents = requests.get(DOMAIN_FILE_URL, timeout=5).text domains = set() @@ -46,7 +46,7 @@ def _domains(): # get the domain before the first comma domain = line.split(",", 1)[0] # sanity-check the string we got from the file here - if Domain.string_could_be_domain(domain): + if DraftDomain.string_could_be_domain(domain): # lowercase everything when we put it in domains domains.add(domain.lower()) return domains @@ -75,12 +75,12 @@ def available(request, domain=""): Response is a JSON dictionary with the key "available" and value true or false. """ - Domain = apps.get_model("registrar.Domain") + DraftDomain = apps.get_model("registrar.DraftDomain") # validate that the given domain could be a domain name and fail early if # not. if not ( - Domain.string_could_be_domain(domain) - or Domain.string_could_be_domain(domain + ".gov") + DraftDomain.string_could_be_domain(domain) + or DraftDomain.string_could_be_domain(domain + ".gov") ): return JsonResponse( {"available": False, "message": DOMAIN_API_MESSAGES["invalid"]} diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 0161527e1..438f6723b 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -5,7 +5,7 @@ from faker import Faker from registrar.models import ( User, DomainApplication, - Domain, + DraftDomain, Contact, Website, ) @@ -216,11 +216,13 @@ class DomainApplicationFixture: if not da.requested_domain: if "requested_domain" in app and app["requested_domain"] is not None: - da.requested_domain, _ = Domain.objects.get_or_create( + da.requested_domain, _ = DraftDomain.objects.get_or_create( name=app["requested_domain"] ) else: - da.requested_domain = Domain.objects.create(name=cls.fake_dot_gov()) + da.requested_domain = DraftDomain.objects.create( + name=cls.fake_dot_gov() + ) @classmethod def _set_many_to_many_relations(cls, da: DomainApplication, app: dict): diff --git a/src/registrar/forms/application_wizard.py b/src/registrar/forms/application_wizard.py index 333c9c6b1..f04a3488e 100644 --- a/src/registrar/forms/application_wizard.py +++ b/src/registrar/forms/application_wizard.py @@ -11,7 +11,7 @@ from django.utils.safestring import mark_safe from api.views import DOMAIN_API_MESSAGES -from registrar.models import Contact, DomainApplication, Domain +from registrar.models import Contact, DomainApplication, DraftDomain, Domain from registrar.utility import errors logger = logging.getLogger(__name__) @@ -453,7 +453,7 @@ class AlternativeDomainForm(RegistrarForm): """Validation code for domain names.""" try: requested = self.cleaned_data.get("alternative_domain", None) - validated = Domain.validate(requested, blank_ok=True) + validated = DraftDomain.validate(requested, blank_ok=True) except errors.ExtraDotsError: raise forms.ValidationError( DOMAIN_API_MESSAGES["extra_dots"], code="extra_dots" @@ -498,7 +498,7 @@ class BaseAlternativeDomainFormSet(RegistrarFormSet): @classmethod def on_fetch(cls, query): - return [{"alternative_domain": domain.sld} for domain in query] + return [{"alternative_domain": Domain.sld(domain.name)} for domain in query] @classmethod def from_database(cls, obj): @@ -524,7 +524,7 @@ class DotGovDomainForm(RegistrarForm): requested_domain.name = f"{domain}.gov" requested_domain.save() else: - requested_domain = Domain.objects.create(name=f"{domain}.gov") + requested_domain = DraftDomain.objects.create(name=f"{domain}.gov") obj.requested_domain = requested_domain obj.save() @@ -535,14 +535,14 @@ class DotGovDomainForm(RegistrarForm): values = {} requested_domain = getattr(obj, "requested_domain", None) if requested_domain is not None: - values["requested_domain"] = requested_domain.sld + values["requested_domain"] = Domain.sld(requested_domain.name) return values def clean_requested_domain(self): """Validation code for domain names.""" try: requested = self.cleaned_data.get("requested_domain", None) - validated = Domain.validate(requested) + validated = DraftDomain.validate(requested) except errors.BlankValueError: raise forms.ValidationError( DOMAIN_API_MESSAGES["required"], code="required" diff --git a/src/registrar/management/commands/load_domains_data.py b/src/registrar/management/commands/load_domains_data.py index 358a4113e..b205e562e 100644 --- a/src/registrar/management/commands/load_domains_data.py +++ b/src/registrar/management/commands/load_domains_data.py @@ -54,16 +54,6 @@ class Command(BaseCommand): domains = [] for row in reader: name = row["Name"].lower() # we typically use lowercase domains - - # Ensure that there is a `Domain` object for each domain name in - # this file and that it is active. There is a uniqueness - # constraint for active Domain objects, so we are going to account - # for that here with this check so that our later bulk_create - # should succeed - if Domain.objects.filter(name=name, is_active=True).exists(): - # don't do anything, this domain is here and active - continue - else: - domains.append(Domain(name=name, is_active=True)) + domains.append(Domain(name=name)) logger.info("Creating %d new domains", len(domains)) Domain.objects.bulk_create(domains) diff --git a/src/registrar/migrations/0022_draftdomain_domainapplication_approved_domain_and_more.py b/src/registrar/migrations/0022_draftdomain_domainapplication_approved_domain_and_more.py new file mode 100644 index 000000000..3670146f1 --- /dev/null +++ b/src/registrar/migrations/0022_draftdomain_domainapplication_approved_domain_and_more.py @@ -0,0 +1,66 @@ +# Generated by Django 4.2.1 on 2023-05-26 13:14 + +from django.db import migrations, models +import django.db.models.deletion +import registrar.models.utility.domain_helper + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0021_publiccontact_domain_publiccontact_registry_id_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="DraftDomain", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "name", + models.CharField( + default=None, + help_text="Fully qualified domain name", + max_length=253, + ), + ), + ], + options={ + "abstract": False, + }, + bases=(models.Model, registrar.models.utility.domain_helper.DomainHelper), + ), + migrations.AddField( + model_name="domainapplication", + name="approved_domain", + field=models.OneToOneField( + blank=True, + help_text="The approved domain", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_application", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="domainapplication", + name="requested_domain", + field=models.OneToOneField( + blank=True, + help_text="The requested domain", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_application", + to="registrar.draftdomain", + ), + ), + ] diff --git a/src/registrar/models/__init__.py b/src/registrar/models/__init__.py index 9351e312d..542cb00e1 100644 --- a/src/registrar/models/__init__.py +++ b/src/registrar/models/__init__.py @@ -4,6 +4,7 @@ from .contact import Contact from .domain_application import DomainApplication from .domain_information import DomainInformation from .domain import Domain +from .draft_domain import DraftDomain from .host_ip import HostIP from .host import Host from .domain_invitation import DomainInvitation @@ -18,6 +19,7 @@ __all__ = [ "DomainApplication", "DomainInformation", "Domain", + "DraftDomain", "DomainInvitation", "HostIP", "Host", @@ -31,6 +33,7 @@ __all__ = [ auditlog.register(Contact) auditlog.register(DomainApplication) auditlog.register(Domain) +auditlog.register(DraftDomain) auditlog.register(DomainInvitation) auditlog.register(HostIP) auditlog.register(Host) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 44c93892c..3859e04a7 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -400,10 +400,19 @@ class DomainApplication(TimeStampedModel): related_name="current+", ) - requested_domain = models.OneToOneField( + approved_domain = models.OneToOneField( "Domain", null=True, blank=True, + help_text="The approved domain", + related_name="domain_application", + on_delete=models.PROTECT, + ) + + requested_domain = models.OneToOneField( + "DraftDomain", + null=True, + blank=True, help_text="The requested domain", related_name="domain_application", on_delete=models.PROTECT, @@ -499,8 +508,8 @@ class DomainApplication(TimeStampedModel): if self.requested_domain is None: raise ValueError("Requested domain is missing.") - Domain = apps.get_model("registrar.Domain") - if not Domain.string_could_be_domain(self.requested_domain.name): + DraftDomain = apps.get_model("registrar.DraftDomain") + if not DraftDomain.string_could_be_domain(self.requested_domain.name): raise ValueError("Requested domain is not a valid domain name.") # When an application is submitted, we need to send a confirmation email @@ -516,13 +525,16 @@ class DomainApplication(TimeStampedModel): application into an admin on that domain. """ - # create the domain if it doesn't exist + # create the domain Domain = apps.get_model("registrar.Domain") - created_domain, _ = Domain.objects.get_or_create(name=self.requested_domain) + if Domain.objects.filter(name=self.requested_domain.name).exists(): + raise ValueError("Cannot approve. Requested domain is already in use.") + created_domain = Domain.objects.create(name=self.requested_domain.name) + self.approved_domain = created_domain # copy the information from domainapplication into domaininformation DomainInformation = apps.get_model("registrar.DomainInformation") - DomainInformation.create_from_da(self) + DomainInformation.create_from_da(self, domain=created_domain) # create the permission for the user UserDomainRole = apps.get_model("registrar.UserDomainRole") diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index c7832266b..c86a6c04f 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -211,24 +211,24 @@ class DomainInformation(TimeStampedModel): return "" @classmethod - def create_from_da(cls, domain_application): + def create_from_da(cls, domain_application, domain=None): """Takes in a DomainApplication dict and converts it into DomainInformation""" da_dict = domain_application.to_dict() # remove the id so one can be assinged on creation - da_id = da_dict.pop("id") + da_id = da_dict.pop("id", None) # check if we have a record that corresponds with the domain # application, if so short circuit the create domain_info = cls.objects.filter(domain_application__id=da_id).first() if domain_info: return domain_info # the following information below is not needed in the domain information: - da_dict.pop("status") - da_dict.pop("current_websites") - da_dict.pop("investigator") - da_dict.pop("alternative_domains") - # use the requested_domain to create information for this domain - da_dict["domain"] = da_dict.pop("requested_domain") - other_contacts = da_dict.pop("other_contacts") + da_dict.pop("status", None) + da_dict.pop("current_websites", None) + da_dict.pop("investigator", None) + da_dict.pop("alternative_domains", None) + da_dict.pop("requested_domain", None) + da_dict.pop("approved_domain", None) + other_contacts = da_dict.pop("other_contacts", []) domain_info = cls(**da_dict) domain_info.domain_application = domain_application # Save so the object now have PK @@ -237,6 +237,8 @@ class DomainInformation(TimeStampedModel): # Process the remaining "many to many" stuff domain_info.other_contacts.add(*other_contacts) + if domain: + domain_info.domain = domain domain_info.save() return domain_info diff --git a/src/registrar/models/draft_domain.py b/src/registrar/models/draft_domain.py new file mode 100644 index 000000000..fc70a18f3 --- /dev/null +++ b/src/registrar/models/draft_domain.py @@ -0,0 +1,22 @@ +import logging + +from django.db import models + +from .utility.domain_helper import DomainHelper +from .utility.time_stamped_model import TimeStampedModel + +logger = logging.getLogger(__name__) + + +class DraftDomain(TimeStampedModel, DomainHelper): + """Store domain names which registrants have requested.""" + + def __str__(self) -> str: + return self.name + + name = models.CharField( + max_length=253, + blank=False, + default=None, # prevent saving without a value + help_text="Fully qualified domain name", + ) diff --git a/src/registrar/models/utility/domain_helper.py b/src/registrar/models/utility/domain_helper.py new file mode 100644 index 000000000..8f5737915 --- /dev/null +++ b/src/registrar/models/utility/domain_helper.py @@ -0,0 +1,64 @@ +import re + +from api.views import in_domains +from registrar.utility import errors + + +class DomainHelper: + """Utility functions and constants for domain names.""" + + # a domain name is alphanumeric or hyphen, up to 63 characters, doesn't + # begin or end with a hyphen, followed by a TLD of 2-6 alphabetic characters + DOMAIN_REGEX = re.compile(r"^(?!-)[A-Za-z0-9-]{1,63}(? bool: + """Return True if the string could be a domain name, otherwise False.""" + if not isinstance(domain, str): + return False + return bool(cls.DOMAIN_REGEX.match(domain)) + + @classmethod + def validate(cls, domain: str | None, blank_ok=False) -> str: + """Attempt to determine if a domain name could be requested.""" + if domain is None: + raise errors.BlankValueError() + if not isinstance(domain, str): + raise ValueError("Domain name must be a string") + domain = domain.lower().strip() + if domain == "": + if blank_ok: + return domain + else: + raise errors.BlankValueError() + if domain.endswith(".gov"): + domain = domain[:-4] + if "." in domain: + raise errors.ExtraDotsError() + if not DomainHelper.string_could_be_domain(domain + ".gov"): + raise ValueError() + if in_domains(domain): + raise errors.DomainUnavailableError() + return domain + + @classmethod + def sld(cls, domain: str): + """ + Get the second level domain. Example: `gsa.gov` -> `gsa`. + + If no TLD is present, returns the original string. + """ + return domain.split(".")[0] + + @classmethod + def tld(cls, domain: str): + """Get the top level domain. Example: `gsa.gov` -> `gov`.""" + parts = domain.rsplit(".") + return parts[-1] if len(parts) > 1 else "" diff --git a/src/registrar/models/website.py b/src/registrar/models/website.py index 65d86ddf1..d21564531 100644 --- a/src/registrar/models/website.py +++ b/src/registrar/models/website.py @@ -1,5 +1,3 @@ -from django.apps import apps -from django.core.exceptions import ValidationError from django.db import models from .utility.time_stamped_model import TimeStampedModel @@ -18,35 +16,5 @@ class Website(TimeStampedModel): help_text="", ) - @property - def sld(self): - """Get or set the second level domain string.""" - return self.website.split(".")[0] - - @sld.setter - def sld(self, value: str): - Domain = apps.get_model("registrar.Domain") - parts = self.website.split(".") - tld = parts[1] if len(parts) > 1 else "" - if Domain.string_could_be_domain(f"{value}.{tld}"): - self.website = f"{value}.{tld}" - else: - raise ValidationError("%s is not a valid second level domain" % value) - - @property - def tld(self): - """Get or set the top level domain string.""" - parts = self.website.split(".") - return parts[1] if len(parts) > 1 else "" - - @tld.setter - def tld(self, value: str): - Domain = apps.get_model("registrar.Domain") - sld = self.website.split(".")[0] - if Domain.string_could_be_domain(f"{sld}.{value}"): - self.website = f"{sld}.{value}" - else: - raise ValidationError("%s is not a valid top level domain" % value) - def __str__(self) -> str: return str(self.website) diff --git a/src/registrar/tests/test_emails.py b/src/registrar/tests/test_emails.py index 44cb565e2..4d7b22d01 100644 --- a/src/registrar/tests/test_emails.py +++ b/src/registrar/tests/test_emails.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock from django.contrib.auth import get_user_model from django.test import TestCase -from registrar.models import Contact, Domain, Website, DomainApplication +from registrar.models import Contact, DraftDomain, Website, DomainApplication import boto3_mocking # type: ignore @@ -28,7 +28,7 @@ class TestEmails(TestCase): email="testy@town.com", phone="(555) 555 5555", ) - domain, _ = Domain.objects.get_or_create(name="city.gov") + domain, _ = DraftDomain.objects.get_or_create(name="city.gov") alt, _ = Website.objects.get_or_create(website="city1.gov") current, _ = Website.objects.get_or_create(website="city.com") you, _ = Contact.objects.get_or_create( diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 2ebca68d7..97fe51a92 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -8,6 +8,7 @@ from registrar.models import ( User, Website, Domain, + DraftDomain, DomainInvitation, UserDomainRole, ) @@ -40,7 +41,7 @@ class TestDomainApplication(TestCase): contact = Contact.objects.create() com_website, _ = Website.objects.get_or_create(website="igorville.com") gov_website, _ = Website.objects.get_or_create(website="igorville.gov") - domain, _ = Domain.objects.get_or_create(name="igorville.gov") + domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") application = DomainApplication.objects.create( creator=user, investigator=user, @@ -100,7 +101,7 @@ class TestDomainApplication(TestCase): def test_status_fsm_submit_succeed(self): user, _ = User.objects.get_or_create() - site = Domain.objects.create(name="igorville.gov") + site = DraftDomain.objects.create(name="igorville.gov") application = DomainApplication.objects.create( creator=user, requested_domain=site ) @@ -113,7 +114,7 @@ class TestDomainApplication(TestCase): """Create an application and submit it and see if email was sent.""" user, _ = User.objects.get_or_create() contact = Contact.objects.create(email="test@test.gov") - domain, _ = Domain.objects.get_or_create(name="igorville.gov") + domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") application = DomainApplication.objects.create( creator=user, requested_domain=domain, @@ -135,62 +136,22 @@ class TestDomainApplication(TestCase): ) -class TestDomain(TestCase): - def test_empty_create_fails(self): - """Can't create a completely empty domain.""" - with self.assertRaisesRegex(IntegrityError, "name"): - Domain.objects.create() - - def test_minimal_create(self): - """Can create with just a name.""" - domain = Domain.objects.create(name="igorville.gov") - self.assertEqual(domain.is_active, False) - - @skip("cannot activate a domain without mock registry") - def test_get_status(self): - """Returns proper status based on `is_active`.""" - domain = Domain.objects.create(name="igorville.gov") - domain.save() - self.assertEqual(None, domain.status) - domain.activate() - domain.save() - self.assertIn("ok", domain.status) - - def test_fsm_activate_fail_unique(self): - """Can't activate domain if name is not unique.""" - d1, _ = Domain.objects.get_or_create(name="igorville.gov") - d2, _ = Domain.objects.get_or_create(name="igorville.gov") - d1.activate() - d1.save() - with self.assertRaises(ValueError): - d2.activate() - - def test_fsm_activate_fail_unapproved(self): - """Can't activate domain if application isn't approved.""" - d1, _ = Domain.objects.get_or_create(name="igorville.gov") - user, _ = User.objects.get_or_create() - application = DomainApplication.objects.create(creator=user) - d1.domain_application = application - d1.save() - with self.assertRaises(ValueError): - d1.activate() - - class TestPermissions(TestCase): """Test the User-Domain-Role connection.""" def test_approval_creates_role(self): - domain, _ = Domain.objects.get_or_create(name="igorville.gov") + draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create( - creator=user, requested_domain=domain + creator=user, requested_domain=draft_domain ) # skip using the submit method application.status = DomainApplication.SUBMITTED application.approve() # should be a role for this user + domain = Domain.objects.get(name="igorville.gov") self.assertTrue(UserDomainRole.objects.get(user=user, domain=domain)) @@ -199,16 +160,17 @@ class TestDomainInfo(TestCase): """Test creation of Domain Information when approved.""" def test_approval_creates_info(self): - domain, _ = Domain.objects.get_or_create(name="igorville.gov") + draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") user, _ = User.objects.get_or_create() application = DomainApplication.objects.create( - creator=user, requested_domain=domain + creator=user, requested_domain=draft_domain ) # skip using the submit method application.status = DomainApplication.SUBMITTED application.approve() # should be an information present for this domain + domain = Domain.objects.get(name="igorville.gov") self.assertTrue(DomainInformation.objects.get(domain=domain)) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py new file mode 100644 index 000000000..25b7be2d2 --- /dev/null +++ b/src/registrar/tests/test_models_domain.py @@ -0,0 +1,54 @@ +from django.test import TestCase +from django.db.utils import IntegrityError + +from registrar.models import ( + DomainApplication, + User, + Domain, +) +from unittest import skip + + +class TestDomain(TestCase): + def test_empty_create_fails(self): + """Can't create a completely empty domain.""" + with self.assertRaisesRegex(IntegrityError, "name"): + Domain.objects.create() + + def test_minimal_create(self): + """Can create with just a name.""" + Domain.objects.create(name="igorville.gov") + # this assertion will not work -- for now, the fact that the + # above command didn't error out is proof enough + # self.assertEquals(domain.state, Domain.State.DRAFTED) + + @skip("cannot activate a domain without mock registry") + def test_get_status(self): + """Returns proper status based on `state`.""" + domain = Domain.objects.create(name="igorville.gov") + domain.save() + self.assertEqual(None, domain.status) + domain.activate() + domain.save() + self.assertIn("ok", domain.status) + + @skip("cannot activate a domain without mock registry") + def test_fsm_activate_fail_unique(self): + """Can't activate domain if name is not unique.""" + d1, _ = Domain.objects.get_or_create(name="igorville.gov") + d2, _ = Domain.objects.get_or_create(name="igorville.gov") + d1.activate() + d1.save() + with self.assertRaises(ValueError): + d2.activate() + + @skip("cannot activate a domain without mock registry") + def test_fsm_activate_fail_unapproved(self): + """Can't activate domain if application isn't approved.""" + d1, _ = Domain.objects.get_or_create(name="igorville.gov") + user, _ = User.objects.get_or_create() + application = DomainApplication.objects.create(creator=user) + d1.domain_application = application + d1.save() + with self.assertRaises(ValueError): + d1.activate() diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 959183f34..daac3fca3 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -13,6 +13,7 @@ import boto3_mocking # type: ignore from registrar.models import ( DomainApplication, Domain, + DraftDomain, DomainInvitation, Contact, Website, @@ -75,7 +76,7 @@ class LoggedInTests(TestWithUser): def test_home_lists_domain_applications(self): response = self.client.get("/") self.assertNotContains(response, "igorville.gov") - site = Domain.objects.create(name="igorville.gov") + site = DraftDomain.objects.create(name="igorville.gov") application = DomainApplication.objects.create( creator=self.user, requested_domain=site ) @@ -1035,6 +1036,8 @@ class TestWithDomainPermissions(TestWithUser): def tearDown(self): try: + if hasattr(self.domain, "contacts"): + self.domain.contacts.all().delete() self.domain.delete() self.role.delete() except ValueError: # pass if already deleted @@ -1347,7 +1350,7 @@ class TestApplicationStatus(TestWithUser, WebTest): email="testy@town.com", phone="(555) 555 5555", ) - domain, _ = Domain.objects.get_or_create(name="citystatus.gov") + domain, _ = DraftDomain.objects.get_or_create(name="citystatus.gov") alt, _ = Website.objects.get_or_create(website="city1.gov") current, _ = Website.objects.get_or_create(website="city.com") you, _ = Contact.objects.get_or_create(