mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-16 01:27:03 +02:00
Merge branch 'main' into dk/927-domain-invitation-email
This commit is contained in:
commit
f5aacb8fb8
8 changed files with 361 additions and 168 deletions
|
@ -238,129 +238,6 @@ class MyHostAdmin(AuditedAdmin):
|
|||
inlines = [HostIPInline]
|
||||
|
||||
|
||||
class DomainAdmin(ListHeaderAdmin):
|
||||
"""Custom domain admin class to add extra buttons."""
|
||||
|
||||
# Columns
|
||||
list_display = [
|
||||
"name",
|
||||
"organization_type",
|
||||
"state",
|
||||
]
|
||||
|
||||
def organization_type(self, obj):
|
||||
return obj.domain_info.organization_type
|
||||
|
||||
organization_type.admin_order_field = ( # type: ignore
|
||||
"domain_info__organization_type"
|
||||
)
|
||||
|
||||
# Filters
|
||||
list_filter = ["domain_info__organization_type"]
|
||||
|
||||
search_fields = ["name"]
|
||||
search_help_text = "Search by domain name."
|
||||
change_form_template = "django/admin/domain_change_form.html"
|
||||
readonly_fields = ["state"]
|
||||
|
||||
def response_change(self, request, obj):
|
||||
# Create dictionary of action functions
|
||||
ACTION_FUNCTIONS = {
|
||||
"_place_client_hold": self.do_place_client_hold,
|
||||
"_remove_client_hold": self.do_remove_client_hold,
|
||||
"_edit_domain": self.do_edit_domain,
|
||||
"_delete_domain": self.do_delete_domain,
|
||||
"_get_status": self.do_get_status,
|
||||
}
|
||||
|
||||
# Check which action button was pressed and call the corresponding function
|
||||
for action, function in ACTION_FUNCTIONS.items():
|
||||
if action in request.POST:
|
||||
return function(request, obj)
|
||||
|
||||
# If no matching action button is found, return the super method
|
||||
return super().response_change(request, obj)
|
||||
|
||||
def do_delete_domain(self, request, obj):
|
||||
try:
|
||||
obj.deleted()
|
||||
obj.save()
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
("Domain %s Should now be deleted " ". Thanks!") % obj.name,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_get_status(self, request, obj):
|
||||
try:
|
||||
statuses = obj.statuses
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
("Domain statuses are %s" ". Thanks!") % statuses,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_place_client_hold(self, request, obj):
|
||||
try:
|
||||
obj.place_client_hold()
|
||||
obj.save()
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
(
|
||||
"%s is in client hold. This domain is no longer accessible on"
|
||||
" the public internet."
|
||||
)
|
||||
% obj.name,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_remove_client_hold(self, request, obj):
|
||||
try:
|
||||
obj.revert_client_hold()
|
||||
obj.save()
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
("%s is ready. This domain is accessible on the public internet.")
|
||||
% obj.name,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_edit_domain(self, request, obj):
|
||||
# We want to know, globally, when an edit action occurs
|
||||
request.session["analyst_action"] = "edit"
|
||||
# Restricts this action to this domain (pk) only
|
||||
request.session["analyst_action_location"] = obj.id
|
||||
return HttpResponseRedirect(reverse("domain", args=(obj.id,)))
|
||||
|
||||
def change_view(self, request, object_id):
|
||||
# If the analyst was recently editing a domain page,
|
||||
# delete any associated session values
|
||||
if "analyst_action" in request.session:
|
||||
del request.session["analyst_action"]
|
||||
del request.session["analyst_action_location"]
|
||||
return super().change_view(request, object_id)
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
# Fixes a bug wherein users which are only is_staff
|
||||
# can access 'change' when GET,
|
||||
# but cannot access this page when it is a request of type POST.
|
||||
if request.user.is_staff:
|
||||
return True
|
||||
return super().has_change_permission(request, obj)
|
||||
|
||||
|
||||
class ContactAdmin(ListHeaderAdmin):
|
||||
"""Custom contact admin class to add search."""
|
||||
|
||||
|
@ -456,6 +333,81 @@ class DomainInformationAdmin(ListHeaderAdmin):
|
|||
]
|
||||
search_help_text = "Search by domain."
|
||||
|
||||
fieldsets = [
|
||||
(None, {"fields": ["creator", "domain_application"]}),
|
||||
(
|
||||
"Type of organization",
|
||||
{
|
||||
"fields": [
|
||||
"organization_type",
|
||||
"federally_recognized_tribe",
|
||||
"state_recognized_tribe",
|
||||
"tribe_name",
|
||||
"federal_agency",
|
||||
"federal_type",
|
||||
"is_election_board",
|
||||
"about_your_organization",
|
||||
]
|
||||
},
|
||||
),
|
||||
(
|
||||
"Organization name and mailing address",
|
||||
{
|
||||
"fields": [
|
||||
"organization_name",
|
||||
"address_line1",
|
||||
"address_line2",
|
||||
"city",
|
||||
"state_territory",
|
||||
"zipcode",
|
||||
"urbanization",
|
||||
]
|
||||
},
|
||||
),
|
||||
("Authorizing official", {"fields": ["authorizing_official"]}),
|
||||
(".gov domain", {"fields": ["domain"]}),
|
||||
("Your contact information", {"fields": ["submitter"]}),
|
||||
("Other employees from your organization?", {"fields": ["other_contacts"]}),
|
||||
(
|
||||
"No other employees from your organization?",
|
||||
{"fields": ["no_other_contacts_rationale"]},
|
||||
),
|
||||
("Anything else we should know?", {"fields": ["anything_else"]}),
|
||||
(
|
||||
"Requirements for operating .gov domains",
|
||||
{"fields": ["is_policy_acknowledged"]},
|
||||
),
|
||||
]
|
||||
|
||||
# Read only that we'll leverage for CISA Analysts
|
||||
analyst_readonly_fields = [
|
||||
"creator",
|
||||
"type_of_work",
|
||||
"more_organization_information",
|
||||
"address_line1",
|
||||
"address_line2",
|
||||
"zipcode",
|
||||
"domain",
|
||||
"submitter",
|
||||
"no_other_contacts_rationale",
|
||||
"anything_else",
|
||||
"is_policy_acknowledged",
|
||||
]
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
"""Set the read-only state on form elements.
|
||||
We have 1 conditions that determine which fields are read-only:
|
||||
admin user permissions.
|
||||
"""
|
||||
|
||||
readonly_fields = list(self.readonly_fields)
|
||||
|
||||
if request.user.is_superuser:
|
||||
return readonly_fields
|
||||
else:
|
||||
readonly_fields.extend([field for field in self.analyst_readonly_fields])
|
||||
return readonly_fields
|
||||
|
||||
|
||||
class DomainApplicationAdminForm(forms.ModelForm):
|
||||
"""Custom form to limit transitions to available transitions"""
|
||||
|
@ -492,10 +444,6 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
|
||||
"""Custom domain applications admin class."""
|
||||
|
||||
# Set multi-selects 'read-only' (hide selects and show data)
|
||||
# based on user perms and application creator's status
|
||||
# form = DomainApplicationForm
|
||||
|
||||
# Columns
|
||||
list_display = [
|
||||
"requested_domain",
|
||||
|
@ -706,6 +654,154 @@ class TransitionDomainAdmin(ListHeaderAdmin):
|
|||
search_help_text = "Search by user or domain name."
|
||||
|
||||
|
||||
class DomainInformationInline(admin.StackedInline):
|
||||
"""Edit a domain information on the domain page.
|
||||
We had issues inheriting from both StackedInline
|
||||
and the source DomainInformationAdmin since these
|
||||
classes conflict, so we'll just pull what we need
|
||||
from DomainInformationAdmin"""
|
||||
|
||||
model = models.DomainInformation
|
||||
|
||||
fieldsets = DomainInformationAdmin.fieldsets
|
||||
analyst_readonly_fields = DomainInformationAdmin.analyst_readonly_fields
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
return DomainInformationAdmin.get_readonly_fields(self, request, obj=None)
|
||||
|
||||
|
||||
class DomainAdmin(ListHeaderAdmin):
|
||||
"""Custom domain admin class to add extra buttons."""
|
||||
|
||||
inlines = [DomainInformationInline]
|
||||
|
||||
# Columns
|
||||
list_display = [
|
||||
"name",
|
||||
"organization_type",
|
||||
"state",
|
||||
]
|
||||
|
||||
def organization_type(self, obj):
|
||||
return obj.domain_info.organization_type
|
||||
|
||||
organization_type.admin_order_field = ( # type: ignore
|
||||
"domain_info__organization_type"
|
||||
)
|
||||
|
||||
# Filters
|
||||
list_filter = ["domain_info__organization_type", "state"]
|
||||
|
||||
search_fields = ["name"]
|
||||
search_help_text = "Search by domain name."
|
||||
change_form_template = "django/admin/domain_change_form.html"
|
||||
readonly_fields = ["state"]
|
||||
|
||||
def response_change(self, request, obj):
|
||||
# Create dictionary of action functions
|
||||
ACTION_FUNCTIONS = {
|
||||
"_place_client_hold": self.do_place_client_hold,
|
||||
"_remove_client_hold": self.do_remove_client_hold,
|
||||
"_edit_domain": self.do_edit_domain,
|
||||
"_delete_domain": self.do_delete_domain,
|
||||
"_get_status": self.do_get_status,
|
||||
}
|
||||
|
||||
# Check which action button was pressed and call the corresponding function
|
||||
for action, function in ACTION_FUNCTIONS.items():
|
||||
if action in request.POST:
|
||||
return function(request, obj)
|
||||
|
||||
# If no matching action button is found, return the super method
|
||||
return super().response_change(request, obj)
|
||||
|
||||
def do_delete_domain(self, request, obj):
|
||||
try:
|
||||
obj.deleted()
|
||||
obj.save()
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
("Domain %s Should now be deleted " ". Thanks!") % obj.name,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_get_status(self, request, obj):
|
||||
try:
|
||||
statuses = obj.statuses
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
("Domain statuses are %s" ". Thanks!") % statuses,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_place_client_hold(self, request, obj):
|
||||
try:
|
||||
obj.place_client_hold()
|
||||
obj.save()
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
(
|
||||
"%s is in client hold. This domain is no longer accessible on"
|
||||
" the public internet."
|
||||
)
|
||||
% obj.name,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_remove_client_hold(self, request, obj):
|
||||
try:
|
||||
obj.revert_client_hold()
|
||||
obj.save()
|
||||
except Exception as err:
|
||||
self.message_user(request, err, messages.ERROR)
|
||||
else:
|
||||
self.message_user(
|
||||
request,
|
||||
("%s is ready. This domain is accessible on the public internet.")
|
||||
% obj.name,
|
||||
)
|
||||
return HttpResponseRedirect(".")
|
||||
|
||||
def do_edit_domain(self, request, obj):
|
||||
# We want to know, globally, when an edit action occurs
|
||||
request.session["analyst_action"] = "edit"
|
||||
# Restricts this action to this domain (pk) only
|
||||
request.session["analyst_action_location"] = obj.id
|
||||
return HttpResponseRedirect(reverse("domain", args=(obj.id,)))
|
||||
|
||||
def change_view(self, request, object_id):
|
||||
# If the analyst was recently editing a domain page,
|
||||
# delete any associated session values
|
||||
if "analyst_action" in request.session:
|
||||
del request.session["analyst_action"]
|
||||
del request.session["analyst_action_location"]
|
||||
return super().change_view(request, object_id)
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
# Fixes a bug wherein users which are only is_staff
|
||||
# can access 'change' when GET,
|
||||
# but cannot access this page when it is a request of type POST.
|
||||
if request.user.is_staff:
|
||||
return True
|
||||
return super().has_change_permission(request, obj)
|
||||
|
||||
|
||||
class DraftDomainAdmin(ListHeaderAdmin):
|
||||
"""Custom draft domain admin class."""
|
||||
|
||||
search_fields = ["name"]
|
||||
search_help_text = "Search by draft domain name."
|
||||
|
||||
|
||||
admin.site.unregister(LogEntry) # Unregister the default registration
|
||||
admin.site.register(LogEntry, CustomLogEntryAdmin)
|
||||
admin.site.register(models.User, MyUserAdmin)
|
||||
|
@ -714,6 +810,7 @@ admin.site.register(models.Contact, ContactAdmin)
|
|||
admin.site.register(models.DomainInvitation, DomainInvitationAdmin)
|
||||
admin.site.register(models.DomainInformation, DomainInformationAdmin)
|
||||
admin.site.register(models.Domain, DomainAdmin)
|
||||
admin.site.register(models.DraftDomain, DraftDomainAdmin)
|
||||
admin.site.register(models.Host, MyHostAdmin)
|
||||
admin.site.register(models.Nameserver, MyHostAdmin)
|
||||
admin.site.register(models.Website, WebsiteAdmin)
|
||||
|
|
|
@ -143,12 +143,22 @@ class UserFixture:
|
|||
"permissions": ["view_logentry"],
|
||||
},
|
||||
{"app_label": "registrar", "model": "contact", "permissions": ["view_contact"]},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "domaininformation",
|
||||
"permissions": ["change_domaininformation"],
|
||||
},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "domainapplication",
|
||||
"permissions": ["change_domainapplication"],
|
||||
},
|
||||
{"app_label": "registrar", "model": "domain", "permissions": ["view_domain"]},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "draftdomain",
|
||||
"permissions": ["change_draftdomain"],
|
||||
},
|
||||
{"app_label": "registrar", "model": "user", "permissions": ["change_user"]},
|
||||
]
|
||||
|
||||
|
|
|
@ -332,24 +332,23 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
@Cache
|
||||
def statuses(self) -> list[str]:
|
||||
"""
|
||||
Get or set the domain `status` elements from the registry.
|
||||
Get the domain `status` elements from the registry.
|
||||
|
||||
A domain's status indicates various properties. See Domain.Status.
|
||||
"""
|
||||
# implementation note: the Status object from EPP stores the string in
|
||||
# a dataclass property `state`, not to be confused with the `state` field here
|
||||
if "statuses" not in self._cache:
|
||||
self._fetch_cache()
|
||||
if "statuses" not in self._cache:
|
||||
raise Exception("Can't retreive status from domain info")
|
||||
else:
|
||||
return self._cache["statuses"]
|
||||
try:
|
||||
return self._get_property("statuses")
|
||||
except KeyError:
|
||||
logger.error("Can't retrieve status from domain info")
|
||||
return []
|
||||
|
||||
@statuses.setter # type: ignore
|
||||
def statuses(self, statuses: list[str]):
|
||||
# TODO: there are a long list of rules in the RFC about which statuses
|
||||
# can be combined; check that here and raise errors for invalid combinations -
|
||||
# some statuses cannot be set by the client at all
|
||||
"""
|
||||
We will not implement this. Statuses are set by the registry
|
||||
when we run delete and client hold, and these are the only statuses
|
||||
we will be triggering.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@Cache
|
||||
|
|
|
@ -45,7 +45,7 @@ class User(AbstractUser):
|
|||
def __str__(self):
|
||||
# this info is pulled from Login.gov
|
||||
if self.first_name or self.last_name:
|
||||
return f"{self.first_name or ''} {self.last_name or ''}"
|
||||
return f"{self.first_name or ''} {self.last_name or ''} {self.email or ''}"
|
||||
elif self.email:
|
||||
return self.email
|
||||
else:
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<li>Domain meets our naming requirements</li>
|
||||
</ul>
|
||||
|
||||
<p> You can <a href="{% url 'todo' %}"><del>check the status</del></a>
|
||||
<p> You can <a href="{% url 'home' %}">check the status</a>
|
||||
of your request at any time. We'll email you with any questions or when we
|
||||
complete our review.</p>
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
<p>Before your domain can be used we'll need information about your domain
|
||||
name servers.</p>
|
||||
|
||||
<p><a class="usa-link" href="{% url "todo" %}">Get help with domain servers.</a></p>
|
||||
|
||||
{% include "includes/required_fields.html" %}
|
||||
|
||||
<form class="usa-form usa-form--large" method="post" novalidate id="form-container">
|
||||
|
|
|
@ -547,17 +547,29 @@ class MockEppLib(TestCase):
|
|||
class fakedEppObject(object):
|
||||
""""""
|
||||
|
||||
def __init__(self, auth_info=..., cr_date=..., contacts=..., hosts=...):
|
||||
def __init__(
|
||||
self,
|
||||
auth_info=...,
|
||||
cr_date=...,
|
||||
contacts=...,
|
||||
hosts=...,
|
||||
statuses=...,
|
||||
):
|
||||
self.auth_info = auth_info
|
||||
self.cr_date = cr_date
|
||||
self.contacts = contacts
|
||||
self.hosts = hosts
|
||||
self.statuses = statuses
|
||||
|
||||
mockDataInfoDomain = fakedEppObject(
|
||||
"fakepw",
|
||||
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
|
||||
contacts=[common.DomainContact(contact="123", type="security")],
|
||||
hosts=["fake.host.com"],
|
||||
statuses=[
|
||||
common.Status(state="serverTransferProhibited", description="", lang="en"),
|
||||
common.Status(state="inactive", description="", lang="en"),
|
||||
],
|
||||
)
|
||||
infoDomainNoContact = fakedEppObject(
|
||||
"security",
|
||||
|
|
|
@ -34,6 +34,8 @@ class TestDomainCache(MockEppLib):
|
|||
# (see InfoDomainResult)
|
||||
self.assertEquals(domain._cache["auth_info"], self.mockDataInfoDomain.auth_info)
|
||||
self.assertEquals(domain._cache["cr_date"], self.mockDataInfoDomain.cr_date)
|
||||
status_list = [status.state for status in self.mockDataInfoDomain.statuses]
|
||||
self.assertEquals(domain._cache["statuses"], status_list)
|
||||
self.assertFalse("avail" in domain._cache.keys())
|
||||
|
||||
# using a setter should clear the cache
|
||||
|
@ -49,7 +51,8 @@ class TestDomainCache(MockEppLib):
|
|||
),
|
||||
call(commands.InfoContact(id="123", auth_info=None), cleaned=True),
|
||||
call(commands.InfoHost(name="fake.host.com"), cleaned=True),
|
||||
]
|
||||
],
|
||||
any_order=False, # Ensure calls are in the specified order
|
||||
)
|
||||
|
||||
def test_cache_used_when_avail(self):
|
||||
|
@ -106,16 +109,14 @@ class TestDomainCache(MockEppLib):
|
|||
domain._get_property("hosts")
|
||||
self.assertEqual(domain._cache["hosts"], [expectedHostsDict])
|
||||
|
||||
def tearDown(self) -> None:
|
||||
Domain.objects.all().delete()
|
||||
super().tearDown()
|
||||
|
||||
class TestDomainCreation(TestCase):
|
||||
|
||||
class TestDomainCreation(MockEppLib):
|
||||
"""Rule: An approved domain application must result in a domain"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Background:
|
||||
Given that a valid domain application exists
|
||||
"""
|
||||
|
||||
def test_approved_application_creates_domain_locally(self):
|
||||
"""
|
||||
Scenario: Analyst approves a domain application
|
||||
|
@ -123,8 +124,6 @@ class TestDomainCreation(TestCase):
|
|||
Then a Domain exists in the database with the same `name`
|
||||
But a domain object does not exist in the registry
|
||||
"""
|
||||
patcher = patch("registrar.models.domain.Domain._get_or_create_domain")
|
||||
mocked_domain_creation = patcher.start()
|
||||
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
||||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(
|
||||
|
@ -137,19 +136,46 @@ class TestDomainCreation(TestCase):
|
|||
# should hav information present for this domain
|
||||
domain = Domain.objects.get(name="igorville.gov")
|
||||
self.assertTrue(domain)
|
||||
mocked_domain_creation.assert_not_called()
|
||||
self.mockedSendFunction.assert_not_called()
|
||||
|
||||
@skip("not implemented yet")
|
||||
def test_accessing_domain_properties_creates_domain_in_registry(self):
|
||||
"""
|
||||
Scenario: A registrant checks the status of a newly approved domain
|
||||
Given that no domain object exists in the registry
|
||||
When a property is accessed
|
||||
Then Domain sends `commands.CreateDomain` to the registry
|
||||
And `domain.state` is set to `CREATED`
|
||||
And `domain.state` is set to `UNKNOWN`
|
||||
And `domain.is_active()` returns False
|
||||
"""
|
||||
raise
|
||||
domain = Domain.objects.create(name="beef-tongue.gov")
|
||||
# trigger getter
|
||||
_ = domain.statuses
|
||||
|
||||
# contacts = PublicContact.objects.filter(domain=domain,
|
||||
# type=PublicContact.ContactTypeChoices.REGISTRANT).get()
|
||||
|
||||
# Called in _fetch_cache
|
||||
self.mockedSendFunction.assert_has_calls(
|
||||
[
|
||||
# TODO: due to complexity of the test, will return to it in
|
||||
# a future ticket
|
||||
# call(
|
||||
# commands.CreateDomain(name="beef-tongue.gov",
|
||||
# id=contact.registry_id, auth_info=None),
|
||||
# cleaned=True,
|
||||
# ),
|
||||
call(
|
||||
commands.InfoDomain(name="beef-tongue.gov", auth_info=None),
|
||||
cleaned=True,
|
||||
),
|
||||
call(commands.InfoContact(id="123", auth_info=None), cleaned=True),
|
||||
call(commands.InfoHost(name="fake.host.com"), cleaned=True),
|
||||
],
|
||||
any_order=False, # Ensure calls are in the specified order
|
||||
)
|
||||
|
||||
self.assertEqual(domain.state, Domain.State.UNKNOWN)
|
||||
self.assertEqual(domain.is_active(), False)
|
||||
|
||||
@skip("assertion broken with mock addition")
|
||||
def test_empty_domain_creation(self):
|
||||
|
@ -168,20 +194,71 @@ class TestDomainCreation(TestCase):
|
|||
with self.assertRaisesRegex(IntegrityError, "name"):
|
||||
Domain.objects.create(name="igorville.gov")
|
||||
|
||||
@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)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
DomainInformation.objects.all().delete()
|
||||
DomainApplication.objects.all().delete()
|
||||
Domain.objects.all().delete()
|
||||
super().tearDown()
|
||||
|
||||
|
||||
class TestDomainStatuses(MockEppLib):
|
||||
"""Domain statuses are set by the registry"""
|
||||
|
||||
def test_get_status(self):
|
||||
"""Domain 'statuses' getter returns statuses by calling epp"""
|
||||
domain, _ = Domain.objects.get_or_create(name="chicken-liver.gov")
|
||||
# trigger getter
|
||||
_ = domain.statuses
|
||||
status_list = [status.state for status in self.mockDataInfoDomain.statuses]
|
||||
self.assertEquals(domain._cache["statuses"], status_list)
|
||||
|
||||
# Called in _fetch_cache
|
||||
self.mockedSendFunction.assert_has_calls(
|
||||
[
|
||||
call(
|
||||
commands.InfoDomain(name="chicken-liver.gov", auth_info=None),
|
||||
cleaned=True,
|
||||
),
|
||||
call(commands.InfoContact(id="123", auth_info=None), cleaned=True),
|
||||
call(commands.InfoHost(name="fake.host.com"), cleaned=True),
|
||||
],
|
||||
any_order=False, # Ensure calls are in the specified order
|
||||
)
|
||||
|
||||
def test_get_status_returns_empty_list_when_value_error(self):
|
||||
"""Domain 'statuses' getter returns an empty list
|
||||
when value error"""
|
||||
domain, _ = Domain.objects.get_or_create(name="pig-knuckles.gov")
|
||||
|
||||
def side_effect(self):
|
||||
raise KeyError
|
||||
|
||||
patcher = patch("registrar.models.domain.Domain._get_property")
|
||||
mocked_get = patcher.start()
|
||||
mocked_get.side_effect = side_effect
|
||||
|
||||
# trigger getter
|
||||
_ = domain.statuses
|
||||
|
||||
with self.assertRaises(KeyError):
|
||||
_ = domain._cache["statuses"]
|
||||
self.assertEquals(_, [])
|
||||
|
||||
patcher.stop()
|
||||
|
||||
@skip("not implemented yet")
|
||||
def test_place_client_hold_sets_status(self):
|
||||
"""Domain 'place_client_hold' method causes the registry to change statuses"""
|
||||
raise
|
||||
|
||||
@skip("not implemented yet")
|
||||
def test_revert_client_hold_sets_status(self):
|
||||
"""Domain 'revert_client_hold' method causes the registry to change statuses"""
|
||||
raise
|
||||
|
||||
def tearDown(self) -> None:
|
||||
Domain.objects.all().delete()
|
||||
super().tearDown()
|
||||
|
||||
|
||||
class TestRegistrantContacts(MockEppLib):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue