mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-29 17:00:02 +02:00
Sketch test cases for Domain integration
This also includes an illustration of how action buttons could be integrated into the admin portal.
This commit is contained in:
parent
78a9c50561
commit
cabe516f9f
5 changed files with 507 additions and 92 deletions
|
@ -1,4 +1,4 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin, messages
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.http.response import HttpResponseRedirect
|
from django.http.response import HttpResponseRedirect
|
||||||
|
@ -50,13 +50,40 @@ class MyHostAdmin(AuditedAdmin):
|
||||||
inlines = [HostIPInline]
|
inlines = [HostIPInline]
|
||||||
|
|
||||||
|
|
||||||
|
class DomainAdmin(AuditedAdmin):
|
||||||
|
|
||||||
|
"""Custom domain admin class to add extra buttons."""
|
||||||
|
|
||||||
|
change_form_template = "django/admin/domain_change_form.html"
|
||||||
|
readonly_fields = ["state"]
|
||||||
|
|
||||||
|
def response_change(self, request, obj):
|
||||||
|
if "_place_client_hold" in request.POST:
|
||||||
|
try:
|
||||||
|
obj.place_client_hold()
|
||||||
|
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(".")
|
||||||
|
|
||||||
|
return super().response_change(request, obj)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(models.User, MyUserAdmin)
|
admin.site.register(models.User, MyUserAdmin)
|
||||||
admin.site.register(models.UserDomainRole, AuditedAdmin)
|
admin.site.register(models.UserDomainRole, AuditedAdmin)
|
||||||
admin.site.register(models.Contact, AuditedAdmin)
|
admin.site.register(models.Contact, AuditedAdmin)
|
||||||
admin.site.register(models.DomainInvitation, AuditedAdmin)
|
admin.site.register(models.DomainInvitation, AuditedAdmin)
|
||||||
admin.site.register(models.DomainApplication, AuditedAdmin)
|
admin.site.register(models.DomainApplication, AuditedAdmin)
|
||||||
admin.site.register(models.DomainInformation, AuditedAdmin)
|
admin.site.register(models.DomainInformation, AuditedAdmin)
|
||||||
admin.site.register(models.Domain, AuditedAdmin)
|
admin.site.register(models.Domain, DomainAdmin)
|
||||||
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, AuditedAdmin)
|
admin.site.register(models.Website, AuditedAdmin)
|
||||||
|
|
|
@ -267,7 +267,7 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
|
|
||||||
def place_client_hold(self):
|
def place_client_hold(self):
|
||||||
"""This domain should not be active."""
|
"""This domain should not be active."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError("This is not implemented yet.")
|
||||||
|
|
||||||
def remove_client_hold(self):
|
def remove_client_hold(self):
|
||||||
"""This domain is okay to be active."""
|
"""This domain is okay to be active."""
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends 'admin/change_form.html' %}
|
||||||
|
|
||||||
|
{% block field_sets %}
|
||||||
|
<div class="submit-row">
|
||||||
|
<input type="submit" value="Place hold" name="_place_client_hold">
|
||||||
|
</div>
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
|
@ -12,7 +12,6 @@ from registrar.models import (
|
||||||
DomainInvitation,
|
DomainInvitation,
|
||||||
UserDomainRole,
|
UserDomainRole,
|
||||||
)
|
)
|
||||||
from unittest import skip
|
|
||||||
|
|
||||||
import boto3_mocking # type: ignore
|
import boto3_mocking # type: ignore
|
||||||
from .common import MockSESClient, less_console_noise
|
from .common import MockSESClient, less_console_noise
|
||||||
|
@ -213,55 +212,3 @@ class TestInvitations(TestCase):
|
||||||
"""A new user's first_login callback retrieves their invitations."""
|
"""A new user's first_login callback retrieves their invitations."""
|
||||||
self.user.first_login()
|
self.user.first_login()
|
||||||
self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain))
|
self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain))
|
||||||
|
|
||||||
|
|
||||||
@skip("Not implemented yet.")
|
|
||||||
class TestDomainApplicationLifeCycle(TestCase):
|
|
||||||
def test_application_approval(self):
|
|
||||||
# DomainApplication is created
|
|
||||||
# test: Domain is created and is inactive
|
|
||||||
# analyst approves DomainApplication
|
|
||||||
# test: Domain is activated
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_application_rejection(self):
|
|
||||||
# DomainApplication is created
|
|
||||||
# test: Domain is created and is inactive
|
|
||||||
# analyst rejects DomainApplication
|
|
||||||
# test: Domain remains inactive
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_application_deleted_before_approval(self):
|
|
||||||
# DomainApplication is created
|
|
||||||
# test: Domain is created and is inactive
|
|
||||||
# admin deletes DomainApplication
|
|
||||||
# test: Domain is deleted; Hosts, HostIps and Nameservers are deleted
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_application_deleted_following_approval(self):
|
|
||||||
# DomainApplication is created
|
|
||||||
# test: Domain is created and is inactive
|
|
||||||
# analyst approves DomainApplication
|
|
||||||
# admin deletes DomainApplication
|
|
||||||
# test: DomainApplication foreign key field on Domain is set to null
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_application_approval_with_conflicting_name(self):
|
|
||||||
# DomainApplication #1 is created
|
|
||||||
# test: Domain #1 is created and is inactive
|
|
||||||
# analyst approves DomainApplication #1
|
|
||||||
# test: Domain #1 is activated
|
|
||||||
# DomainApplication #2 is created, with the same domain name string
|
|
||||||
# test: Domain #2 is created and is inactive
|
|
||||||
# analyst approves DomainApplication #2
|
|
||||||
# test: error is raised
|
|
||||||
# test: DomainApplication #1 remains approved
|
|
||||||
# test: Domain #1 remains active
|
|
||||||
# test: DomainApplication #2 remains in investigating
|
|
||||||
# test: Domain #2 remains inactive
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_application_approval_with_network_errors(self):
|
|
||||||
# TODO: scenario wherein application is approved,
|
|
||||||
# but attempts to contact the registry to activate the domain fail
|
|
||||||
pass
|
|
||||||
|
|
|
@ -1,54 +1,487 @@
|
||||||
|
"""
|
||||||
|
Feature being tested: Registry Integration
|
||||||
|
|
||||||
|
This file tests the various ways in which the registrar interacts with the registry.
|
||||||
|
"""
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
|
|
||||||
from registrar.models import (
|
from registrar.models import (
|
||||||
DomainApplication,
|
|
||||||
User,
|
|
||||||
Domain,
|
Domain,
|
||||||
)
|
)
|
||||||
from unittest import skip
|
from unittest import skip
|
||||||
|
|
||||||
|
|
||||||
class TestDomain(TestCase):
|
class TestDomainCreation(TestCase):
|
||||||
def test_empty_create_fails(self):
|
"""Rule: An approved domain application must result in a domain"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Background:
|
||||||
|
Given that a valid domain application exists
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_approved_application_creates_domain_locally(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst approves a domain application
|
||||||
|
When the DomainApplication transitions to approved
|
||||||
|
Then a Domain exists in the database with the same `name`
|
||||||
|
But a domain object does not exist in the registry
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@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 `domain.is_active()` is called
|
||||||
|
Then Domain sends `commands.CreateDomain` to the registry
|
||||||
|
And `domain.state` is set to `CREATED`
|
||||||
|
And `domain.is_active()` returns False
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
def test_empty_domain_creation(self):
|
||||||
"""Can't create a completely empty domain."""
|
"""Can't create a completely empty domain."""
|
||||||
with self.assertRaisesRegex(IntegrityError, "name"):
|
with self.assertRaisesRegex(IntegrityError, "name"):
|
||||||
Domain.objects.create()
|
Domain.objects.create()
|
||||||
|
|
||||||
def test_minimal_create(self):
|
def test_minimal_creation(self):
|
||||||
"""Can create with just a name."""
|
"""Can create with just a name."""
|
||||||
Domain.objects.create(name="igorville.gov")
|
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_duplicate_creation(self):
|
||||||
def test_get_status(self):
|
"""Can't create domain if name is not unique."""
|
||||||
"""Returns proper status based on `state`."""
|
Domain.objects.create(name="igorville.gov")
|
||||||
domain = Domain.objects.create(name="igorville.gov")
|
with self.assertRaisesRegex(IntegrityError, "name"):
|
||||||
domain.save()
|
Domain.objects.create(name="igorville.gov")
|
||||||
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")
|
class TestRegistrantContacts(TestCase):
|
||||||
def test_fsm_activate_fail_unapproved(self):
|
"""Rule: Registrants may modify their WHOIS data"""
|
||||||
"""Can't activate domain if application isn't approved."""
|
|
||||||
d1, _ = Domain.objects.get_or_create(name="igorville.gov")
|
def setUp(self):
|
||||||
user, _ = User.objects.get_or_create()
|
"""
|
||||||
application = DomainApplication.objects.create(creator=user)
|
Background:
|
||||||
d1.domain_application = application
|
Given the registrant is logged in
|
||||||
d1.save()
|
And the registrant is the admin on a domain
|
||||||
with self.assertRaises(ValueError):
|
"""
|
||||||
d1.activate()
|
pass
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_no_security_email(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant declines to add a security contact email
|
||||||
|
Given the domain exists in the registry
|
||||||
|
Then the domain has a valid security contact with CISA defaults
|
||||||
|
And disclose flags are set to keep the email address hidden
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_adds_security_email(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds a security contact email
|
||||||
|
When `domain.security_contact` is set equal to a PublicContact with the
|
||||||
|
chosen security contact email
|
||||||
|
Then Domain sends `commands.CreateContact` to the registry
|
||||||
|
And Domain sends `commands.UpdateDomain` to the registry with the newly
|
||||||
|
created contact of type 'security'
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_security_email_is_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds a security contact email twice, due to a UI glitch
|
||||||
|
When `commands.CreateContact` and `commands.UpdateDomain` are sent
|
||||||
|
to the registry twice with identical data
|
||||||
|
Then no errors are raised in Domain
|
||||||
|
"""
|
||||||
|
# implementation note: this requires seeing what happens when these are actually
|
||||||
|
# sent like this, and then implementing appropriate mocks for any errors the
|
||||||
|
# registry normally sends in this case
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_deletes_security_email(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant clears out an existing security contact email
|
||||||
|
Given a domain exists in the registry with a user-added security email
|
||||||
|
When `domain.security_contact` is set equal to a PublicContact with an empty
|
||||||
|
security contact email
|
||||||
|
Then Domain sends `commands.UpdateDomain` and `commands.DeleteContact`
|
||||||
|
to the registry
|
||||||
|
And the domain has a valid security contact with CISA defaults
|
||||||
|
And disclose flags are set to keep the email address hidden
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_updates_security_email(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant replaces one valid security contact email with another
|
||||||
|
Given a domain exists in the registry with a user-added security email
|
||||||
|
When `domain.security_contact` is set equal to a PublicContact with a new
|
||||||
|
security contact email
|
||||||
|
Then Domain sends `commands.UpdateContact` to the registry
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_update_is_unsuccessful(self):
|
||||||
|
"""
|
||||||
|
Scenario: An update to the security contact is unsuccessful
|
||||||
|
When an error is returned from epplibwrapper
|
||||||
|
Then a user-friendly error message is returned for displaying on the web
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class TestRegistrantNameservers(TestCase):
|
||||||
|
"""Rule: Registrants may modify their nameservers"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Background:
|
||||||
|
Given the registrant is logged in
|
||||||
|
And the registrant is the admin on a domain
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_adds_one_nameserver(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds a single nameserver
|
||||||
|
Given the domain has zero nameservers
|
||||||
|
When `domain.nameservers` is set to an array of length 1
|
||||||
|
Then `commands.CreateHost` and `commands.UpdateDomain` is sent
|
||||||
|
to the registry
|
||||||
|
And `domain.is_active` returns False
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_adds_two_nameservers(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds 2 or more nameservers, thereby activating the domain
|
||||||
|
Given the domain has zero nameservers
|
||||||
|
When `domain.nameservers` is set to an array of length 2
|
||||||
|
Then `commands.CreateHost` and `commands.UpdateDomain` is sent
|
||||||
|
to the registry
|
||||||
|
And `domain.is_active` returns True
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_adds_too_many_nameservers(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds 14 or more nameservers
|
||||||
|
Given the domain has zero nameservers
|
||||||
|
When `domain.nameservers` is set to an array of length 14
|
||||||
|
Then Domain raises a user-friendly error
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_removes_some_nameservers(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant removes some nameservers, while keeping at least 2
|
||||||
|
Given the domain has 3 nameservers
|
||||||
|
When `domain.nameservers` is set to an array containing nameserver #1 and #2
|
||||||
|
Then `commands.UpdateDomain` and `commands.DeleteHost` is sent
|
||||||
|
to the registry
|
||||||
|
And `domain.is_active` returns True
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_removes_too_many_nameservers(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant removes some nameservers, bringing the total to less than 2
|
||||||
|
Given the domain has 3 nameservers
|
||||||
|
When `domain.nameservers` is set to an array containing nameserver #1
|
||||||
|
Then `commands.UpdateDomain` and `commands.DeleteHost` is sent
|
||||||
|
to the registry
|
||||||
|
And `domain.is_active` returns False
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_replaces_nameservers(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant simultaneously adds and removes some nameservers
|
||||||
|
Given the domain has 3 nameservers
|
||||||
|
When `domain.nameservers` is set to an array containing nameserver #1 plus
|
||||||
|
two new nameservers
|
||||||
|
Then `commands.CreateHost` is sent to create #4 and #5
|
||||||
|
And `commands.UpdateDomain` is sent to add #4 and #5 plus remove #2 and #3
|
||||||
|
And `commands.DeleteHost` is sent to delete #2 and #3
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_cannot_add_subordinate_without_ip(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds a nameserver which is a subdomain of their .gov
|
||||||
|
Given the domain exists in the registry
|
||||||
|
When `domain.nameservers` is set to an array containing an entry
|
||||||
|
with a subdomain of the domain and no IP addresses
|
||||||
|
Then Domain raises a user-friendly error
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_updates_ips(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant changes IP addresses for a nameserver
|
||||||
|
Given the domain exists in the registry
|
||||||
|
And has a subordinate nameserver
|
||||||
|
When `domain.nameservers` is set to an array containing that nameserver
|
||||||
|
with a different IP address(es)
|
||||||
|
Then `commands.UpdateHost` is sent to the registry
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_cannot_add_non_subordinate_with_ip(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds a nameserver which is NOT a subdomain of their .gov
|
||||||
|
Given the domain exists in the registry
|
||||||
|
When `domain.nameservers` is set to an array containing an entry
|
||||||
|
which is not a subdomain of the domain and has IP addresses
|
||||||
|
Then Domain raises a user-friendly error
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_nameservers_are_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds a set of nameservers twice, due to a UI glitch
|
||||||
|
When `commands.CreateHost` and `commands.UpdateDomain` are sent
|
||||||
|
to the registry twice with identical data
|
||||||
|
Then no errors are raised in Domain
|
||||||
|
"""
|
||||||
|
# implementation note: this requires seeing what happens when these are actually
|
||||||
|
# sent like this, and then implementing appropriate mocks for any errors the
|
||||||
|
# registry normally sends in this case
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_update_is_unsuccessful(self):
|
||||||
|
"""
|
||||||
|
Scenario: An update to the nameservers is unsuccessful
|
||||||
|
When an error is returned from epplibwrapper
|
||||||
|
Then a user-friendly error message is returned for displaying on the web
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class TestRegistrantDNSSEC(TestCase):
|
||||||
|
"""Rule: Registrants may modify their secure DNS data"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Background:
|
||||||
|
Given the registrant is logged in
|
||||||
|
And the registrant is the admin on a domain
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_user_adds_dns_data(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds DNS data
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_dnssec_is_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Registrant adds DNS data twice, due to a UI glitch
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
# implementation note: this requires seeing what happens when these are actually
|
||||||
|
# sent like this, and then implementing appropriate mocks for any errors the
|
||||||
|
# registry normally sends in this case
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_update_is_unsuccessful(self):
|
||||||
|
"""
|
||||||
|
Scenario: An update to the security contact is unsuccessful
|
||||||
|
When an error is returned from epplibwrapper
|
||||||
|
Then a user-friendly error message is returned for displaying on the web
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class TestAnalystClientHold(TestCase):
|
||||||
|
"""Rule: Analysts may suspend or restore a domain by using client hold"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Background:
|
||||||
|
Given the analyst is logged in
|
||||||
|
And a domain exists in the registry
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_places_client_hold(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst takes a domain off the internet
|
||||||
|
When `domain.place_client_hold()` is called
|
||||||
|
Then `CLIENT_HOLD` is added to the domain's statuses
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_places_client_hold_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst tries to place client hold twice
|
||||||
|
Given `CLIENT_HOLD` is already in the domain's statuses
|
||||||
|
When `domain.place_client_hold()` is called
|
||||||
|
Then Domain returns normally (without error)
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_removes_client_hold(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst restores a suspended domain
|
||||||
|
Given `CLIENT_HOLD` is in the domain's statuses
|
||||||
|
When `domain.remove_client_hold()` is called
|
||||||
|
Then `CLIENT_HOLD` is no longer in the domain's statuses
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_removes_client_hold_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst tries to remove client hold twice
|
||||||
|
Given `CLIENT_HOLD` is not in the domain's statuses
|
||||||
|
When `domain.remove_client_hold()` is called
|
||||||
|
Then Domain returns normally (without error)
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_update_is_unsuccessful(self):
|
||||||
|
"""
|
||||||
|
Scenario: An update to place or remove client hold is unsuccessful
|
||||||
|
When an error is returned from epplibwrapper
|
||||||
|
Then a user-friendly error message is returned for displaying on the web
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class TestAnalystLock(TestCase):
|
||||||
|
"""Rule: Analysts may lock or unlock a domain to prevent or allow updates"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Background:
|
||||||
|
Given the analyst is logged in
|
||||||
|
And a domain exists in the registry
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_locks_domain(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst locks a domain to prevent edits or deletion
|
||||||
|
When `domain.lock()` is called
|
||||||
|
Then `CLIENT_DELETE_PROHIBITED` is added to the domain's statuses
|
||||||
|
And `CLIENT_TRANSFER_PROHIBITED` is added to the domain's statuses
|
||||||
|
And `CLIENT_UPDATE_PROHIBITED` is added to the domain's statuses
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_locks_domain_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst tries to lock a domain twice
|
||||||
|
Given `CLIENT_*_PROHIBITED` is already in the domain's statuses
|
||||||
|
When `domain.lock()` is called
|
||||||
|
Then Domain returns normally (without error)
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_removes_client_hold(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst unlocks a domain to allow deletion or edits
|
||||||
|
Given `CLIENT_*_PROHIBITED` is in the domain's statuses
|
||||||
|
When `domain.unlock()` is called
|
||||||
|
Then `CLIENT_DELETE_PROHIBITED` is no longer in the domain's statuses
|
||||||
|
And `CLIENT_TRANSFER_PROHIBITED` is no longer in the domain's statuses
|
||||||
|
And `CLIENT_UPDATE_PROHIBITED` is no longer in the domain's statuses
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_removes_client_hold_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst tries to unlock a domain twice
|
||||||
|
Given `CLIENT_*_PROHIBITED` is not in the domain's statuses
|
||||||
|
When `domain.unlock()` is called
|
||||||
|
Then Domain returns normally (without error)
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_update_is_unsuccessful(self):
|
||||||
|
"""
|
||||||
|
Scenario: An update to lock or unlock a domain is unsuccessful
|
||||||
|
When an error is returned from epplibwrapper
|
||||||
|
Then a user-friendly error message is returned for displaying on the web
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class TestAnalystDelete(TestCase):
|
||||||
|
"""Rule: Analysts may delete a domain"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Background:
|
||||||
|
Given the analyst is logged in
|
||||||
|
And a domain exists in the registry
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_deletes_domain(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst permanently deletes a domain
|
||||||
|
When `domain.delete()` is called
|
||||||
|
Then `commands.DeleteDomain` is sent to the registry
|
||||||
|
And `state` is set to `DELETED`
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_analyst_deletes_domain_idempotent(self):
|
||||||
|
"""
|
||||||
|
Scenario: Analyst tries to delete an already deleted domain
|
||||||
|
Given `state` is already `DELETED`
|
||||||
|
When `domain.delete()` is called
|
||||||
|
Then `commands.DeleteDomain` is sent to the registry
|
||||||
|
And Domain returns normally (without error)
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_deletion_is_unsuccessful(self):
|
||||||
|
"""
|
||||||
|
Scenario: Domain deletion is unsuccessful
|
||||||
|
When an error is returned from epplibwrapper
|
||||||
|
Then a user-friendly error message is returned for displaying on the web
|
||||||
|
And `state` is not set to `DELETED`
|
||||||
|
"""
|
||||||
|
raise
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue