Reflect refunded billing events on deletion in expiration time (#579)

* Reflect refunded billing events on deletion in expiration time

This doesn't make any change at the time of the domain deletion itself, but it
will matter if the domain is then undeleted, because we need to know what
expiration date to restore, and if there were any renew or autorenew charges
that were refunded by the deletion because they were in a grace period, they
shouldn't be coming back during the restore.

* Add tests for new expiration date behavior

* Add handling of add/renew grace period overlap
This commit is contained in:
Ben McIlwain 2020-05-08 21:51:20 -04:00 committed by GitHub
parent 04f429c4d6
commit 7f3dbfb62f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 523 additions and 26 deletions

View file

@ -212,6 +212,28 @@ public final class DomainDeleteFlow implements TransactionalFlow {
builder.setDeletePollMessage(Key.create(deletePollMessage)); builder.setDeletePollMessage(Key.create(deletePollMessage));
} }
// Cancel any grace periods that were still active, and set the expiration time accordingly.
DateTime newExpirationTime = existingDomain.getRegistrationExpirationTime();
for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) {
// No cancellation is written if the grace period was not for a billable event.
if (gracePeriod.hasBillingEvent()) {
entitiesToSave.add(
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId));
if (gracePeriod.getOneTimeBillingEvent() != null) {
// Take the amount of amount of registration time being refunded off the expiration time.
// This can be either add grace periods or renew grace periods.
BillingEvent.OneTime oneTime =
ofy().load().key(gracePeriod.getOneTimeBillingEvent()).now();
newExpirationTime = newExpirationTime.minusYears(oneTime.getPeriodYears());
} else if (gracePeriod.getRecurringBillingEvent() != null) {
// Take 1 year off the registration if in the autorenew grace period (no need to load the
// recurring billing event; all autorenews are for 1 year).
newExpirationTime = newExpirationTime.minusYears(1);
}
}
}
builder.setRegistrationExpirationTime(newExpirationTime);
DomainBase newDomain = builder.build(); DomainBase newDomain = builder.build();
updateForeignKeyIndexDeletionTime(newDomain); updateForeignKeyIndexDeletionTime(newDomain);
handlePendingTransferOnDelete(existingDomain, newDomain, now, historyEntry); handlePendingTransferOnDelete(existingDomain, newDomain, now, historyEntry);
@ -221,14 +243,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
// event and poll message will already have been deleted in // event and poll message will already have been deleted in
// ResourceDeleteFlow since it's listed in serverApproveEntities. // ResourceDeleteFlow since it's listed in serverApproveEntities.
dnsQueue.addDomainRefreshTask(existingDomain.getFullyQualifiedDomainName()); dnsQueue.addDomainRefreshTask(existingDomain.getFullyQualifiedDomainName());
// Cancel any grace periods that were still active.
for (GracePeriod gracePeriod : existingDomain.getGracePeriods()) {
// No cancellation is written if the grace period was not for a billable event.
if (gracePeriod.hasBillingEvent()) {
entitiesToSave.add(
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId));
}
}
entitiesToSave.add(newDomain, historyEntry); entitiesToSave.add(newDomain, historyEntry);
EntityChanges entityChanges = flowCustomLogic.beforeSave( EntityChanges entityChanges = flowCustomLogic.beforeSave(
BeforeSaveParameters.newBuilder() BeforeSaveParameters.newBuilder()

View file

@ -18,6 +18,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.eppoutput.Result.Code.SUCCESS; import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_AND_CLOSE; import static google.registry.model.eppoutput.Result.Code.SUCCESS_AND_CLOSE;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING; 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.registry.Registry.TldState.GENERAL_AVAILABILITY; import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
import static google.registry.model.registry.Registry.TldState.PREDELEGATION; import static google.registry.model.registry.Registry.TldState.PREDELEGATION;
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE; import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
@ -26,6 +27,7 @@ import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds; import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType; import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DomainBaseSubject.assertAboutDomains;
import static google.registry.testing.EppMetricSubject.assertThat; import static google.registry.testing.EppMetricSubject.assertThat;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD; import static org.joda.money.CurrencyUnit.USD;
@ -84,19 +86,311 @@ public class EppLifecycleDomainTest extends EppTestCase {
"domain_create_response.xml", "domain_create_response.xml",
ImmutableMap.of( ImmutableMap.of(
"DOMAIN", "example.tld", "DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00.0Z", "CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00.0Z")); "EXDATE", "2002-06-01T00:02:00Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-07T00:02:00Z")
.hasResponse(
"domain_info_response_inactive.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z",
"UPDATE", "2000-06-06T00:02:00Z"));
// Delete domain example.tld after its add grace period has expired. // Delete domain example.tld after its add grace period has expired.
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld")) assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-07-01T00:02:00Z") .atTime("2000-07-01T00:02:00Z")
.hasResponse("generic_success_action_pending_response.xml"); .hasResponse("generic_success_action_pending_response.xml");
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-07-03T00:02:00Z")
.hasResponse(
"domain_info_response_redemptionperiod_wildcard.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// The exp. date doesn't change because the deletion didn't cancel any charges.
"EXDATE", "2002-06-01T00:02:00Z",
"UPDATE", "2000-07-01T00:02:00Z"));
// Restore the domain. // Restore the domain.
assertThatCommand("domain_update_restore_request.xml") assertThatCommand("domain_update_restore_request.xml")
.atTime("2000-07-01T00:03:00Z") .atTime("2000-07-01T00:03:00Z")
.hasResponse("generic_success_response.xml"); .hasResponse("generic_success_response.xml");
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-07-02T00:03:00Z")
.hasResponse(
"domain_info_response_inactive.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// TODO(mcilwain): The exp. date should be restored back to 2002-06-01T00:02:00Z,
// but this is old behavior of being 1 year after the moment of the restore.
"EXDATE", "2001-07-01T00:03:00Z",
"UPDATE", "2000-07-01T00:03:00Z"));
assertThatLogoutSucceeds();
}
@Test
public void testDomainDeleteRestore_duringAutorenewGracePeriod() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-01T00:02:00Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-07T00:02:00Z")
.hasResponse(
"domain_info_response_inactive.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z",
"UPDATE", "2000-06-06T00:02:00Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2002-06-07T00:02:00Z")
.hasResponse(
"domain_info_response_graceperiod.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// The exp. date has advanced 1 year because of autorenew.
"EXDATE", "2003-06-01T00:02:00Z",
// This is the time of the autorenew.
"UPDATE", "2002-06-01T00:02:00Z",
"GRACEPERIOD", "autoRenewPeriod"));
// Delete domain example.tld during its autorenew grace period.
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2002-07-01T00:02:00Z")
.hasResponse("generic_success_action_pending_response.xml");
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2002-07-03T00:02:00Z")
.hasResponse(
"domain_info_response_redemptionperiod_wildcard.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// The exp. date reverts back to what it was originally because the deletion
// canceled out the autorenew.
"EXDATE", "2002-06-01T00:02:00Z",
"UPDATE", "2002-07-01T00:02:00Z"));
// Restore the domain.
assertThatCommand("domain_update_restore_request.xml")
.atTime("2002-07-05T00:03:00Z")
.hasResponse("generic_success_response.xml");
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2002-07-07T00:03:00Z")
.hasResponse(
"domain_info_response_inactive.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// TODO(mcilwain): The exp. date should be 2003-06-01T00:02:00Z, the same as its
// value prior to the deletion, because the year that was taken off when the
// autorenew was canceled will be re-added in renewal during the restore.
// For now though, the current behavior is 1 year after restore.
"EXDATE", "2003-07-05T00:03:00Z",
"UPDATE", "2002-07-05T00:03:00Z"));
assertThatLogoutSucceeds();
}
@Test
public void testDomainDeleteRestore_duringRenewalGracePeriod() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-01T00:02:00Z")
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-07T00:02:00Z")
.hasResponse(
"domain_info_response_inactive.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z",
"UPDATE", "2000-06-06T00:02:00Z"));
assertThatCommand(
"domain_renew.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXPDATE", "2002-06-01", "YEARS", "3"))
.atTime("2000-06-08T00:00:00Z")
.hasResponse(
"domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2005-06-01T00:02:00Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-10T00:02:00Z")
.hasResponse(
"domain_info_response_graceperiod.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// The exp. date is 5 years in total after the create.
"EXDATE", "2005-06-01T00:02:00Z",
// This is the time of the renew.
"UPDATE", "2000-06-08T00:00:00Z",
"GRACEPERIOD", "renewPeriod"));
// Delete domain example.tld during its renew grace period.
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-12T00:00:00Z")
.hasResponse("generic_success_action_pending_response.xml");
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-13T00:00:00Z")
.hasResponse(
"domain_info_response_redemptionperiod_wildcard.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// The exp. date reverts back to what it was originally because the deletion
// canceled out the 3-year renewal.
"EXDATE", "2002-06-01T00:02:00Z",
"UPDATE", "2000-06-12T00:00:00Z"));
// Restore the domain.
assertThatCommand("domain_update_restore_request.xml")
.atTime("2000-06-20T00:00:00Z")
.hasResponse("generic_success_response.xml");
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-21T00:00:00Z")
.hasResponse(
"domain_info_response_inactive.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// TODO(mcilwain): The exp. date should be 2002-06-01T00:02:00Z, which is the
// current registration expiration time on the (deleted) domain, but for now is
// 1 year after restore.
"EXDATE", "2001-06-20T00:00:00Z",
"UPDATE", "2000-06-20T00:00:00Z"));
assertThatLogoutSucceeds();
}
@Test
public void testDomainDelete_duringAddAndRenewalGracePeriod_deletesImmediately()
throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
createContacts(DateTime.parse("2000-06-01T00:00:00Z"));
DateTime createTime = DateTime.parse("2000-06-01T00:02:00Z");
// Create domain example.tld
assertThatCommand(
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime(createTime)
.hasResponse(
"domain_create_response.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-02T00:02:00Z")
.hasResponse(
"domain_info_response_addperiod_wildcard.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
"EXDATE", "2002-06-01T00:02:00Z"));
DateTime renewTime = DateTime.parse("2000-06-03T00:00:00Z");
assertThatCommand(
"domain_renew.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXPDATE", "2002-06-01", "YEARS", "3"))
.atTime(renewTime)
.hasResponse(
"domain_renew_response.xml",
ImmutableMap.of("DOMAIN", "example.tld", "EXDATE", "2005-06-01T00:02:00Z"));
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-03T03:00:00Z")
.hasResponse(
"domain_info_response_graceperiod_add_and_renew.xml",
ImmutableMap.of(
"DOMAIN", "example.tld",
"CRDATE", "2000-06-01T00:02:00Z",
// The exp. date is 5 years in total after the create.
"EXDATE", "2005-06-01T00:02:00Z",
// This is the time of the renew.
"UPDATE", "2000-06-03T00:00:00Z"));
DomainBase domain =
loadByForeignKey(DomainBase.class, "example.tld", DateTime.parse("2000-06-03T04:00:00Z"))
.get();
DateTime deleteTime = DateTime.parse("2000-06-04T00:00:00Z");
// Delete domain example.tld during both grace periods.
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-04T00:00:00Z")
.hasResponse("generic_success_response.xml");
// Verify that it is immediately non-existent.
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "example.tld"))
.atTime("2000-06-04T00:01:00Z")
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2303", "MSG", "The domain with given ID (example.tld) doesn't exist."));
// The expected one-time billing event, that should have an associated Cancellation.
OneTime oneTimeCreateBillingEvent = makeOneTimeCreateBillingEvent(domain, createTime);
OneTime oneTimeRenewBillingEvent = makeOneTimeRenewBillingEvent(domain, renewTime);
// Verify that the OneTime billing event associated with the domain creation is canceled.
assertBillingEventsForResource(
domain,
// There should be one-time billing events for the create and the renew.
oneTimeCreateBillingEvent,
oneTimeRenewBillingEvent,
// There should be two ended recurring billing events, one each from the create and renew.
// (The former was ended by the renew and the latter was ended by the delete.)
makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), renewTime),
makeRecurringRenewBillingEvent(domain, createTime.plusYears(5), deleteTime),
// There should be Cancellations offsetting both of the one-times.
makeCancellationBillingEventForCreate(
domain, oneTimeCreateBillingEvent, createTime, deleteTime),
makeCancellationBillingEventForRenew(
domain, oneTimeRenewBillingEvent, renewTime, deleteTime));
// Verify that the registration expiration time was set back to the creation time, because the
// entire cost of registration was refunded. We have to do this through the DB instead of EPP
// because domains deleted during the add grace period vanish immediately as far as the world
// outside our system is concerned.
DomainBase deletedDomain = ofy().load().entity(domain).now();
assertAboutDomains().that(deletedDomain).hasRegistrationExpirationTime(createTime);
assertThatLogoutSucceeds(); assertThatLogoutSucceeds();
} }
@ -143,9 +437,16 @@ public class EppLifecycleDomainTest extends EppTestCase {
oneTimeCreateBillingEvent, oneTimeCreateBillingEvent,
makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), deleteTime), makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), deleteTime),
// Check for the existence of a cancellation for the given one-time billing event. // Check for the existence of a cancellation for the given one-time billing event.
makeCancellationBillingEventFor( makeCancellationBillingEventForCreate(
domain, oneTimeCreateBillingEvent, createTime, deleteTime)); domain, oneTimeCreateBillingEvent, createTime, deleteTime));
// Verify that the registration expiration time was set back to the creation time, because the
// entire cost of registration was refunded. We have to do this through the DB instead of EPP
// because domains deleted during the add grace period vanish immediately as far as the world
// outside our system is concerned.
DomainBase deletedDomain = ofy().load().entity(domain).now();
assertAboutDomains().that(deletedDomain).hasRegistrationExpirationTime(createTime);
assertThatLogoutSucceeds(); assertThatLogoutSucceeds();
} }
@ -268,7 +569,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
expectedCreateEapBillingEvent, expectedCreateEapBillingEvent,
makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), deleteTime), makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), deleteTime),
// ... and verify that the create one-time billing event was canceled ... // ... and verify that the create one-time billing event was canceled ...
makeCancellationBillingEventFor( makeCancellationBillingEventForCreate(
domain, expectedOneTimeCreateBillingEvent, createTime, deleteTime)); domain, expectedOneTimeCreateBillingEvent, createTime, deleteTime));
// ... but there was NOT a Cancellation for the EAP fee, as this would fail if additional // ... but there was NOT a Cancellation for the EAP fee, as this would fail if additional
// billing events were present. // billing events were present.

View file

@ -284,20 +284,42 @@ public class EppTestCase extends ShardableTestCase {
.setCost(Money.parse("USD 26.00")) .setCost(Money.parse("USD 26.00"))
.setPeriodYears(2) .setPeriodYears(2)
.setEventTime(createTime) .setEventTime(createTime)
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength())) .setBillingTime(createTime.plus(Registry.get(domain.getTld()).getAddGracePeriodLength()))
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE)) .setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE))
.build(); .build();
} }
/** Makes a one-time billing event corresponding to the given domain's renewal. */
protected static BillingEvent.OneTime makeOneTimeRenewBillingEvent(
DomainBase domain, DateTime renewTime) {
return new BillingEvent.OneTime.Builder()
.setReason(Reason.RENEW)
.setTargetId(domain.getFullyQualifiedDomainName())
.setClientId(domain.getCurrentSponsorClientId())
.setCost(Money.parse("USD 33.00"))
.setPeriodYears(3)
.setEventTime(renewTime)
.setBillingTime(renewTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_RENEW))
.build();
}
/** Makes a recurring billing event corresponding to the given domain's creation. */ /** Makes a recurring billing event corresponding to the given domain's creation. */
protected static BillingEvent.Recurring makeRecurringCreateBillingEvent( protected static BillingEvent.Recurring makeRecurringCreateBillingEvent(
DomainBase domain, DateTime eventTime, DateTime endTime) { DomainBase domain, DateTime eventTime, DateTime endTime) {
return makeRecurringCreateBillingEvent( return makeRecurringBillingEvent(
domain, getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE), eventTime, endTime); domain, getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE), eventTime, endTime);
} }
/** Makes a recurring billing event corresponding to the given domain's renewal. */
protected static BillingEvent.Recurring makeRecurringRenewBillingEvent(
DomainBase domain, DateTime eventTime, DateTime endTime) {
return makeRecurringBillingEvent(
domain, getOnlyHistoryEntryOfType(domain, Type.DOMAIN_RENEW), eventTime, endTime);
}
/** Makes a recurring billing event corresponding to the given history entry. */ /** Makes a recurring billing event corresponding to the given history entry. */
protected static BillingEvent.Recurring makeRecurringCreateBillingEvent( protected static BillingEvent.Recurring makeRecurringBillingEvent(
DomainBase domain, HistoryEntry historyEntry, DateTime eventTime, DateTime endTime) { DomainBase domain, HistoryEntry historyEntry, DateTime eventTime, DateTime endTime) {
return new BillingEvent.Recurring.Builder() return new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW) .setReason(Reason.RENEW)
@ -311,22 +333,33 @@ public class EppTestCase extends ShardableTestCase {
} }
/** Makes a cancellation billing event cancelling out the given domain create billing event. */ /** Makes a cancellation billing event cancelling out the given domain create billing event. */
protected static BillingEvent.Cancellation makeCancellationBillingEventFor( protected static BillingEvent.Cancellation makeCancellationBillingEventForCreate(
DomainBase domain, DomainBase domain, OneTime billingEventToCancel, DateTime createTime, DateTime deleteTime) {
OneTime billingEventToCancel,
DateTime createTime,
DateTime deleteTime) {
return new BillingEvent.Cancellation.Builder() return new BillingEvent.Cancellation.Builder()
.setTargetId(domain.getFullyQualifiedDomainName()) .setTargetId(domain.getFullyQualifiedDomainName())
.setClientId(domain.getCurrentSponsorClientId()) .setClientId(domain.getCurrentSponsorClientId())
.setEventTime(deleteTime) .setEventTime(deleteTime)
.setOneTimeEventKey(findKeyToActualOneTimeBillingEvent(billingEventToCancel)) .setOneTimeEventKey(findKeyToActualOneTimeBillingEvent(billingEventToCancel))
.setBillingTime(createTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength())) .setBillingTime(createTime.plus(Registry.get(domain.getTld()).getAddGracePeriodLength()))
.setReason(Reason.CREATE) .setReason(Reason.CREATE)
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_DELETE)) .setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_DELETE))
.build(); .build();
} }
/** Makes a cancellation billing event cancelling out the given domain renew billing event. */
protected static BillingEvent.Cancellation makeCancellationBillingEventForRenew(
DomainBase domain, OneTime billingEventToCancel, DateTime renewTime, DateTime deleteTime) {
return new BillingEvent.Cancellation.Builder()
.setTargetId(domain.getFullyQualifiedDomainName())
.setClientId(domain.getCurrentSponsorClientId())
.setEventTime(deleteTime)
.setOneTimeEventKey(findKeyToActualOneTimeBillingEvent(billingEventToCancel))
.setBillingTime(renewTime.plus(Registry.get(domain.getTld()).getRenewGracePeriodLength()))
.setReason(Reason.RENEW)
.setParent(getOnlyHistoryEntryOfType(domain, Type.DOMAIN_DELETE))
.build();
}
/** /**
* Finds the Key to the actual one-time create billing event associated with a domain's creation. * Finds the Key to the actual one-time create billing event associated with a domain's creation.
* *

View file

@ -391,7 +391,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
GracePeriod.create(GracePeriodStatus.TRANSFER, TIME_BEFORE_FLOW.plusDays(1), "foo", null)); GracePeriod.create(GracePeriodStatus.TRANSFER, TIME_BEFORE_FLOW.plusDays(1), "foo", null));
// We should see exactly one poll message, which is for the autorenew 1 month in the future. // We should see exactly one poll message, which is for the autorenew 1 month in the future.
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build()); assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
DateTime originalExpirationTime = domain.getRegistrationExpirationTime(); DateTime expectedExpirationTime = domain.getRegistrationExpirationTime().minusYears(2);
clock.advanceOneMilli(); clock.advanceOneMilli();
runFlowAssertResponse(loadFile(responseFilename, substitutions)); runFlowAssertResponse(loadFile(responseFilename, substitutions));
DomainBase resource = reloadResourceByForeignKey(); DomainBase resource = reloadResourceByForeignKey();
@ -413,7 +413,7 @@ public class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow,
// deletion time, that means once it passes the domain will experience a "phantom autorenew" // 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 // where the expirationTime advances and the grace period appears, but since the delete flow
// closed the autorenew recurrences immediately, there are no other autorenew effects. // closed the autorenew recurrences immediately, there are no other autorenew effects.
assertAboutDomains().that(resource).hasRegistrationExpirationTime(originalExpirationTime); assertAboutDomains().that(resource).hasRegistrationExpirationTime(expectedExpirationTime);
// All existing grace periods that were for billable actions should cause cancellations. // All existing grace periods that were for billable actions should cause cancellations.
assertAutorenewClosedAndCancellationCreatedFor( assertAutorenewClosedAndCancellationCreatedFor(
renewBillingEvent, getOnlyHistoryEntryOfType(resource, DOMAIN_DELETE)); renewBillingEvent, getOnlyHistoryEntryOfType(resource, DOMAIN_DELETE));

View file

@ -160,19 +160,19 @@ public class EppLifecycleToolsTest extends EppTestCase {
makeOneTimeCreateBillingEvent(domain, createTime), makeOneTimeCreateBillingEvent(domain, createTime),
renewBillingEvent, renewBillingEvent,
// The initial autorenew billing event, which was closed at the time of the explicit renew. // The initial autorenew billing event, which was closed at the time of the explicit renew.
makeRecurringCreateBillingEvent( makeRecurringBillingEvent(
domain, domain,
getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE), getOnlyHistoryEntryOfType(domain, Type.DOMAIN_CREATE),
createTime.plusYears(2), createTime.plusYears(2),
DateTime.parse("2000-06-07T00:00:00.000Z")), DateTime.parse("2000-06-07T00:00:00.000Z")),
// The renew's autorenew billing event, which was closed at the time of the unrenew. // The renew's autorenew billing event, which was closed at the time of the unrenew.
makeRecurringCreateBillingEvent( makeRecurringBillingEvent(
domain, domain,
getOnlyHistoryEntryOfType(domain, Type.DOMAIN_RENEW), getOnlyHistoryEntryOfType(domain, Type.DOMAIN_RENEW),
DateTime.parse("2006-06-01T00:02:00.000Z"), DateTime.parse("2006-06-01T00:02:00.000Z"),
DateTime.parse("2001-06-07T00:00:00.000Z")), DateTime.parse("2001-06-07T00:00:00.000Z")),
// The remaining active autorenew billing event which was created by the unrenew. // The remaining active autorenew billing event which was created by the unrenew.
makeRecurringCreateBillingEvent( makeRecurringBillingEvent(
domain, domain,
getOnlyHistoryEntryOfType(domain, Type.SYNTHETIC), getOnlyHistoryEntryOfType(domain, Type.SYNTHETIC),
DateTime.parse("2003-06-01T00:02:00.000Z"), DateTime.parse("2003-06-01T00:02:00.000Z"),

View file

@ -0,0 +1,35 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>%CRDATE%</domain:crDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="addPeriod"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,37 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>2000-06-01T00:02:00Z</domain:crDate>
<domain:upID>NewRegistrar</domain:upID>
<domain:upDate>%UPDATE%</domain:upDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="%GRACEPERIOD%"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,38 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>2000-06-01T00:02:00Z</domain:crDate>
<domain:upID>NewRegistrar</domain:upID>
<domain:upDate>%UPDATE%</domain:upDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="renewPeriod"/>
<rgp:rgpStatus s="addPeriod"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,38 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:infData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:roid>8-TLD</domain:roid>
<domain:status s="pendingDelete"/>
<domain:status s="inactive"/>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:clID>NewRegistrar</domain:clID>
<domain:crID>NewRegistrar</domain:crID>
<domain:crDate>%CRDATE%</domain:crDate>
<domain:upID>NewRegistrar</domain:upID>
<domain:upDate>%UPDATE%</domain:upDate>
<domain:exDate>%EXDATE%</domain:exDate>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:infData>
</resData>
<extension>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
<rgp:rgpStatus s="redemptionPeriod"/>
</rgp:infData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>