mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-12 05:58:18 +02:00
Merge branch 'main' into hotgov/2355-rejection-reason-emails
This commit is contained in:
commit
62156ac176
7 changed files with 326 additions and 73 deletions
66
src/api/tests/test_rdap.py
Normal file
66
src/api/tests/test_rdap.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
"""Test the domain rdap lookup API."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.test import RequestFactory
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from ..views import rdap
|
||||||
|
|
||||||
|
API_BASE_PATH = "/api/v1/rdap/?domain="
|
||||||
|
|
||||||
|
|
||||||
|
class RdapViewTest(TestCase):
|
||||||
|
"""Test that the RDAP view function works as expected"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.user = get_user_model().objects.create(username="username")
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
|
def test_rdap_get_no_tld(self):
|
||||||
|
"""RDAP API successfully fetches RDAP for domain without a TLD"""
|
||||||
|
request = self.factory.get(API_BASE_PATH + "whitehouse")
|
||||||
|
request.user = self.user
|
||||||
|
response = rdap(request, domain="whitehouse")
|
||||||
|
# contains the right text
|
||||||
|
self.assertContains(response, "rdap")
|
||||||
|
# can be parsed into JSON with appropriate keys
|
||||||
|
response_object = json.loads(response.content)
|
||||||
|
self.assertIn("rdapConformance", response_object)
|
||||||
|
|
||||||
|
def test_rdap_invalid_domain(self):
|
||||||
|
"""RDAP API accepts invalid domain queries and returns JSON response
|
||||||
|
with appropriate error codes"""
|
||||||
|
request = self.factory.get(API_BASE_PATH + "whitehouse.com")
|
||||||
|
request.user = self.user
|
||||||
|
response = rdap(request, domain="whitehouse.com")
|
||||||
|
|
||||||
|
self.assertContains(response, "errorCode")
|
||||||
|
response_object = json.loads(response.content)
|
||||||
|
self.assertIn("errorCode", response_object)
|
||||||
|
|
||||||
|
|
||||||
|
class RdapAPITest(TestCase):
|
||||||
|
"""Test that the API can be called as expected."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
username = "test_user"
|
||||||
|
first_name = "First"
|
||||||
|
last_name = "Last"
|
||||||
|
email = "info@example.com"
|
||||||
|
title = "title"
|
||||||
|
phone = "8080102431"
|
||||||
|
self.user = get_user_model().objects.create(
|
||||||
|
username=username, title=title, first_name=first_name, last_name=last_name, email=email, phone=phone
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rdap_get(self):
|
||||||
|
"""Can call RDAP API"""
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
response = self.client.get(API_BASE_PATH + "whitehouse.gov")
|
||||||
|
self.assertContains(response, "rdap")
|
||||||
|
response_object = json.loads(response.content)
|
||||||
|
self.assertIn("rdapConformance", response_object)
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from registrar.templatetags.url_helpers import public_site_url
|
from registrar.templatetags.url_helpers import public_site_url
|
||||||
|
@ -18,7 +18,7 @@ from cachetools.func import ttl_cache
|
||||||
from registrar.utility.s3_bucket import S3ClientError, S3ClientHelper
|
from registrar.utility.s3_bucket import S3ClientError, S3ClientHelper
|
||||||
|
|
||||||
|
|
||||||
DOMAIN_FILE_URL = "https://raw.githubusercontent.com/cisagov/dotgov-data/main/current-full.csv"
|
RDAP_URL = "https://rdap.cloudflareregistry.com/rdap/domain/{domain}"
|
||||||
|
|
||||||
|
|
||||||
DOMAIN_API_MESSAGES = {
|
DOMAIN_API_MESSAGES = {
|
||||||
|
@ -41,30 +41,6 @@ DOMAIN_API_MESSAGES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# this file doesn't change that often, nor is it that big, so cache the result
|
|
||||||
# in memory for ten minutes
|
|
||||||
@ttl_cache(ttl=600)
|
|
||||||
def _domains():
|
|
||||||
"""Return a list of the current .gov domains.
|
|
||||||
|
|
||||||
Fetch a file from DOMAIN_FILE_URL, parse the CSV for the domain,
|
|
||||||
lowercase everything and return the list.
|
|
||||||
"""
|
|
||||||
DraftDomain = apps.get_model("registrar.DraftDomain")
|
|
||||||
# 5 second timeout
|
|
||||||
file_contents = requests.get(DOMAIN_FILE_URL, timeout=5).text
|
|
||||||
domains = set()
|
|
||||||
# skip the first line
|
|
||||||
for line in file_contents.splitlines()[1:]:
|
|
||||||
# get the domain before the first comma
|
|
||||||
domain = line.split(",", 1)[0]
|
|
||||||
# sanity-check the string we got from the file here
|
|
||||||
if DraftDomain.string_could_be_domain(domain):
|
|
||||||
# lowercase everything when we put it in domains
|
|
||||||
domains.add(domain.lower())
|
|
||||||
return domains
|
|
||||||
|
|
||||||
|
|
||||||
def check_domain_available(domain):
|
def check_domain_available(domain):
|
||||||
"""Return true if the given domain is available.
|
"""Return true if the given domain is available.
|
||||||
|
|
||||||
|
@ -99,6 +75,22 @@ def available(request, domain=""):
|
||||||
return json_response
|
return json_response
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(["GET"])
|
||||||
|
@login_not_required
|
||||||
|
# Since we cache domain RDAP data, cache time may need to be re-evaluated this if we encounter any memory issues
|
||||||
|
@ttl_cache(ttl=600)
|
||||||
|
def rdap(request, domain=""):
|
||||||
|
"""Returns JSON dictionary of a domain's RDAP data from Cloudflare API"""
|
||||||
|
domain = request.GET.get("domain", "")
|
||||||
|
|
||||||
|
# If inputted domain doesn't have a TLD, append .gov to it
|
||||||
|
if "." not in domain:
|
||||||
|
domain = f"{domain}.gov"
|
||||||
|
|
||||||
|
rdap_data = requests.get(RDAP_URL.format(domain=domain), timeout=5).json()
|
||||||
|
return JsonResponse(rdap_data)
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
@login_not_required
|
@login_not_required
|
||||||
def get_current_full(request, file_name="current-full.csv"):
|
def get_current_full(request, file_name="current-full.csv"):
|
||||||
|
|
|
@ -9,6 +9,7 @@ from django.conf import settings
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django_fsm import get_available_FIELD_transitions, FSMField
|
from django_fsm import get_available_FIELD_transitions, FSMField
|
||||||
from registrar.models.domain_information import DomainInformation
|
from registrar.models.domain_information import DomainInformation
|
||||||
|
from registrar.models.domain_invitation import DomainInvitation
|
||||||
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
from registrar.models.user_portfolio_permission import UserPortfolioPermission
|
||||||
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
||||||
from waffle.decorators import flag_is_active
|
from waffle.decorators import flag_is_active
|
||||||
|
@ -1564,7 +1565,7 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
modified_fieldsets = []
|
modified_fieldsets = []
|
||||||
for name, data in fieldsets:
|
for name, data in fieldsets:
|
||||||
fields = data.get("fields", [])
|
fields = data.get("fields", [])
|
||||||
fields = tuple(field for field in fields if field not in DomainInformationAdmin.superuser_only_fields)
|
fields = [field for field in fields if field not in DomainInformationAdmin.superuser_only_fields]
|
||||||
modified_fieldsets.append((name, {**data, "fields": fields}))
|
modified_fieldsets.append((name, {**data, "fields": fields}))
|
||||||
return modified_fieldsets
|
return modified_fieldsets
|
||||||
return fieldsets
|
return fieldsets
|
||||||
|
@ -2282,10 +2283,58 @@ class DomainInformationInline(admin.StackedInline):
|
||||||
template = "django/admin/includes/domain_info_inline_stacked.html"
|
template = "django/admin/includes/domain_info_inline_stacked.html"
|
||||||
model = models.DomainInformation
|
model = models.DomainInformation
|
||||||
|
|
||||||
fieldsets = DomainInformationAdmin.fieldsets
|
fieldsets = copy.deepcopy(list(DomainInformationAdmin.fieldsets))
|
||||||
readonly_fields = DomainInformationAdmin.readonly_fields
|
analyst_readonly_fields = copy.deepcopy(DomainInformationAdmin.analyst_readonly_fields)
|
||||||
analyst_readonly_fields = DomainInformationAdmin.analyst_readonly_fields
|
autocomplete_fields = copy.deepcopy(DomainInformationAdmin.autocomplete_fields)
|
||||||
autocomplete_fields = DomainInformationAdmin.autocomplete_fields
|
|
||||||
|
def get_domain_managers(self, obj):
|
||||||
|
user_domain_roles = UserDomainRole.objects.filter(domain=obj.domain)
|
||||||
|
user_ids = user_domain_roles.values_list("user_id", flat=True)
|
||||||
|
domain_managers = User.objects.filter(id__in=user_ids)
|
||||||
|
return domain_managers
|
||||||
|
|
||||||
|
def get_domain_invitations(self, obj):
|
||||||
|
domain_invitations = DomainInvitation.objects.filter(
|
||||||
|
domain=obj.domain, status=DomainInvitation.DomainInvitationStatus.INVITED
|
||||||
|
)
|
||||||
|
return domain_invitations
|
||||||
|
|
||||||
|
def domain_managers(self, obj):
|
||||||
|
"""Get domain managers for the domain, unpack and return an HTML block."""
|
||||||
|
domain_managers = self.get_domain_managers(obj)
|
||||||
|
if not domain_managers:
|
||||||
|
return "No domain managers found."
|
||||||
|
|
||||||
|
domain_manager_details = "<table><thead><tr><th>UID</th><th>Name</th><th>Email</th></tr></thead><tbody>"
|
||||||
|
for domain_manager in domain_managers:
|
||||||
|
full_name = domain_manager.get_formatted_name()
|
||||||
|
change_url = reverse("admin:registrar_user_change", args=[domain_manager.pk])
|
||||||
|
domain_manager_details += "<tr>"
|
||||||
|
domain_manager_details += f'<td><a href="{change_url}">{escape(domain_manager.username)}</a>'
|
||||||
|
domain_manager_details += f"<td>{escape(full_name)}</td>"
|
||||||
|
domain_manager_details += f"<td>{escape(domain_manager.email)}</td>"
|
||||||
|
domain_manager_details += "</tr>"
|
||||||
|
domain_manager_details += "</tbody></table>"
|
||||||
|
return format_html(domain_manager_details)
|
||||||
|
|
||||||
|
domain_managers.short_description = "Domain managers" # type: ignore
|
||||||
|
|
||||||
|
def invited_domain_managers(self, obj):
|
||||||
|
"""Get emails which have been invited to the domain, unpack and return an HTML block."""
|
||||||
|
domain_invitations = self.get_domain_invitations(obj)
|
||||||
|
if not domain_invitations:
|
||||||
|
return "No invited domain managers found."
|
||||||
|
|
||||||
|
domain_invitation_details = "<table><thead><tr><th>Email</th><th>Status</th>" + "</tr></thead><tbody>"
|
||||||
|
for domain_invitation in domain_invitations:
|
||||||
|
domain_invitation_details += "<tr>"
|
||||||
|
domain_invitation_details += f"<td>{escape(domain_invitation.email)}</td>"
|
||||||
|
domain_invitation_details += f"<td>{escape(domain_invitation.status.capitalize())}</td>"
|
||||||
|
domain_invitation_details += "</tr>"
|
||||||
|
domain_invitation_details += "</tbody></table>"
|
||||||
|
return format_html(domain_invitation_details)
|
||||||
|
|
||||||
|
invited_domain_managers.short_description = "Invited domain managers" # type: ignore
|
||||||
|
|
||||||
def has_change_permission(self, request, obj=None):
|
def has_change_permission(self, request, obj=None):
|
||||||
"""Custom has_change_permission override so that we can specify that
|
"""Custom has_change_permission override so that we can specify that
|
||||||
|
@ -2325,7 +2374,9 @@ class DomainInformationInline(admin.StackedInline):
|
||||||
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
return DomainInformationAdmin.get_readonly_fields(self, request, obj=None)
|
readonly_fields = copy.deepcopy(DomainInformationAdmin.get_readonly_fields(self, request, obj=None))
|
||||||
|
readonly_fields.extend(["domain_managers", "invited_domain_managers"]) # type: ignore
|
||||||
|
return readonly_fields
|
||||||
|
|
||||||
# Re-route the get_fieldsets method to utilize DomainInformationAdmin.get_fieldsets
|
# Re-route the get_fieldsets method to utilize DomainInformationAdmin.get_fieldsets
|
||||||
# since that has all the logic for excluding certain fields according to user permissions.
|
# since that has all the logic for excluding certain fields according to user permissions.
|
||||||
|
@ -2334,13 +2385,34 @@ class DomainInformationInline(admin.StackedInline):
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
# Grab fieldsets from DomainInformationAdmin so that it handles all logic
|
# Grab fieldsets from DomainInformationAdmin so that it handles all logic
|
||||||
# for permission-based field visibility.
|
# for permission-based field visibility.
|
||||||
modified_fieldsets = DomainInformationAdmin.get_fieldsets(self, request, obj=None)
|
modified_fieldsets = copy.deepcopy(DomainInformationAdmin.get_fieldsets(self, request, obj=None))
|
||||||
|
|
||||||
# remove .gov domain from fieldset
|
# Modify fieldset sections in place
|
||||||
|
for index, (title, options) in enumerate(modified_fieldsets):
|
||||||
|
if title is None:
|
||||||
|
options["fields"] = [
|
||||||
|
field for field in options["fields"] if field not in ["creator", "domain_request", "notes"]
|
||||||
|
]
|
||||||
|
elif title == "Contacts":
|
||||||
|
options["fields"] = [
|
||||||
|
field
|
||||||
|
for field in options["fields"]
|
||||||
|
if field not in ["other_contacts", "no_other_contacts_rationale"]
|
||||||
|
]
|
||||||
|
options["fields"].extend(["domain_managers", "invited_domain_managers"]) # type: ignore
|
||||||
|
elif title == "Background info":
|
||||||
|
# move domain request and notes to background
|
||||||
|
options["fields"].extend(["domain_request", "notes"]) # type: ignore
|
||||||
|
|
||||||
|
# Remove or remove fieldset sections
|
||||||
for index, (title, f) in enumerate(modified_fieldsets):
|
for index, (title, f) in enumerate(modified_fieldsets):
|
||||||
if title == ".gov domain":
|
if title == ".gov domain":
|
||||||
del modified_fieldsets[index]
|
# remove .gov domain from fieldset
|
||||||
break
|
modified_fieldsets.pop(index)
|
||||||
|
elif title == "Background info":
|
||||||
|
# move Background info to the bottom of the list
|
||||||
|
fieldsets_to_move = modified_fieldsets.pop(index)
|
||||||
|
modified_fieldsets.append(fieldsets_to_move)
|
||||||
|
|
||||||
return modified_fieldsets
|
return modified_fieldsets
|
||||||
|
|
||||||
|
@ -2398,13 +2470,10 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
{"fields": ["name", "state", "expiration_date", "first_ready", "deleted"]},
|
{"fields": ["state", "expiration_date", "first_ready", "deleted", "dnssecdata", "nameservers"]},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# this ordering effects the ordering of results in autocomplete_fields for domain
|
|
||||||
ordering = ["name"]
|
|
||||||
|
|
||||||
def generic_org_type(self, obj):
|
def generic_org_type(self, obj):
|
||||||
return obj.domain_info.get_generic_org_type_display()
|
return obj.domain_info.get_generic_org_type_display()
|
||||||
|
|
||||||
|
@ -2425,6 +2494,28 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
|
|
||||||
organization_name.admin_order_field = "domain_info__organization_name" # type: ignore
|
organization_name.admin_order_field = "domain_info__organization_name" # type: ignore
|
||||||
|
|
||||||
|
def dnssecdata(self, obj):
|
||||||
|
return "Yes" if obj.dnssecdata else "No"
|
||||||
|
|
||||||
|
dnssecdata.short_description = "DNSSEC enabled" # type: ignore
|
||||||
|
|
||||||
|
# Custom method to display formatted nameservers
|
||||||
|
def nameservers(self, obj):
|
||||||
|
if not obj.nameservers:
|
||||||
|
return "No nameservers"
|
||||||
|
|
||||||
|
formatted_nameservers = []
|
||||||
|
for server, ip_list in obj.nameservers:
|
||||||
|
server_display = str(server)
|
||||||
|
if ip_list:
|
||||||
|
server_display += f" [{', '.join(ip_list)}]"
|
||||||
|
formatted_nameservers.append(server_display)
|
||||||
|
|
||||||
|
# Join the formatted strings with line breaks
|
||||||
|
return "\n".join(formatted_nameservers)
|
||||||
|
|
||||||
|
nameservers.short_description = "Name servers" # type: ignore
|
||||||
|
|
||||||
def custom_election_board(self, obj):
|
def custom_election_board(self, obj):
|
||||||
domain_info = getattr(obj, "domain_info", None)
|
domain_info = getattr(obj, "domain_info", None)
|
||||||
if domain_info:
|
if domain_info:
|
||||||
|
@ -2451,7 +2542,15 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
search_fields = ["name"]
|
search_fields = ["name"]
|
||||||
search_help_text = "Search by domain name."
|
search_help_text = "Search by domain name."
|
||||||
change_form_template = "django/admin/domain_change_form.html"
|
change_form_template = "django/admin/domain_change_form.html"
|
||||||
readonly_fields = ("state", "expiration_date", "first_ready", "deleted", "federal_agency")
|
readonly_fields = (
|
||||||
|
"state",
|
||||||
|
"expiration_date",
|
||||||
|
"first_ready",
|
||||||
|
"deleted",
|
||||||
|
"federal_agency",
|
||||||
|
"dnssecdata",
|
||||||
|
"nameservers",
|
||||||
|
)
|
||||||
|
|
||||||
# Table ordering
|
# Table ordering
|
||||||
ordering = ["name"]
|
ordering = ["name"]
|
||||||
|
|
|
@ -33,7 +33,7 @@ from registrar.views.utility.api_views import (
|
||||||
)
|
)
|
||||||
from registrar.views.domains_json import get_domains_json
|
from registrar.views.domains_json import get_domains_json
|
||||||
from registrar.views.utility import always_404
|
from registrar.views.utility import always_404
|
||||||
from api.views import available, get_current_federal, get_current_full
|
from api.views import available, rdap, get_current_federal, get_current_full
|
||||||
|
|
||||||
|
|
||||||
DOMAIN_REQUEST_NAMESPACE = views.DomainRequestWizard.URL_NAMESPACE
|
DOMAIN_REQUEST_NAMESPACE = views.DomainRequestWizard.URL_NAMESPACE
|
||||||
|
@ -200,6 +200,7 @@ urlpatterns = [
|
||||||
path("openid/", include("djangooidc.urls")),
|
path("openid/", include("djangooidc.urls")),
|
||||||
path("request/", include((domain_request_urls, DOMAIN_REQUEST_NAMESPACE))),
|
path("request/", include((domain_request_urls, DOMAIN_REQUEST_NAMESPACE))),
|
||||||
path("api/v1/available/", available, name="available"),
|
path("api/v1/available/", available, name="available"),
|
||||||
|
path("api/v1/rdap/", rdap, name="rdap"),
|
||||||
path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"),
|
path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"),
|
||||||
path("api/v1/get-report/current-full", get_current_full, name="get-current-full"),
|
path("api/v1/get-report/current-full", get_current_full, name="get-current-full"),
|
||||||
path(
|
path(
|
||||||
|
|
|
@ -119,7 +119,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
{% elif field.field.name == "display_admins" %}
|
{% elif field.field.name == "display_admins" or field.field.name == "domain_managers" or field.field.namd == "invited_domain_managers" %}
|
||||||
<div class="readonly">{{ field.contents|safe }}</div>
|
<div class="readonly">{{ field.contents|safe }}</div>
|
||||||
{% elif field.field.name == "display_members" %}
|
{% elif field.field.name == "display_members" %}
|
||||||
<div class="readonly">
|
<div class="readonly">
|
||||||
|
|
|
@ -167,12 +167,6 @@ class TestDomainAdminAsStaff(MockEppLib):
|
||||||
expected_organization_name = "MonkeySeeMonkeyDo"
|
expected_organization_name = "MonkeySeeMonkeyDo"
|
||||||
self.assertContains(response, expected_organization_name)
|
self.assertContains(response, expected_organization_name)
|
||||||
|
|
||||||
# clean up this test's data
|
|
||||||
domain.delete()
|
|
||||||
domain_information.delete()
|
|
||||||
_domain_request.delete()
|
|
||||||
_creator.delete()
|
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_deletion_is_successful(self):
|
def test_deletion_is_successful(self):
|
||||||
"""
|
"""
|
||||||
|
@ -227,9 +221,6 @@ class TestDomainAdminAsStaff(MockEppLib):
|
||||||
|
|
||||||
self.assertEqual(domain.state, Domain.State.DELETED)
|
self.assertEqual(domain.state, Domain.State.DELETED)
|
||||||
|
|
||||||
# clean up data within this test
|
|
||||||
domain.delete()
|
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_deletion_ready_fsm_failure(self):
|
def test_deletion_ready_fsm_failure(self):
|
||||||
"""
|
"""
|
||||||
|
@ -269,9 +260,6 @@ class TestDomainAdminAsStaff(MockEppLib):
|
||||||
|
|
||||||
self.assertEqual(domain.state, Domain.State.READY)
|
self.assertEqual(domain.state, Domain.State.READY)
|
||||||
|
|
||||||
# delete data created in this test
|
|
||||||
domain.delete()
|
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_analyst_deletes_domain_idempotent(self):
|
def test_analyst_deletes_domain_idempotent(self):
|
||||||
"""
|
"""
|
||||||
|
@ -330,8 +318,130 @@ class TestDomainAdminAsStaff(MockEppLib):
|
||||||
)
|
)
|
||||||
self.assertEqual(domain.state, Domain.State.DELETED)
|
self.assertEqual(domain.state, Domain.State.DELETED)
|
||||||
|
|
||||||
# delete data created in this test
|
|
||||||
domain.delete()
|
class TestDomainInformationInline(MockEppLib):
|
||||||
|
"""Test DomainAdmin class, specifically the DomainInformationInline class, as staff user.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
all tests share staffuser; do not change staffuser model in tests
|
||||||
|
tests have available staffuser, client, and admin
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.staffuser = create_user()
|
||||||
|
cls.site = AdminSite()
|
||||||
|
cls.admin = DomainAdmin(model=Domain, admin_site=cls.site)
|
||||||
|
cls.factory = RequestFactory()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client(HTTP_HOST="localhost:8080")
|
||||||
|
self.client.force_login(self.staffuser)
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
Host.objects.all().delete()
|
||||||
|
UserDomainRole.objects.all().delete()
|
||||||
|
Domain.objects.all().delete()
|
||||||
|
DomainInformation.objects.all().delete()
|
||||||
|
DomainRequest.objects.all().delete()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
User.objects.all().delete()
|
||||||
|
super().tearDownClass()
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_domain_managers_display(self):
|
||||||
|
"""Tests the custom domain managers field"""
|
||||||
|
admin_user_1 = User.objects.create(
|
||||||
|
username="testuser1",
|
||||||
|
first_name="Gerald",
|
||||||
|
last_name="Meoward",
|
||||||
|
email="meoward@gov.gov",
|
||||||
|
)
|
||||||
|
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=self.staffuser, name="fake.gov"
|
||||||
|
)
|
||||||
|
domain_request.approve()
|
||||||
|
_domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get()
|
||||||
|
domain = Domain.objects.filter(domain_info=_domain_info).get()
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(user=admin_user_1, domain=domain, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
|
||||||
|
admin_user_2 = User.objects.create(
|
||||||
|
username="testuser2",
|
||||||
|
first_name="Arnold",
|
||||||
|
last_name="Poopy",
|
||||||
|
email="poopy@gov.gov",
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(user=admin_user_2, domain=domain, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
|
||||||
|
# Get the first inline (DomainInformationInline)
|
||||||
|
inline_instance = self.admin.inlines[0](self.admin.model, self.admin.admin_site)
|
||||||
|
|
||||||
|
# Call the domain_managers method
|
||||||
|
domain_managers = inline_instance.domain_managers(domain.domain_info)
|
||||||
|
|
||||||
|
self.assertIn(
|
||||||
|
f'<a href="/admin/registrar/user/{admin_user_1.pk}/change/">testuser1</a>',
|
||||||
|
domain_managers,
|
||||||
|
)
|
||||||
|
self.assertIn("Gerald Meoward", domain_managers)
|
||||||
|
self.assertIn("meoward@gov.gov", domain_managers)
|
||||||
|
self.assertIn(f'<a href="/admin/registrar/user/{admin_user_2.pk}/change/">testuser2</a>', domain_managers)
|
||||||
|
self.assertIn("Arnold Poopy", domain_managers)
|
||||||
|
self.assertIn("poopy@gov.gov", domain_managers)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_invited_domain_managers_display(self):
|
||||||
|
"""Tests the custom invited domain managers field"""
|
||||||
|
admin_user_1 = User.objects.create(
|
||||||
|
username="testuser1",
|
||||||
|
first_name="Gerald",
|
||||||
|
last_name="Meoward",
|
||||||
|
email="meoward@gov.gov",
|
||||||
|
)
|
||||||
|
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=self.staffuser, name="fake.gov"
|
||||||
|
)
|
||||||
|
domain_request.approve()
|
||||||
|
_domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get()
|
||||||
|
domain = Domain.objects.filter(domain_info=_domain_info).get()
|
||||||
|
|
||||||
|
# domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY)
|
||||||
|
UserDomainRole.objects.get_or_create(user=admin_user_1, domain=domain, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
|
||||||
|
admin_user_2 = User.objects.create(
|
||||||
|
username="testuser2",
|
||||||
|
first_name="Arnold",
|
||||||
|
last_name="Poopy",
|
||||||
|
email="poopy@gov.gov",
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(user=admin_user_2, domain=domain, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
|
||||||
|
# Get the first inline (DomainInformationInline)
|
||||||
|
inline_instance = self.admin.inlines[0](self.admin.model, self.admin.admin_site)
|
||||||
|
|
||||||
|
# Call the domain_managers method
|
||||||
|
domain_managers = inline_instance.domain_managers(domain.domain_info)
|
||||||
|
# domain_managers = self.admin.get_inlinesdomain_managers(self.domain)
|
||||||
|
|
||||||
|
self.assertIn(
|
||||||
|
f'<a href="/admin/registrar/user/{admin_user_1.pk}/change/">testuser1</a>',
|
||||||
|
domain_managers,
|
||||||
|
)
|
||||||
|
self.assertIn("Gerald Meoward", domain_managers)
|
||||||
|
self.assertIn("meoward@gov.gov", domain_managers)
|
||||||
|
self.assertIn(f'<a href="/admin/registrar/user/{admin_user_2.pk}/change/">testuser2</a>', domain_managers)
|
||||||
|
self.assertIn("Arnold Poopy", domain_managers)
|
||||||
|
self.assertIn("poopy@gov.gov", domain_managers)
|
||||||
|
|
||||||
|
|
||||||
class TestDomainAdminWithClient(TestCase):
|
class TestDomainAdminWithClient(TestCase):
|
||||||
|
@ -415,17 +525,6 @@ class TestDomainAdminWithClient(TestCase):
|
||||||
self.assertContains(response, domain.name)
|
self.assertContains(response, domain.name)
|
||||||
|
|
||||||
# Check that the fields have the right values.
|
# Check that the fields have the right values.
|
||||||
# == Check for the creator == #
|
|
||||||
|
|
||||||
# Check for the right title, email, and phone number in the response.
|
|
||||||
# We only need to check for the end tag
|
|
||||||
# (Otherwise this test will fail if we change classes, etc)
|
|
||||||
self.assertContains(response, "Treat inspector")
|
|
||||||
self.assertContains(response, "meoward.jones@igorville.gov")
|
|
||||||
self.assertContains(response, "(555) 123 12345")
|
|
||||||
|
|
||||||
# Check for the field itself
|
|
||||||
self.assertContains(response, "Meoward Jones")
|
|
||||||
|
|
||||||
# == Check for the senior_official == #
|
# == Check for the senior_official == #
|
||||||
self.assertContains(response, "testy@town.com")
|
self.assertContains(response, "testy@town.com")
|
||||||
|
@ -435,11 +534,6 @@ class TestDomainAdminWithClient(TestCase):
|
||||||
# Includes things like readonly fields
|
# Includes things like readonly fields
|
||||||
self.assertContains(response, "Testy Tester")
|
self.assertContains(response, "Testy Tester")
|
||||||
|
|
||||||
# == Test the other_employees field == #
|
|
||||||
self.assertContains(response, "testy2@town.com")
|
|
||||||
self.assertContains(response, "Another Tester")
|
|
||||||
self.assertContains(response, "(555) 555 5557")
|
|
||||||
|
|
||||||
# Test for the copy link
|
# Test for the copy link
|
||||||
self.assertContains(response, "button--clipboard")
|
self.assertContains(response, "button--clipboard")
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,7 @@ class TestURLAuth(TestCase):
|
||||||
"/api/v1/available/",
|
"/api/v1/available/",
|
||||||
"/api/v1/get-report/current-federal",
|
"/api/v1/get-report/current-federal",
|
||||||
"/api/v1/get-report/current-full",
|
"/api/v1/get-report/current-full",
|
||||||
|
"/api/v1/rdap/",
|
||||||
"/health",
|
"/health",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue