Don't extend expiration times for deleted domains (#160)

* Don't extend expiration times for deleted domains

* Flip order and add a comment

* oops forgot a period

* Use END_OF_TIME

* Add tests for expiration times of domains with pending transfers

* Add test for transfer during autorenew and clean up other tests

* Clarify tests

* Add domain expiration check in EppLifecycleDomainTest

* Add a comment and format test files
This commit is contained in:
gbrodman 2019-07-16 18:34:21 -04:00 committed by GitHub
parent f472740fb7
commit c7f97231a2
3 changed files with 322 additions and 113 deletions

View file

@ -28,6 +28,7 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.earliestOf;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
@ -376,10 +377,10 @@ public class DomainBase extends EppResource
Optional<DateTime> newLastEppUpdateTime = Optional.empty();
// There is no transfer. Do any necessary autorenews.
// There is no transfer. Do any necessary autorenews for active domains.
Builder builder = asBuilder();
if (isBeforeOrAt(registrationExpirationTime, now)) {
if (isBeforeOrAt(registrationExpirationTime, now) && END_OF_TIME.equals(getDeletionTime())) {
// Autorenew by the number of years between the old expiration time and now.
DateTime lastAutorenewTime = leapSafeAddYears(
registrationExpirationTime,

View file

@ -33,6 +33,7 @@ import static org.joda.money.CurrencyUnit.USD;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import com.google.common.truth.Truth;
import com.google.re2j.Matcher;
import com.google.re2j.Pattern;
import google.registry.model.billing.BillingEvent;
@ -197,6 +198,11 @@ public class EppLifecycleDomainTest extends EppTestCase {
makeRecurringCreateBillingEvent(domain, createTime.plusYears(2), deleteTime));
assertThatLogoutSucceeds();
// Make sure that in the future, the domain expiration is unchanged after deletion
DomainBase clonedDomain = domain.cloneProjectedAtTime(deleteTime.plusYears(5));
Truth.assertThat(clonedDomain.getRegistrationExpirationTime())
.isEqualTo(createTime.plusYears(2));
}
@Test

View file

@ -28,6 +28,7 @@ import static google.registry.testing.DomainBaseSubject.assertAboutDomains;
import static google.registry.testing.JUnitBackports.assertThrows;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@ -61,34 +62,40 @@ import org.junit.Test;
/** Unit tests for {@link DomainBase}. */
public class DomainBaseTest extends EntityTestCase {
DomainBase domain;
private DomainBase domain;
private Key<BillingEvent.OneTime> oneTimeBillKey;
private Key<BillingEvent.Recurring> recurringBillKey;
@Before
public void setUp() {
createTld("com");
Key<DomainBase> domainKey = Key.create(null, DomainBase.class, "4-COM");
Key<HostResource> hostKey = Key.create(persistResource(
new HostResource.Builder()
.setFullyQualifiedHostName("ns1.example.com")
.setSuperordinateDomain(domainKey)
.setRepoId("1-COM")
.build()));
Key<ContactResource> contact1Key = Key.create(persistResource(
new ContactResource.Builder()
.setContactId("contact_id1")
.setRepoId("2-COM")
.build()));
Key<ContactResource> contact2Key = Key.create(persistResource(
new ContactResource.Builder()
.setContactId("contact_id2")
.setRepoId("3-COM")
.build()));
Key<HostResource> hostKey =
Key.create(
persistResource(
new HostResource.Builder()
.setFullyQualifiedHostName("ns1.example.com")
.setSuperordinateDomain(domainKey)
.setRepoId("1-COM")
.build()));
Key<ContactResource> contact1Key =
Key.create(
persistResource(
new ContactResource.Builder()
.setContactId("contact_id1")
.setRepoId("2-COM")
.build()));
Key<ContactResource> contact2Key =
Key.create(
persistResource(
new ContactResource.Builder()
.setContactId("contact_id2")
.setRepoId("3-COM")
.build()));
Key<HistoryEntry> historyEntryKey =
Key.create(persistResource(new HistoryEntry.Builder().setParent(domainKey).build()));
Key<BillingEvent.OneTime> oneTimeBillKey =
Key.create(historyEntryKey, BillingEvent.OneTime.class, 1);
Key<BillingEvent.Recurring> recurringBillKey =
Key.create(historyEntryKey, BillingEvent.Recurring.class, 2);
oneTimeBillKey = Key.create(historyEntryKey, BillingEvent.OneTime.class, 1);
recurringBillKey = Key.create(historyEntryKey, BillingEvent.Recurring.class, 2);
Key<PollMessage.Autorenew> autorenewPollKey =
Key.create(historyEntryKey, PollMessage.Autorenew.class, 3);
Key<PollMessage.OneTime> onetimePollKey =
@ -168,21 +175,27 @@ public class DomainBaseTest extends EntityTestCase {
@Test
public void testEmptyStringsBecomeNull() {
assertThat(newDomainBase("example.com").asBuilder()
.setPersistedCurrentSponsorClientId(null)
.build()
.getCurrentSponsorClientId())
.isNull();
assertThat(newDomainBase("example.com").asBuilder()
.setPersistedCurrentSponsorClientId("")
.build()
assertThat(
newDomainBase("example.com")
.asBuilder()
.setPersistedCurrentSponsorClientId(null)
.build()
.getCurrentSponsorClientId())
.isNull();
assertThat(newDomainBase("example.com").asBuilder()
.setPersistedCurrentSponsorClientId(" ")
.build()
.getCurrentSponsorClientId())
.isNotNull();
.isNull();
assertThat(
newDomainBase("example.com")
.asBuilder()
.setPersistedCurrentSponsorClientId("")
.build()
.getCurrentSponsorClientId())
.isNull();
assertThat(
newDomainBase("example.com")
.asBuilder()
.setPersistedCurrentSponsorClientId(" ")
.build()
.getCurrentSponsorClientId())
.isNotNull();
}
@Test
@ -201,29 +214,49 @@ public class DomainBaseTest extends EntityTestCase {
.build()
.nsHosts)
.isNull();
assertThat(newDomainBase("example.com").asBuilder()
.setNameservers(ImmutableSet.of(Key.create(newHostResource("foo.example.tld"))))
.build().nsHosts)
.isNotNull();
assertThat(
newDomainBase("example.com")
.asBuilder()
.setNameservers(ImmutableSet.of(Key.create(newHostResource("foo.example.tld"))))
.build()
.nsHosts)
.isNotNull();
// This behavior should also hold true for ImmutableObjects nested in collections.
assertThat(newDomainBase("example.com").asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 1, 1, (byte[]) null)))
.build().getDsData().asList().get(0).getDigest())
.isNull();
assertThat(newDomainBase("example.com").asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 1, 1, new byte[]{})))
.build().getDsData().asList().get(0).getDigest())
.isNull();
assertThat(newDomainBase("example.com").asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 1, 1, new byte[]{1})))
.build().getDsData().asList().get(0).getDigest())
.isNotNull();
assertThat(
newDomainBase("example.com")
.asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 1, 1, (byte[]) null)))
.build()
.getDsData()
.asList()
.get(0)
.getDigest())
.isNull();
assertThat(
newDomainBase("example.com")
.asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 1, 1, new byte[] {})))
.build()
.getDsData()
.asList()
.get(0)
.getDigest())
.isNull();
assertThat(
newDomainBase("example.com")
.asBuilder()
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 1, 1, new byte[] {1})))
.build()
.getDsData()
.asList()
.get(0)
.getDigest())
.isNotNull();
}
@Test
public void testEmptyTransferDataBecomesNull() {
DomainBase withNull =
newDomainBase("example.com").asBuilder().setTransferData(null).build();
DomainBase withNull = newDomainBase("example.com").asBuilder().setTransferData(null).build();
DomainBase withEmpty = withNull.asBuilder().setTransferData(TransferData.EMPTY).build();
assertThat(withNull).isEqualTo(withEmpty);
assertThat(withEmpty.transferData).isNull();
@ -241,18 +274,22 @@ public class DomainBaseTest extends EntityTestCase {
StatusValue[] statuses1 = {StatusValue.CLIENT_HOLD};
// If there are other status values, OK should be suppressed. (Domains can't be LINKED.)
assertAboutDomains()
.that(newDomainBase("example.com").asBuilder()
.setNameservers(nameservers)
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_HOLD))
.build())
.that(
newDomainBase("example.com")
.asBuilder()
.setNameservers(nameservers)
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_HOLD))
.build())
.hasExactlyStatusValues(statuses1);
StatusValue[] statuses2 = {StatusValue.CLIENT_HOLD};
// When OK is suppressed, it should be removed even if it was originally there.
assertAboutDomains()
.that(newDomainBase("example.com").asBuilder()
.setNameservers(nameservers)
.setStatusValues(ImmutableSet.of(StatusValue.OK, StatusValue.CLIENT_HOLD))
.build())
.that(
newDomainBase("example.com")
.asBuilder()
.setNameservers(nameservers)
.setStatusValues(ImmutableSet.of(StatusValue.OK, StatusValue.CLIENT_HOLD))
.build())
.hasExactlyStatusValues(statuses2);
StatusValue[] statuses3 = {StatusValue.INACTIVE};
// If there are no nameservers, INACTIVE should be added, which suppresses OK.
@ -262,17 +299,21 @@ public class DomainBaseTest extends EntityTestCase {
StatusValue[] statuses4 = {StatusValue.CLIENT_HOLD, StatusValue.INACTIVE};
// If there are no nameservers but there are status values, INACTIVE should still be added.
assertAboutDomains()
.that(newDomainBase("example.com").asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_HOLD))
.build())
.that(
newDomainBase("example.com")
.asBuilder()
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_HOLD))
.build())
.hasExactlyStatusValues(statuses4);
StatusValue[] statuses5 = {StatusValue.CLIENT_HOLD};
// If there are nameservers, INACTIVE should be removed even if it was originally there.
assertAboutDomains()
.that(newDomainBase("example.com").asBuilder()
.setNameservers(nameservers)
.setStatusValues(ImmutableSet.of(StatusValue.INACTIVE, StatusValue.CLIENT_HOLD))
.build())
.that(
newDomainBase("example.com")
.asBuilder()
.setNameservers(nameservers)
.setStatusValues(ImmutableSet.of(StatusValue.INACTIVE, StatusValue.CLIENT_HOLD))
.build())
.hasExactlyStatusValues(statuses5);
}
@ -290,18 +331,22 @@ public class DomainBaseTest extends EntityTestCase {
private void doExpiredTransferTest(DateTime oldExpirationTime) {
HistoryEntry historyEntry = new HistoryEntry.Builder().setParent(domain).build();
BillingEvent.OneTime transferBillingEvent = persistResource(
new BillingEvent.OneTime.Builder()
.setReason(Reason.TRANSFER)
.setClientId("winner")
.setTargetId("example.com")
.setEventTime(clock.nowUtc())
.setBillingTime(
clock.nowUtc().plusDays(1).plus(Registry.get("com").getTransferGracePeriodLength()))
.setCost(Money.of(USD, 11))
.setPeriodYears(1)
.setParent(historyEntry)
.build());
BillingEvent.OneTime transferBillingEvent =
persistResource(
new BillingEvent.OneTime.Builder()
.setReason(Reason.TRANSFER)
.setClientId("winner")
.setTargetId("example.com")
.setEventTime(clock.nowUtc())
.setBillingTime(
clock
.nowUtc()
.plusDays(1)
.plus(Registry.get("com").getTransferGracePeriodLength()))
.setCost(Money.of(USD, 11))
.setPeriodYears(1)
.setParent(historyEntry)
.build());
domain =
domain
.asBuilder()
@ -329,14 +374,16 @@ public class DomainBaseTest extends EntityTestCase {
domain.getTransferData().getServerApproveAutorenewEvent();
assertTransferred(afterTransfer, newExpirationTime, serverApproveAutorenewEvent);
assertThat(afterTransfer.getGracePeriods())
.containsExactly(GracePeriod.create(
GracePeriodStatus.TRANSFER,
clock.nowUtc().plusDays(1).plus(Registry.get("com").getTransferGracePeriodLength()),
"winner",
Key.create(transferBillingEvent)));
.containsExactly(
GracePeriod.create(
GracePeriodStatus.TRANSFER,
clock.nowUtc().plusDays(1).plus(Registry.get("com").getTransferGracePeriodLength()),
"winner",
Key.create(transferBillingEvent)));
// If we project after the grace period expires all should be the same except the grace period.
DomainBase afterGracePeriod = domain.cloneProjectedAtTime(
clock.nowUtc().plusDays(2).plus(Registry.get("com").getTransferGracePeriodLength()));
DomainBase afterGracePeriod =
domain.cloneProjectedAtTime(
clock.nowUtc().plusDays(2).plus(Registry.get("com").getTransferGracePeriodLength()));
assertTransferred(afterGracePeriod, newExpirationTime, serverApproveAutorenewEvent);
assertThat(afterGracePeriod.getGracePeriods()).isEmpty();
}
@ -439,10 +486,11 @@ public class DomainBaseTest extends EntityTestCase {
@Test
public void testStackedGracePeriods() {
ImmutableList<GracePeriod> gracePeriods = ImmutableList.of(
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(3), "foo", null),
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(2), "bar", null),
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(1), "baz", null));
ImmutableList<GracePeriod> gracePeriods =
ImmutableList.of(
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(3), "foo", null),
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(2), "bar", null),
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(1), "baz", null));
domain = domain.asBuilder().setGracePeriods(ImmutableSet.copyOf(gracePeriods)).build();
for (int i = 1; i < 3; ++i) {
assertThat(domain.cloneProjectedAtTime(clock.nowUtc().plusDays(i)).getGracePeriods())
@ -452,12 +500,14 @@ public class DomainBaseTest extends EntityTestCase {
@Test
public void testGracePeriodsByType() {
ImmutableSet<GracePeriod> addGracePeriods = ImmutableSet.of(
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(3), "foo", null),
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(1), "baz", null));
ImmutableSet<GracePeriod> renewGracePeriods = ImmutableSet.of(
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(3), "foo", null),
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(1), "baz", null));
ImmutableSet<GracePeriod> addGracePeriods =
ImmutableSet.of(
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(3), "foo", null),
GracePeriod.create(GracePeriodStatus.ADD, clock.nowUtc().plusDays(1), "baz", null));
ImmutableSet<GracePeriod> renewGracePeriods =
ImmutableSet.of(
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(3), "foo", null),
GracePeriod.create(GracePeriodStatus.RENEW, clock.nowUtc().plusDays(1), "baz", null));
domain =
domain
.asBuilder()
@ -472,8 +522,7 @@ public class DomainBaseTest extends EntityTestCase {
@Test
public void testRenewalsHappenAtExpiration() {
DomainBase renewed =
domain.cloneProjectedAtTime(domain.getRegistrationExpirationTime());
DomainBase renewed = domain.cloneProjectedAtTime(domain.getRegistrationExpirationTime());
assertThat(renewed.getRegistrationExpirationTime())
.isEqualTo(domain.getRegistrationExpirationTime().plusYears(1));
assertThat(renewed.getLastEppUpdateTime()).isEqualTo(domain.getRegistrationExpirationTime());
@ -490,9 +539,11 @@ public class DomainBaseTest extends EntityTestCase {
@Test
public void testRenewalsDontHappenOnFebruary29() {
domain = domain.asBuilder()
.setRegistrationExpirationTime(DateTime.parse("2004-02-29T22:00:00.0Z"))
.build();
domain =
domain
.asBuilder()
.setRegistrationExpirationTime(DateTime.parse("2004-02-29T22:00:00.0Z"))
.build();
DomainBase renewed =
domain.cloneProjectedAtTime(domain.getRegistrationExpirationTime().plusYears(4));
assertThat(renewed.getRegistrationExpirationTime().getDayOfMonth()).isEqualTo(28);
@ -517,23 +568,24 @@ public class DomainBaseTest extends EntityTestCase {
.put(oldExpirationTime.plusYears(2).plusMillis(1), Money.of(USD, 5))
.build())
.build());
DomainBase renewedThreeTimes =
domain.cloneProjectedAtTime(oldExpirationTime.plusYears(2));
DomainBase renewedThreeTimes = domain.cloneProjectedAtTime(oldExpirationTime.plusYears(2));
assertThat(renewedThreeTimes.getRegistrationExpirationTime())
.isEqualTo(oldExpirationTime.plusYears(3));
assertThat(renewedThreeTimes.getLastEppUpdateTime()).isEqualTo(oldExpirationTime.plusYears(2));
assertThat(renewedThreeTimes.getGracePeriods())
.containsExactly(GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
oldExpirationTime.plusYears(2).plus(
Registry.get("com").getAutoRenewGracePeriodLength()),
renewedThreeTimes.getCurrentSponsorClientId(),
renewedThreeTimes.autorenewBillingEvent));
.containsExactly(
GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
oldExpirationTime
.plusYears(2)
.plus(Registry.get("com").getAutoRenewGracePeriodLength()),
renewedThreeTimes.getCurrentSponsorClientId(),
renewedThreeTimes.autorenewBillingEvent));
}
@Test
public void testToHydratedString_notCircular() {
domain.toHydratedString(); // If there are circular references, this will overflow the stack.
domain.toHydratedString(); // If there are circular references, this will overflow the stack.
}
@Test
@ -557,4 +609,154 @@ public class DomainBaseTest extends EntityTestCase {
.hasMessageThat()
.contains("Domain name must be in puny-coded, lower-case form");
}
@Test
public void testClone_doNotExtendExpirationOnDeletedDomain() {
DateTime now = DateTime.now(UTC);
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(now.minusDays(1))
.setDeletionTime(now.minusDays(10))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE, StatusValue.INACTIVE))
.build());
assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime())
.isEqualTo(now.minusDays(1));
}
@Test
public void testClone_doNotExtendExpirationOnFutureDeletedDomain() {
// if a domain is in pending deletion (StatusValue.PENDING_DELETE), don't extend expiration
DateTime now = DateTime.now(UTC);
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(now.plusDays(1))
.setDeletionTime(now.plusDays(20))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE, StatusValue.INACTIVE))
.build());
assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime())
.isEqualTo(now.plusDays(1));
}
@Test
public void testClone_extendsExpirationForExpiredTransferredDomain() {
// If the transfer implicitly succeeded, the expiration time should be extended
DateTime now = DateTime.now(UTC);
DateTime transferExpirationTime = now.minusDays(1);
DateTime previousExpiration = now.minusDays(2);
TransferData transferData =
new TransferData.Builder()
.setPendingTransferExpirationTime(transferExpirationTime)
.setTransferStatus(TransferStatus.PENDING)
.setGainingClientId("TheRegistrar")
.build();
Period extensionPeriod = transferData.getTransferPeriod();
DateTime newExpiration = previousExpiration.plusYears(extensionPeriod.getValue());
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(previousExpiration)
.setTransferData(transferData)
.build());
assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime())
.isEqualTo(newExpiration);
}
@Test
public void testClone_extendsExpirationForNonExpiredTransferredDomain() {
// If the transfer implicitly succeeded, the expiration time should be extended even if it
// hadn't already expired
DateTime now = DateTime.now(UTC);
DateTime transferExpirationTime = now.minusDays(1);
DateTime previousExpiration = now.plusWeeks(2);
TransferData transferData =
new TransferData.Builder()
.setPendingTransferExpirationTime(transferExpirationTime)
.setTransferStatus(TransferStatus.PENDING)
.setGainingClientId("TheRegistrar")
.build();
Period extensionPeriod = transferData.getTransferPeriod();
DateTime newExpiration = previousExpiration.plusYears(extensionPeriod.getValue());
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(previousExpiration)
.setTransferData(transferData)
.build());
assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime())
.isEqualTo(newExpiration);
}
@Test
public void testClone_doesNotExtendExpirationForPendingTransfer() {
// Pending transfers shouldn't affect the expiration time
DateTime now = DateTime.now(UTC);
DateTime transferExpirationTime = now.plusDays(1);
DateTime previousExpiration = now.plusWeeks(2);
TransferData transferData =
new TransferData.Builder()
.setPendingTransferExpirationTime(transferExpirationTime)
.setTransferStatus(TransferStatus.PENDING)
.setGainingClientId("TheRegistrar")
.build();
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(previousExpiration)
.setTransferData(transferData)
.build());
assertThat(domain.cloneProjectedAtTime(now).getRegistrationExpirationTime())
.isEqualTo(previousExpiration);
}
@Test
public void testClone_transferDuringAutorenew() {
// When the domain is an an autorenew grace period, we should not extend the registration
// expiration by a further year--it should just be whatever the autorenew was
DateTime now = DateTime.now(UTC);
DateTime transferExpirationTime = now.minusDays(1);
DateTime previousExpiration = now.minusDays(2);
TransferData transferData =
new TransferData.Builder()
.setPendingTransferExpirationTime(transferExpirationTime)
.setTransferStatus(TransferStatus.PENDING)
.setGainingClientId("TheRegistrar")
.setServerApproveAutorenewEvent(recurringBillKey)
.setServerApproveBillingEvent(oneTimeBillKey)
.build();
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(previousExpiration)
.setGracePeriods(
ImmutableSet.of(
GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
now.plusDays(1),
"NewRegistrar",
recurringBillKey)))
.setTransferData(transferData)
.setAutorenewBillingEvent(recurringBillKey)
.build());
DomainBase clone = domain.cloneProjectedAtTime(now);
assertThat(clone.getRegistrationExpirationTime())
.isEqualTo(domain.getRegistrationExpirationTime().plusYears(1));
// Transferring removes the AUTORENEW grace period and adds a TRANSFER grace period
assertThat(getOnlyElement(clone.getGracePeriods()).getType())
.isEqualTo(GracePeriodStatus.TRANSFER);
}
}