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]
|
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):
|
class ContactAdmin(ListHeaderAdmin):
|
||||||
"""Custom contact admin class to add search."""
|
"""Custom contact admin class to add search."""
|
||||||
|
|
||||||
|
@ -456,6 +333,81 @@ class DomainInformationAdmin(ListHeaderAdmin):
|
||||||
]
|
]
|
||||||
search_help_text = "Search by domain."
|
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):
|
class DomainApplicationAdminForm(forms.ModelForm):
|
||||||
"""Custom form to limit transitions to available transitions"""
|
"""Custom form to limit transitions to available transitions"""
|
||||||
|
@ -492,10 +444,6 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
"""Custom domain applications admin class."""
|
"""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
|
# Columns
|
||||||
list_display = [
|
list_display = [
|
||||||
"requested_domain",
|
"requested_domain",
|
||||||
|
@ -706,6 +654,154 @@ class TransitionDomainAdmin(ListHeaderAdmin):
|
||||||
search_help_text = "Search by user or domain name."
|
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.unregister(LogEntry) # Unregister the default registration
|
||||||
admin.site.register(LogEntry, CustomLogEntryAdmin)
|
admin.site.register(LogEntry, CustomLogEntryAdmin)
|
||||||
admin.site.register(models.User, MyUserAdmin)
|
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.DomainInvitation, DomainInvitationAdmin)
|
||||||
admin.site.register(models.DomainInformation, DomainInformationAdmin)
|
admin.site.register(models.DomainInformation, DomainInformationAdmin)
|
||||||
admin.site.register(models.Domain, DomainAdmin)
|
admin.site.register(models.Domain, DomainAdmin)
|
||||||
|
admin.site.register(models.DraftDomain, DraftDomainAdmin)
|
||||||
admin.site.register(models.Host, MyHostAdmin)
|
admin.site.register(models.Host, MyHostAdmin)
|
||||||
admin.site.register(models.Nameserver, MyHostAdmin)
|
admin.site.register(models.Nameserver, MyHostAdmin)
|
||||||
admin.site.register(models.Website, WebsiteAdmin)
|
admin.site.register(models.Website, WebsiteAdmin)
|
||||||
|
|
|
@ -143,12 +143,22 @@ class UserFixture:
|
||||||
"permissions": ["view_logentry"],
|
"permissions": ["view_logentry"],
|
||||||
},
|
},
|
||||||
{"app_label": "registrar", "model": "contact", "permissions": ["view_contact"]},
|
{"app_label": "registrar", "model": "contact", "permissions": ["view_contact"]},
|
||||||
|
{
|
||||||
|
"app_label": "registrar",
|
||||||
|
"model": "domaininformation",
|
||||||
|
"permissions": ["change_domaininformation"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"app_label": "registrar",
|
"app_label": "registrar",
|
||||||
"model": "domainapplication",
|
"model": "domainapplication",
|
||||||
"permissions": ["change_domainapplication"],
|
"permissions": ["change_domainapplication"],
|
||||||
},
|
},
|
||||||
{"app_label": "registrar", "model": "domain", "permissions": ["view_domain"]},
|
{"app_label": "registrar", "model": "domain", "permissions": ["view_domain"]},
|
||||||
|
{
|
||||||
|
"app_label": "registrar",
|
||||||
|
"model": "draftdomain",
|
||||||
|
"permissions": ["change_draftdomain"],
|
||||||
|
},
|
||||||
{"app_label": "registrar", "model": "user", "permissions": ["change_user"]},
|
{"app_label": "registrar", "model": "user", "permissions": ["change_user"]},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -332,24 +332,23 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
@Cache
|
@Cache
|
||||||
def statuses(self) -> list[str]:
|
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.
|
A domain's status indicates various properties. See Domain.Status.
|
||||||
"""
|
"""
|
||||||
# implementation note: the Status object from EPP stores the string in
|
try:
|
||||||
# a dataclass property `state`, not to be confused with the `state` field here
|
return self._get_property("statuses")
|
||||||
if "statuses" not in self._cache:
|
except KeyError:
|
||||||
self._fetch_cache()
|
logger.error("Can't retrieve status from domain info")
|
||||||
if "statuses" not in self._cache:
|
return []
|
||||||
raise Exception("Can't retreive status from domain info")
|
|
||||||
else:
|
|
||||||
return self._cache["statuses"]
|
|
||||||
|
|
||||||
@statuses.setter # type: ignore
|
@statuses.setter # type: ignore
|
||||||
def statuses(self, statuses: list[str]):
|
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 -
|
We will not implement this. Statuses are set by the registry
|
||||||
# some statuses cannot be set by the client at all
|
when we run delete and client hold, and these are the only statuses
|
||||||
|
we will be triggering.
|
||||||
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@Cache
|
@Cache
|
||||||
|
|
|
@ -45,7 +45,7 @@ class User(AbstractUser):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# this info is pulled from Login.gov
|
# this info is pulled from Login.gov
|
||||||
if self.first_name or self.last_name:
|
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:
|
elif self.email:
|
||||||
return self.email
|
return self.email
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<li>Domain meets our naming requirements</li>
|
<li>Domain meets our naming requirements</li>
|
||||||
</ul>
|
</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
|
of your request at any time. We'll email you with any questions or when we
|
||||||
complete our review.</p>
|
complete our review.</p>
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
<p>Before your domain can be used we'll need information about your domain
|
<p>Before your domain can be used we'll need information about your domain
|
||||||
name servers.</p>
|
name servers.</p>
|
||||||
|
|
||||||
<p><a class="usa-link" href="{% url "todo" %}">Get help with domain servers.</a></p>
|
|
||||||
|
|
||||||
{% include "includes/required_fields.html" %}
|
{% include "includes/required_fields.html" %}
|
||||||
|
|
||||||
<form class="usa-form usa-form--large" method="post" novalidate id="form-container">
|
<form class="usa-form usa-form--large" method="post" novalidate id="form-container">
|
||||||
|
|
|
@ -547,17 +547,29 @@ class MockEppLib(TestCase):
|
||||||
class fakedEppObject(object):
|
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.auth_info = auth_info
|
||||||
self.cr_date = cr_date
|
self.cr_date = cr_date
|
||||||
self.contacts = contacts
|
self.contacts = contacts
|
||||||
self.hosts = hosts
|
self.hosts = hosts
|
||||||
|
self.statuses = statuses
|
||||||
|
|
||||||
mockDataInfoDomain = fakedEppObject(
|
mockDataInfoDomain = fakedEppObject(
|
||||||
"fakepw",
|
"fakepw",
|
||||||
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
|
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
|
||||||
contacts=[common.DomainContact(contact="123", type="security")],
|
contacts=[common.DomainContact(contact="123", type="security")],
|
||||||
hosts=["fake.host.com"],
|
hosts=["fake.host.com"],
|
||||||
|
statuses=[
|
||||||
|
common.Status(state="serverTransferProhibited", description="", lang="en"),
|
||||||
|
common.Status(state="inactive", description="", lang="en"),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
infoDomainNoContact = fakedEppObject(
|
infoDomainNoContact = fakedEppObject(
|
||||||
"security",
|
"security",
|
||||||
|
|
|
@ -34,6 +34,8 @@ class TestDomainCache(MockEppLib):
|
||||||
# (see InfoDomainResult)
|
# (see InfoDomainResult)
|
||||||
self.assertEquals(domain._cache["auth_info"], self.mockDataInfoDomain.auth_info)
|
self.assertEquals(domain._cache["auth_info"], self.mockDataInfoDomain.auth_info)
|
||||||
self.assertEquals(domain._cache["cr_date"], self.mockDataInfoDomain.cr_date)
|
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())
|
self.assertFalse("avail" in domain._cache.keys())
|
||||||
|
|
||||||
# using a setter should clear the cache
|
# 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.InfoContact(id="123", auth_info=None), cleaned=True),
|
||||||
call(commands.InfoHost(name="fake.host.com"), 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):
|
def test_cache_used_when_avail(self):
|
||||||
|
@ -106,16 +109,14 @@ class TestDomainCache(MockEppLib):
|
||||||
domain._get_property("hosts")
|
domain._get_property("hosts")
|
||||||
self.assertEqual(domain._cache["hosts"], [expectedHostsDict])
|
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"""
|
"""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):
|
def test_approved_application_creates_domain_locally(self):
|
||||||
"""
|
"""
|
||||||
Scenario: Analyst approves a domain application
|
Scenario: Analyst approves a domain application
|
||||||
|
@ -123,8 +124,6 @@ class TestDomainCreation(TestCase):
|
||||||
Then a Domain exists in the database with the same `name`
|
Then a Domain exists in the database with the same `name`
|
||||||
But a domain object does not exist in the registry
|
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")
|
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
|
||||||
user, _ = User.objects.get_or_create()
|
user, _ = User.objects.get_or_create()
|
||||||
application = DomainApplication.objects.create(
|
application = DomainApplication.objects.create(
|
||||||
|
@ -137,19 +136,46 @@ class TestDomainCreation(TestCase):
|
||||||
# should hav information present for this domain
|
# should hav information present for this domain
|
||||||
domain = Domain.objects.get(name="igorville.gov")
|
domain = Domain.objects.get(name="igorville.gov")
|
||||||
self.assertTrue(domain)
|
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):
|
def test_accessing_domain_properties_creates_domain_in_registry(self):
|
||||||
"""
|
"""
|
||||||
Scenario: A registrant checks the status of a newly approved domain
|
Scenario: A registrant checks the status of a newly approved domain
|
||||||
Given that no domain object exists in the registry
|
Given that no domain object exists in the registry
|
||||||
When a property is accessed
|
When a property is accessed
|
||||||
Then Domain sends `commands.CreateDomain` to the registry
|
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
|
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")
|
@skip("assertion broken with mock addition")
|
||||||
def test_empty_domain_creation(self):
|
def test_empty_domain_creation(self):
|
||||||
|
@ -168,20 +194,71 @@ class TestDomainCreation(TestCase):
|
||||||
with self.assertRaisesRegex(IntegrityError, "name"):
|
with self.assertRaisesRegex(IntegrityError, "name"):
|
||||||
Domain.objects.create(name="igorville.gov")
|
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:
|
def tearDown(self) -> None:
|
||||||
DomainInformation.objects.all().delete()
|
DomainInformation.objects.all().delete()
|
||||||
DomainApplication.objects.all().delete()
|
DomainApplication.objects.all().delete()
|
||||||
Domain.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):
|
class TestRegistrantContacts(MockEppLib):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue