Record domain transaction for domain deletes

This is the third of many cls adding explicit logging in all our domain
mutation flows to facilitate transaction reporting.

We add a +1 counter for either grace or nograce deletes, based on the grace period status of the domain. We then search back in time for DOMAIN_CREATE, DOMAIN_RENEW and DOMAIN_AUTORENEW HistoryEntries off the same resource that happened in their corresponding grace periods (5, 5 and 45 days respectively). All transaction records for these events are then given -1 counters to properly account for cancellations in the NET_CREATE and NET_RENEW fields.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=166506010
This commit is contained in:
larryruili 2017-08-25 11:55:39 -07:00 committed by Ben McIlwain
parent d011b8e073
commit 7ee8bc9070
4 changed files with 350 additions and 60 deletions

View file

@ -24,15 +24,20 @@ import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.createCancellingRecords;
import static google.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.isAddsField;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.isRenewsField;
import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.union;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
@ -77,14 +82,18 @@ import google.registry.model.poll.PendingActionNotificationResponse.DomainPendin
import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.OneTime;
import google.registry.model.registry.Registry;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.transfer.TransferStatus;
import java.util.Collections;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/**
* An EPP flow that deletes a domain.
@ -133,24 +142,30 @@ public final class DomainDeleteFlow implements TransactionalFlow {
customLogic.afterValidation(
AfterValidationParameters.newBuilder().setExistingDomain(existingDomain).build());
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
HistoryEntry historyEntry = buildHistoryEntry(existingDomain, now);
Builder builder = existingDomain.getStatusValues().contains(StatusValue.PENDING_TRANSFER)
? ResourceFlowUtils.<DomainResource, DomainResource.Builder>resolvePendingTransfer(
existingDomain, TransferStatus.SERVER_CANCELLED, now)
: existingDomain.asBuilder();
builder.setDeletionTime(now).setStatusValues(null);
// If the domain is in the Add Grace Period, we delete it immediately, which is already
// reflected in the builder we just prepared. Otherwise we give it a PENDING_DELETE status.
if (!existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.ADD)) {
boolean inAddGracePeriod =
existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.ADD);
// If the domain is in the Add Grace Period, we delete it immediately.
// Otherwise, we give it a PENDING_DELETE status.
Duration durationUntilDelete =
inAddGracePeriod
? Duration.ZERO
// By default, this should be 30 days of grace, and 5 days of pending delete.
DateTime deletionTime = now
.plus(registry.getRedemptionGracePeriodLength())
.plus(registry.getPendingDeleteLength());
: registry.getRedemptionGracePeriodLength().plus(registry.getPendingDeleteLength());
HistoryEntry historyEntry = buildHistoryEntry(
existingDomain, registry, now, durationUntilDelete, inAddGracePeriod);
if (inAddGracePeriod) {
builder.setDeletionTime(now).setStatusValues(null);
} else {
DateTime deletionTime = now.plus(durationUntilDelete);
PollMessage.OneTime deletePollMessage =
createDeletePollMessage(existingDomain, historyEntry, deletionTime);
entitiesToSave.add(deletePollMessage);
builder.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.setDeletionTime(deletionTime)
builder.setDeletionTime(deletionTime)
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
// Clear out all old grace periods and add REDEMPTION, which does not include a key to a
// billing event because there isn't one for a domain delete.
.setGracePeriods(ImmutableSet.of(GracePeriod.createWithoutBillingEvent(
@ -218,11 +233,43 @@ public final class DomainDeleteFlow implements TransactionalFlow {
}
}
private HistoryEntry buildHistoryEntry(DomainResource existingResource, DateTime now) {
private HistoryEntry buildHistoryEntry(
DomainResource existingResource,
Registry registry,
DateTime now,
Duration durationUntilDelete,
boolean inAddGracePeriod) {
Duration maxGracePeriod = Collections.max(
ImmutableSet.of(
registry.getAddGracePeriodLength(),
registry.getAutoRenewGracePeriodLength(),
registry.getRenewGracePeriodLength()));
ImmutableSet<DomainTransactionRecord> cancelledRecords =
createCancellingRecords(
existingResource,
now,
maxGracePeriod,
new Predicate<DomainTransactionRecord>() {
@Override
public boolean apply(@Nullable DomainTransactionRecord domainTransactionRecord) {
return isAddsField(domainTransactionRecord.getReportField())
|| isRenewsField(domainTransactionRecord.getReportField());
}
});
return historyBuilder
.setType(HistoryEntry.Type.DOMAIN_DELETE)
.setModificationTime(now)
.setParent(Key.create(existingResource))
.setDomainTransactionRecords(
union(
cancelledRecords,
DomainTransactionRecord.create(
existingResource.getTld(),
now.plus(durationUntilDelete),
inAddGracePeriod
? TransactionReportField.DELETED_DOMAINS_GRACE
: TransactionReportField.DELETED_DOMAINS_NOGRACE,
1)))
.build();
}

View file

@ -42,7 +42,9 @@ import static google.registry.util.FormattingLogger.getLoggerForCallerClass;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@ -105,6 +107,7 @@ import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservationType;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tmch.ClaimsListShard;
import google.registry.util.FormattingLogger;
@ -118,6 +121,7 @@ import javax.annotation.Nullable;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/** Static utility functions for domain flows. */
public class DomainFlowUtils {
@ -909,6 +913,56 @@ public class DomainFlowUtils {
return builder.build();
}
/**
* Returns a set of DomainTransactionRecords which negate the most recent HistoryEntry's records.
*
* <p>Domain deletes and transfers use this function to account for previous records negated by
* their flow. For example, if a grace period delete occurs, we must add -1 counters for the
* associated NET_ADDS_#_YRS field, if it exists.
*
* <p>The steps are as follows: 1. Find all HistoryEntries under the domain modified in the past,
* up to the maxSearchPeriod. 2. Only keep HistoryEntries with a DomainTransactionRecord that a)
* hasn't been reported yet and b) matches the predicate 3. Return the transactionRecords under
* the most recent HistoryEntry that fits the above criteria, with negated reportAmounts.
*/
static ImmutableSet<DomainTransactionRecord> createCancellingRecords(
DomainResource domainResource,
final DateTime now,
Duration maxSearchPeriod,
final Predicate<DomainTransactionRecord> isCancellable) {
List<HistoryEntry> recentHistoryEntries = ofy().load()
.type(HistoryEntry.class)
.ancestor(domainResource)
.filter("modificationTime >=", now.minus(maxSearchPeriod))
.order("modificationTime")
.list();
Optional<HistoryEntry> entryToCancel = FluentIterable.from(recentHistoryEntries)
.filter(
new Predicate<HistoryEntry>() {
@Override
public boolean apply(HistoryEntry historyEntry) {
// Look for add and renew transaction records that have yet to be reported
for (DomainTransactionRecord record : historyEntry.getDomainTransactionRecords()) {
if (isCancellable.apply(record) && record.getReportingTime().isAfter(now)) {
return true;
}
}
return false;
}
})
// We only want to cancel out the most recent add or renewal
.last();
ImmutableSet.Builder<DomainTransactionRecord> recordsBuilder = new ImmutableSet.Builder<>();
if (entryToCancel.isPresent()) {
for (DomainTransactionRecord record : entryToCancel.get().getDomainTransactionRecords()) {
int cancelledAmount = -1 * record.getReportAmount();
recordsBuilder.add(record.asBuilder().setReportAmount(cancelledAmount).build());
}
}
return recordsBuilder.build();
}
/** Resource linked to this domain does not exist. */
static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException {
public LinkedResourcesDoNotExistException(Class<?> type, ImmutableSet<String> resourceIds) {

View file

@ -17,6 +17,7 @@ package google.registry.model.reporting;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
@ -114,6 +115,42 @@ public class DomainTransactionRecord extends ImmutableObject implements Buildabl
return nameToField("NET_RENEWS_%d_YR", years);
}
private static final ImmutableSet<TransactionReportField> ADD_FIELDS =
ImmutableSet.of(
NET_ADDS_1_YR,
NET_ADDS_2_YR,
NET_ADDS_3_YR,
NET_ADDS_4_YR,
NET_ADDS_5_YR,
NET_ADDS_6_YR,
NET_ADDS_7_YR,
NET_ADDS_8_YR,
NET_ADDS_9_YR,
NET_ADDS_10_YR);
private static final ImmutableSet<TransactionReportField> RENEW_FIELDS =
ImmutableSet.of(
NET_RENEWS_1_YR,
NET_RENEWS_2_YR,
NET_RENEWS_3_YR,
NET_RENEWS_4_YR,
NET_RENEWS_5_YR,
NET_RENEWS_6_YR,
NET_RENEWS_7_YR,
NET_RENEWS_8_YR,
NET_RENEWS_9_YR,
NET_RENEWS_10_YR);
/** Boilerplate to determine if a field is one of the NET_ADDS fields. */
public static boolean isAddsField(TransactionReportField field) {
return ADD_FIELDS.contains(field);
}
/** Boilerplate to determine if a field is one of the NET_ADDS fields. */
public static boolean isRenewsField(TransactionReportField field) {
return RENEW_FIELDS.contains(field);
}
private static TransactionReportField nameToField(String enumTemplate, int years) {
checkArgument(
years >= 1 && years <= 10, "domain add and renew years must be between 1 and 10");

View file

@ -18,6 +18,15 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.flows.domain.DomainTransferFlowTestCase.persistWithPendingTransfer;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.DELETED_DOMAINS_GRACE;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.DELETED_DOMAINS_NOGRACE;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.NET_ADDS_10_YR;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.NET_ADDS_1_YR;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.NET_RENEWS_3_YR;
import static google.registry.model.reporting.DomainTransactionRecord.TransactionReportField.RESTORED_DOMAINS;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_DELETE;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
import static google.registry.testing.DatastoreHelper.assertBillingEvents;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
@ -67,6 +76,7 @@ import google.registry.model.poll.PendingActionNotificationResponse;
import google.registry.model.poll.PollMessage;
import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
@ -106,7 +116,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
// For flags extension tests.
}
private void setupSuccessfulTest() throws Exception {
private void setUpSuccessfulTest() throws Exception {
createReferencedEntities(A_MONTH_FROM_NOW);
BillingEvent.Recurring autorenewBillingEvent = persistResource(
createAutorenewBillingEvent("TheRegistrar").build());
@ -129,17 +139,29 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
.build();
earlierHistoryEntry = persistResource(
new HistoryEntry.Builder()
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setType(DOMAIN_CREATE)
.setParent(domain)
.build());
}
private void setupGracePeriods(GracePeriod... gracePeriods) throws Exception {
private void setUpGracePeriods(GracePeriod... gracePeriods) throws Exception {
domain = persistResource(
domain.asBuilder().setGracePeriods(ImmutableSet.copyOf(gracePeriods)).build());
}
private void setupAutorenewGracePeriod() throws Exception {
private void setUpGracePeriodDurations() {
persistResource(
Registry.get("tld")
.asBuilder()
.setAddGracePeriodLength(Duration.standardDays(3))
.setRenewGracePeriodLength(Duration.standardDays(2))
.setAutoRenewGracePeriodLength(Duration.standardDays(1))
.setRedemptionGracePeriodLength(Duration.standardHours(1))
.setPendingDeleteLength(Duration.standardHours(2))
.build());
}
private void setUpAutorenewGracePeriod() throws Exception {
createReferencedEntities(A_MONTH_AGO.plusYears(1));
BillingEvent.Recurring autorenewBillingEvent = persistResource(
createAutorenewBillingEvent("TheRegistrar")
@ -224,15 +246,15 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testDryRun() throws Exception {
setupSuccessfulTest();
setupGracePeriods(GracePeriod.create(
setUpSuccessfulTest();
setUpGracePeriods(GracePeriod.create(
GracePeriodStatus.ADD, TIME_BEFORE_FLOW.plusDays(1), "foo", null));
dryRunFlowAssertResponse(readFile("domain_delete_response.xml"));
}
@Test
public void testDryRun_noGracePeriods() throws Exception {
setupSuccessfulTest();
setUpSuccessfulTest();
dryRunFlowAssertResponse(readFile("domain_delete_response_pending.xml"));
}
@ -246,10 +268,10 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
String responseFilename,
Map<String, String> substitutions) throws Exception {
// Persist the billing event so it can be retrieved for cancellation generation and checking.
setupSuccessfulTest();
setUpSuccessfulTest();
BillingEvent.OneTime graceBillingEvent =
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 123)));
setupGracePeriods(GracePeriod.forBillingEvent(gracePeriodStatus, graceBillingEvent));
setUpGracePeriods(GracePeriod.forBillingEvent(gracePeriodStatus, graceBillingEvent));
// We should see exactly one poll message, which is for the autorenew 1 month in the future.
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
clock.advanceOneMilli();
@ -258,8 +280,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
assertThat(reloadResourceByForeignKey()).isNull();
// The add grace period is for a billable action, so it should trigger a cancellation.
assertAutorenewClosedAndCancellationCreatedFor(
graceBillingEvent,
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_DELETE));
graceBillingEvent, getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE));
assertDnsTasksEnqueued("example.tld");
// There should be no poll messages. The previous autorenew poll message should now be deleted.
assertThat(getPollMessages("TheRegistrar", A_MONTH_FROM_NOW)).isEmpty();
@ -302,10 +323,10 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
private void doSuccessfulTest_noAddGracePeriod(
String responseFilename, Map<String, String> substitutions) throws Exception {
// Persist the billing event so it can be retrieved for cancellation generation and checking.
setupSuccessfulTest();
setUpSuccessfulTest();
BillingEvent.OneTime renewBillingEvent =
persistResource(createBillingEvent(Reason.RENEW, Money.of(USD, 456)));
setupGracePeriods(
setUpGracePeriods(
GracePeriod.forBillingEvent(GracePeriodStatus.RENEW, renewBillingEvent),
// This grace period has no associated billing event, so it won't cause a cancellation.
GracePeriod.create(GracePeriodStatus.TRANSFER, TIME_BEFORE_FLOW.plusDays(1), "foo", null));
@ -322,9 +343,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
.plus(Registry.get("tld").getPendingDeleteLength())).and()
.hasDeletePollMessage().and()
.hasExactlyStatusValues(StatusValue.INACTIVE, StatusValue.PENDING_DELETE).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_DELETE);
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_DELETE);
// We leave the original expiration time unchanged; if the expiration time is before the
// deletion time, that means once it passes the domain will experience a "phantom autorenew"
// where the expirationTime advances and the grace period appears, but since the delete flow
@ -332,8 +351,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
assertAboutDomains().that(resource).hasRegistrationExpirationTime(originalExpirationTime);
// All existing grace periods that were for billable actions should cause cancellations.
assertAutorenewClosedAndCancellationCreatedFor(
renewBillingEvent,
getOnlyHistoryEntryOfType(resource, HistoryEntry.Type.DOMAIN_DELETE));
renewBillingEvent, getOnlyHistoryEntryOfType(resource, DOMAIN_DELETE));
// All existing grace periods should be gone, and a new REDEMPTION one should be added.
assertThat(resource.getGracePeriods()).containsExactly(
GracePeriod.create(
@ -377,7 +395,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_autorenewPollMessageIsNotDeleted() throws Exception {
setupSuccessfulTest();
setUpSuccessfulTest();
// Modify the autorenew poll message so that it has unacked messages in the past. This should
// prevent it from being deleted when the domain is deleted.
persistResource(
@ -420,7 +438,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
public void testSuccess_autoRenewGracePeriod_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
setupAutorenewGracePeriod();
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
}
@ -428,14 +446,14 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_autoRenewGracePeriod_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
setupAutorenewGracePeriod();
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
}
@Test
public void testSuccess_autoRenewGracePeriod_v12() throws Exception {
setupAutorenewGracePeriod();
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
}
@ -450,7 +468,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
.setRenewBillingCostTransitions(ImmutableSortedMap.of(
START_OF_TIME, Money.of(USD, 11), TIME_BEFORE_FLOW.minusDays(5), Money.of(USD, 20)))
.build());
setupAutorenewGracePeriod();
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
}
@ -464,7 +482,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
.setRenewBillingCostTransitions(ImmutableSortedMap.of(
START_OF_TIME, Money.of(USD, 11), TIME_BEFORE_FLOW.minusDays(5), Money.of(USD, 20)))
.build());
setupAutorenewGracePeriod();
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
}
@ -477,7 +495,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
.setRenewBillingCostTransitions(ImmutableSortedMap.of(
START_OF_TIME, Money.of(USD, 11), TIME_BEFORE_FLOW.minusDays(5), Money.of(USD, 20)))
.build());
setupAutorenewGracePeriod();
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
}
@ -485,7 +503,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_noPendingTransfer_deletedAndHasNoTransferData() throws Exception {
setClientIdForFlow("TheRegistrar");
setupSuccessfulTest();
setUpSuccessfulTest();
clock.advanceOneMilli();
runFlowAssertResponse(readFile("domain_delete_response_pending.xml"));
DomainResource domain = reloadResourceByForeignKey();
@ -496,7 +514,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_nullAutorenewBillingEvent_isIgnore() throws Exception {
setClientIdForFlow("TheRegistrar");
setupSuccessfulTest();
setUpSuccessfulTest();
ofy().deleteWithoutBackup().key(domain.getAutorenewBillingEvent()).now();
ofy().clearSessionCache();
clock.advanceOneMilli();
@ -509,7 +527,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_pendingTransfer() throws Exception {
setClientIdForFlow("TheRegistrar");
setupSuccessfulTest();
setUpSuccessfulTest();
// Modify the domain we are testing to include a pending transfer.
TransferData oldTransferData =
persistWithPendingTransfer(reloadResourceByForeignKey()).getTransferData();
@ -522,10 +540,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
.hasExactlyStatusValues(StatusValue.INACTIVE, StatusValue.PENDING_DELETE).and()
.hasDeletionTime(clock.nowUtc().plus(Registry.get("tld").getRedemptionGracePeriodLength())
.plus(Registry.get("tld").getPendingDeleteLength())).and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST,
HistoryEntry.Type.DOMAIN_DELETE);
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_TRANSFER_REQUEST, DOMAIN_DELETE);
// All existing grace periods should be gone, and a new REDEMPTION one should be added.
assertThat(domain.getGracePeriods()).containsExactly(
GracePeriod.create(
@ -578,12 +593,12 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testUnlinkingOfResources() throws Exception {
sessionMetadata.setServiceExtensionUris(ImmutableSet.<String>of());
setupSuccessfulTest();
setUpSuccessfulTest();
// Persist the billing event so it can be retrieved for cancellation generation and checking.
BillingEvent.OneTime graceBillingEvent =
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 123)));
// Use a grace period so that the delete is immediate, simplifying the assertions below.
setupGracePeriods(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, graceBillingEvent));
setUpGracePeriods(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, graceBillingEvent));
// Add a nameserver.
HostResource host = persistResource(newHostResource("ns1.example.tld"));
persistResource(loadByForeignKey(
@ -605,12 +620,12 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
assertDnsTasksEnqueued("example.tld");
assertAutorenewClosedAndCancellationCreatedFor(
graceBillingEvent,
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_DELETE));
getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE));
}
@Test
public void testSuccess_deletedSubordinateDomain() throws Exception {
setupSuccessfulTest();
setUpSuccessfulTest();
persistResource(
newHostResource("ns1." + getUniqueIdFromCommand()).asBuilder()
.setSuperordinateDomain(Key.create(reloadResourceByForeignKey()))
@ -625,7 +640,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testFailure_predelegation() throws Exception {
createTld("tld", TldState.PREDELEGATION);
setupSuccessfulTest();
setUpSuccessfulTest();
thrown.expect(BadCommandForRegistryPhaseException.class);
runFlow();
}
@ -633,7 +648,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_superuserPredelegation() throws Exception {
createTld("tld", TldState.PREDELEGATION);
setupSuccessfulTest();
setUpSuccessfulTest();
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("domain_delete_response_pending.xml"));
@ -681,7 +696,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_superuserUnauthorizedClient() throws Exception {
sessionMetadata.setClientId("NewRegistrar");
setupSuccessfulTest();
setUpSuccessfulTest();
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("domain_delete_response_pending.xml"));
@ -689,7 +704,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testFailure_notAuthorizedForTld() throws Exception {
setupSuccessfulTest();
setUpSuccessfulTest();
persistResource(
loadRegistrar("TheRegistrar")
.asBuilder()
@ -701,7 +716,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testSuccess_superuserNotAuthorizedForTld() throws Exception {
setupSuccessfulTest();
setUpSuccessfulTest();
persistResource(
loadRegistrar("TheRegistrar")
.asBuilder()
@ -743,16 +758,14 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
public void testSuccess_metadata() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
setEppInput("domain_delete_metadata.xml");
setupSuccessfulTest();
setUpSuccessfulTest();
clock.advanceOneMilli();
runFlow();
assertAboutDomains().that(reloadResourceByForeignKey())
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE,
HistoryEntry.Type.DOMAIN_DELETE);
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_DELETE);
assertAboutHistoryEntries()
.that(getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_DELETE))
.hasType(HistoryEntry.Type.DOMAIN_DELETE).and()
.that(getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE))
.hasType(DOMAIN_DELETE).and()
.hasMetadataReason("domain-delete-test").and()
.hasMetadataRequestedByRegistrar(false);
}
@ -767,10 +780,149 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
@Test
public void testIcannActivityReportField_getsLogged() throws Exception {
setupSuccessfulTest();
setUpSuccessfulTest();
clock.advanceOneMilli();
runFlow();
assertIcannReportingActivityFieldLogged("srs-dom-delete");
assertTldsFieldLogged("tld");
}
@Test
public void testIcannTransactionRecord_noGrace_entryOutsideMaxGracePeriod() throws Exception {
setUpSuccessfulTest();
setUpGracePeriodDurations();
clock.advanceOneMilli();
earlierHistoryEntry =
persistResource(
earlierHistoryEntry
.asBuilder()
.setType(DOMAIN_CREATE)
.setModificationTime(TIME_BEFORE_FLOW.minusDays(4))
.setDomainTransactionRecords(
ImmutableSet.of(
DomainTransactionRecord.create(
"tld", TIME_BEFORE_FLOW.plusDays(1), NET_ADDS_1_YR, 1)))
.build());
runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should just be the non-grace period delete
assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(
DomainTransactionRecord.create(
"tld", clock.nowUtc().plusHours(3), DELETED_DOMAINS_NOGRACE, 1));
}
@Test
public void testIcannTransactionRecord_noGrace_noAddOrRenewRecords() throws Exception {
setUpSuccessfulTest();
setUpGracePeriodDurations();
clock.advanceOneMilli();
earlierHistoryEntry =
persistResource(
earlierHistoryEntry
.asBuilder()
.setType(DOMAIN_CREATE)
.setModificationTime(TIME_BEFORE_FLOW.minusDays(2))
.setDomainTransactionRecords(
ImmutableSet.of(
// Only add or renew records counts should be cancelled
DomainTransactionRecord.create(
"tld", TIME_BEFORE_FLOW.plusDays(1), RESTORED_DOMAINS, 1)))
.build());
runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should just be the non-grace period delete
assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(
DomainTransactionRecord.create(
"tld", clock.nowUtc().plusHours(3), DELETED_DOMAINS_NOGRACE, 1));
}
/** Verifies that if there's no add grace period, we still cancel out valid renew records */
@Test
public void testIcannTransactionRecord_noGrace_hasRenewRecord() throws Exception {
setUpSuccessfulTest();
setUpGracePeriodDurations();
clock.advanceOneMilli();
DomainTransactionRecord renewRecord =
DomainTransactionRecord.create("tld", TIME_BEFORE_FLOW.plusDays(1), NET_RENEWS_3_YR, 1);
earlierHistoryEntry =
persistResource(
earlierHistoryEntry
.asBuilder()
.setType(DOMAIN_CREATE)
.setModificationTime(TIME_BEFORE_FLOW.minusDays(2))
.setDomainTransactionRecords(ImmutableSet.of(renewRecord))
.build());
runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should be the non-grace period delete, and the renew cancellation record
assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(
DomainTransactionRecord.create(
"tld", clock.nowUtc().plusHours(3), DELETED_DOMAINS_NOGRACE, 1),
renewRecord.asBuilder().setReportAmount(-1).build());
}
@Test
public void testIcannTransactionRecord_inGrace_noRecords() throws Exception {
setUpSuccessfulTest();
setUpGracePeriods(
GracePeriod.create(
GracePeriodStatus.ADD, TIME_BEFORE_FLOW.plusDays(1), "TheRegistrar", null));
setUpGracePeriodDurations();
clock.advanceOneMilli();
earlierHistoryEntry =
persistResource(
earlierHistoryEntry
.asBuilder()
.setType(DOMAIN_CREATE)
.setModificationTime(TIME_BEFORE_FLOW.minusDays(2))
.build());
runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should just be the grace period delete
assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(
DomainTransactionRecord.create("tld", clock.nowUtc(), DELETED_DOMAINS_GRACE, 1));
}
@Test
public void testIcannTransactionRecord_inGrace_multipleRecords() throws Exception {
setUpSuccessfulTest();
setUpGracePeriods(
GracePeriod.create(
GracePeriodStatus.ADD, TIME_BEFORE_FLOW.plusDays(1), "TheRegistrar", null));
setUpGracePeriodDurations();
clock.advanceOneMilli();
earlierHistoryEntry =
persistResource(
earlierHistoryEntry
.asBuilder()
.setType(DOMAIN_CREATE)
.setModificationTime(TIME_BEFORE_FLOW.minusDays(2))
.setDomainTransactionRecords(
ImmutableSet.of(
DomainTransactionRecord.create(
"tld", TIME_BEFORE_FLOW.plusDays(1), NET_ADDS_10_YR, 1)))
.build());
DomainTransactionRecord existingRecord =
DomainTransactionRecord.create("tld", TIME_BEFORE_FLOW.plusDays(2), NET_ADDS_10_YR, 1);
// Create a HistoryEntry with a later modification time
persistResource(
new HistoryEntry.Builder()
.setType(DOMAIN_CREATE)
.setParent(domain)
.setModificationTime(TIME_BEFORE_FLOW.minusDays(1))
.setDomainTransactionRecords(ImmutableSet.of(existingRecord))
.build());
runFlow();
HistoryEntry persistedEntry = getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE);
// Transaction record should be the grace period delete, and the more recent cancellation record
assertThat(persistedEntry.getDomainTransactionRecords())
.containsExactly(
DomainTransactionRecord.create("tld", clock.nowUtc(), DELETED_DOMAINS_GRACE, 1),
// The cancellation record is the same as the original, except with a -1 counter
existingRecord.asBuilder().setReportAmount(-1).build());
}
}