From b8e9d56aecfb9fb197da29daac9557e7f5185d2e Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Tue, 25 Aug 2020 17:24:09 -0400 Subject: [PATCH] Add --autorenews parameter to `nomulus update_domain` tool (#772) --- .../flows/domain/DomainUpdateFlow.java | 13 +- .../DomainDeleteSuperuserExtension.java | 1 + ...mainTransferRequestSuperuserExtension.java | 1 + .../DomainUpdateSuperuserExtension.java | 35 +++ .../registry/model/eppinput/EppInput.java | 96 +++---- .../registry/tools/UpdateDomainCommand.java | 90 +++++-- .../java/google/registry/xjc/XjcObject.java | 4 - .../google/registry/xml/xsd/superuser.xsd | 8 + .../registry/tools/soy/DomainUpdate.soy | 72 +++--- .../domain/DomainTransferRequestFlowTest.java | 5 +- .../flows/domain/DomainUpdateFlowTest.java | 240 ++++++++++-------- .../testing/AbstractEppResourceSubject.java | 12 + .../registry/testing/DomainBaseSubject.java | 10 + .../registry/tools/CommandTestCase.java | 4 +- .../tools/CountDomainsCommandTest.java | 12 +- .../tools/UpdateDomainCommandTest.java | 84 +++++- .../BackfillRegistryLocksCommandTest.java | 7 +- .../domain_update_superuser_extension.xml | 17 ++ .../server/domain_update_set_autorenew.xml | 17 ++ 19 files changed, 498 insertions(+), 230 deletions(-) create mode 100644 core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java create mode 100644 core/src/test/resources/google/registry/flows/domain/domain_update_superuser_extension.xml create mode 100644 core/src/test/resources/google/registry/tools/server/domain_update_set_autorenew.xml diff --git a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java index 5c9be0f87..e55153d3d 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -68,6 +68,7 @@ import google.registry.model.domain.DomainCommand.Update.Change; import google.registry.model.domain.fee.FeeUpdateCommandExtension; import google.registry.model.domain.metadata.MetadataExtension; import google.registry.model.domain.secdns.SecDnsUpdateExtension; +import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.EppInput; @@ -152,7 +153,8 @@ public final class DomainUpdateFlow implements TransactionalFlow { extensionManager.register( FeeUpdateCommandExtension.class, MetadataExtension.class, - SecDnsUpdateExtension.class); + SecDnsUpdateExtension.class, + DomainUpdateSuperuserExtension.class); flowCustomLogic.beforeValidation(); extensionManager.validate(); validateClientIsLoggedIn(clientId); @@ -251,6 +253,15 @@ public final class DomainUpdateFlow implements TransactionalFlow { .removeContacts(remove.getContacts()) .setRegistrant(firstNonNull(change.getRegistrant(), domain.getRegistrant())) .setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo())); + Optional superuserExt = + eppInput.getSingleExtension(DomainUpdateSuperuserExtension.class); + if (superuserExt.isPresent()) { + if (superuserExt.get().getAutorenews().isPresent()) { + boolean autorenews = superuserExt.get().getAutorenews().get(); + domainBuilder.setAutorenewEndTime( + Optional.ofNullable(autorenews ? null : domain.getRegistrationExpirationTime())); + } + } return domainBuilder.build(); } diff --git a/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java b/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java index 48d69313d..ae8f17a68 100644 --- a/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java +++ b/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java @@ -20,6 +20,7 @@ import javax.xml.bind.annotation.XmlRootElement; /** A superuser extension that may be present on domain delete commands. */ @XmlRootElement(name = "domainDelete") public class DomainDeleteSuperuserExtension extends SuperuserExtension { + @XmlElement(name = "redemptionGracePeriodDays") int redemptionGracePeriodDays; diff --git a/core/src/main/java/google/registry/model/domain/superuser/DomainTransferRequestSuperuserExtension.java b/core/src/main/java/google/registry/model/domain/superuser/DomainTransferRequestSuperuserExtension.java index bd188d563..93f55fe32 100644 --- a/core/src/main/java/google/registry/model/domain/superuser/DomainTransferRequestSuperuserExtension.java +++ b/core/src/main/java/google/registry/model/domain/superuser/DomainTransferRequestSuperuserExtension.java @@ -21,6 +21,7 @@ import javax.xml.bind.annotation.XmlRootElement; /** A superuser extension that may be present on domain transfer request commands. */ @XmlRootElement(name = "domainTransferRequest") public class DomainTransferRequestSuperuserExtension extends SuperuserExtension { + // We need to specify the period here because the transfer object's period cannot be set to zero. @XmlElement(name = "renewalPeriod") Period renewalPeriod; diff --git a/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java b/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java new file mode 100644 index 000000000..7c0ab6803 --- /dev/null +++ b/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java @@ -0,0 +1,35 @@ +// Copyright 2020 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.model.domain.superuser; + +import static com.google.common.base.Strings.isNullOrEmpty; + +import java.util.Optional; +import javax.annotation.Nullable; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** A superuser extension that may be present on domain update commands. */ +@XmlRootElement(name = "domainUpdate") +public class DomainUpdateSuperuserExtension extends SuperuserExtension { + + @XmlElement(name = "autorenews") + @Nullable + String autorenews; + + public Optional getAutorenews() { + return Optional.ofNullable(isNullOrEmpty(autorenews) ? null : Boolean.valueOf(autorenews)); + } +} diff --git a/core/src/main/java/google/registry/model/eppinput/EppInput.java b/core/src/main/java/google/registry/model/eppinput/EppInput.java index ce542e9a3..de0dc456c 100644 --- a/core/src/main/java/google/registry/model/eppinput/EppInput.java +++ b/core/src/main/java/google/registry/model/eppinput/EppInput.java @@ -50,6 +50,7 @@ import google.registry.model.domain.secdns.SecDnsCreateExtension; import google.registry.model.domain.secdns.SecDnsUpdateExtension; import google.registry.model.domain.superuser.DomainDeleteSuperuserExtension; import google.registry.model.domain.superuser.DomainTransferRequestSuperuserExtension; +import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension; import google.registry.model.domain.token.AllocationTokenExtension; import google.registry.model.eppinput.ResourceCommand.ResourceCheck; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; @@ -309,53 +310,62 @@ public class EppInput extends ImmutableObject { @XmlType(propOrder = {"command", "extension", "clTRID"}) public static class CommandWrapper extends ImmutableObject { @XmlElements({ - @XmlElement(name = "check", type = Check.class), - @XmlElement(name = "create", type = Create.class), - @XmlElement(name = "delete", type = Delete.class), - @XmlElement(name = "info", type = Info.class), - @XmlElement(name = "login", type = Login.class), - @XmlElement(name = "logout", type = Logout.class), - @XmlElement(name = "poll", type = Poll.class), - @XmlElement(name = "renew", type = Renew.class), - @XmlElement(name = "transfer", type = Transfer.class), - @XmlElement(name = "update", type = Update.class) }) + @XmlElement(name = "check", type = Check.class), + @XmlElement(name = "create", type = Create.class), + @XmlElement(name = "delete", type = Delete.class), + @XmlElement(name = "info", type = Info.class), + @XmlElement(name = "login", type = Login.class), + @XmlElement(name = "logout", type = Logout.class), + @XmlElement(name = "poll", type = Poll.class), + @XmlElement(name = "renew", type = Renew.class), + @XmlElement(name = "transfer", type = Transfer.class), + @XmlElement(name = "update", type = Update.class) + }) InnerCommand command; /** Zero or more command extensions. */ @XmlElementRefs({ - // allocation token extension - @XmlElementRef(type = AllocationTokenExtension.class), - // fee extension version 0.6 - @XmlElementRef(type = FeeCheckCommandExtensionV06.class), - @XmlElementRef(type = FeeInfoCommandExtensionV06.class), - @XmlElementRef(type = FeeCreateCommandExtensionV06.class), - @XmlElementRef(type = FeeRenewCommandExtensionV06.class), - @XmlElementRef(type = FeeTransferCommandExtensionV06.class), - @XmlElementRef(type = FeeUpdateCommandExtensionV06.class), - // fee extension version 0.11 - @XmlElementRef(type = FeeCheckCommandExtensionV11.class), - @XmlElementRef(type = FeeCreateCommandExtensionV11.class), - @XmlElementRef(type = FeeRenewCommandExtensionV11.class), - @XmlElementRef(type = FeeTransferCommandExtensionV11.class), - @XmlElementRef(type = FeeUpdateCommandExtensionV11.class), - // fee extension version 0.12 - @XmlElementRef(type = FeeCheckCommandExtensionV12.class), - @XmlElementRef(type = FeeCreateCommandExtensionV12.class), - @XmlElementRef(type = FeeRenewCommandExtensionV12.class), - @XmlElementRef(type = FeeTransferCommandExtensionV12.class), - @XmlElementRef(type = FeeUpdateCommandExtensionV12.class), - // other extensions - @XmlElementRef(type = LaunchCheckExtension.class), - @XmlElementRef(type = LaunchCreateExtension.class), - @XmlElementRef(type = LaunchDeleteExtension.class), - @XmlElementRef(type = LaunchInfoExtension.class), - @XmlElementRef(type = LaunchUpdateExtension.class), - @XmlElementRef(type = MetadataExtension.class), - @XmlElementRef(type = RgpUpdateExtension.class), - @XmlElementRef(type = SecDnsCreateExtension.class), - @XmlElementRef(type = SecDnsUpdateExtension.class), - @XmlElementRef(type = DomainTransferRequestSuperuserExtension.class), - @XmlElementRef(type = DomainDeleteSuperuserExtension.class) }) + // Fee extension version 0.6 + @XmlElementRef(type = FeeCheckCommandExtensionV06.class), + @XmlElementRef(type = FeeInfoCommandExtensionV06.class), + @XmlElementRef(type = FeeCreateCommandExtensionV06.class), + @XmlElementRef(type = FeeRenewCommandExtensionV06.class), + @XmlElementRef(type = FeeTransferCommandExtensionV06.class), + @XmlElementRef(type = FeeUpdateCommandExtensionV06.class), + + // Fee extension version 0.11 + @XmlElementRef(type = FeeCheckCommandExtensionV11.class), + @XmlElementRef(type = FeeCreateCommandExtensionV11.class), + @XmlElementRef(type = FeeRenewCommandExtensionV11.class), + @XmlElementRef(type = FeeTransferCommandExtensionV11.class), + @XmlElementRef(type = FeeUpdateCommandExtensionV11.class), + + // Fee extension version 0.12 + @XmlElementRef(type = FeeCheckCommandExtensionV12.class), + @XmlElementRef(type = FeeCreateCommandExtensionV12.class), + @XmlElementRef(type = FeeRenewCommandExtensionV12.class), + @XmlElementRef(type = FeeTransferCommandExtensionV12.class), + @XmlElementRef(type = FeeUpdateCommandExtensionV12.class), + + // Launch phase extensions + @XmlElementRef(type = LaunchCheckExtension.class), + @XmlElementRef(type = LaunchCreateExtension.class), + @XmlElementRef(type = LaunchDeleteExtension.class), + @XmlElementRef(type = LaunchInfoExtension.class), + @XmlElementRef(type = LaunchUpdateExtension.class), + + // Superuser extensions + @XmlElementRef(type = DomainDeleteSuperuserExtension.class), + @XmlElementRef(type = DomainTransferRequestSuperuserExtension.class), + @XmlElementRef(type = DomainUpdateSuperuserExtension.class), + + // Other extensions + @XmlElementRef(type = AllocationTokenExtension.class), + @XmlElementRef(type = MetadataExtension.class), + @XmlElementRef(type = RgpUpdateExtension.class), + @XmlElementRef(type = SecDnsCreateExtension.class), + @XmlElementRef(type = SecDnsUpdateExtension.class) + }) @XmlElementWrapper List extension; diff --git a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java index 52834e107..73dd3a270 100644 --- a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java @@ -17,10 +17,11 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static google.registry.model.EppResourceUtils.loadByForeignKey; +import static google.registry.model.domain.rgp.GracePeriodStatus.AUTO_RENEW; import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.util.PreconditionsUtils.checkArgumentPresent; -import static org.joda.time.DateTimeZone.UTC; +import static java.util.function.Predicate.isEqual; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -31,15 +32,19 @@ import com.google.common.flogger.FluentLogger; import com.google.template.soy.data.SoyMapData; import google.registry.model.domain.DesignatedContact; import google.registry.model.domain.DomainBase; +import google.registry.model.domain.GracePeriodBase; import google.registry.model.eppcommon.StatusValue; import google.registry.tools.params.NameserversParameter; import google.registry.tools.soy.DomainUpdateSoyInfo; +import google.registry.util.Clock; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.TreeSet; +import javax.annotation.Nullable; +import javax.inject.Inject; import org.joda.time.DateTime; /** A command to update a new domain via EPP. */ @@ -48,6 +53,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + @Inject Clock clock; + @Parameter(names = "--statuses", description = "Comma-separated list of statuses to set.") private List statuses = new ArrayList<>(); @@ -123,6 +130,15 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { ) boolean clearDsRecords = false; + @Nullable + @Parameter( + names = "--autorenews", + arity = 1, + description = + "Whether the domain autorenews. If false, the domain will automatically be" + + " deleted at the end of its current registration period.") + Boolean autorenews; + @Override protected void initMutatingEppToolCommand() { if (!nameservers.isEmpty()) { @@ -159,7 +175,18 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { clearDsRecords = true; } + ImmutableSet.Builder autorenewGracePeriodWarningDomains = new ImmutableSet.Builder<>(); + DateTime now = clock.nowUtc(); for (String domain : domains) { + Optional domainOptional = loadByForeignKey(DomainBase.class, domain, now); + checkArgumentPresent(domainOptional, "Domain '%s' does not exist or is deleted", domain); + DomainBase domainBase = domainOptional.get(); + checkArgument( + !domainBase.getStatusValues().contains(SERVER_UPDATE_PROHIBITED), + "The domain '%s' has status SERVER_UPDATE_PROHIBITED. Verify that you are allowed " + + "to make updates, and if so, use the domain_unlock command to enable updates.", + domain); + // Use TreeSets so that the results are always in the same order (this makes testing easier). Set addAdminsThisDomain = new TreeSet<>(addAdmins); Set removeAdminsThisDomain = new TreeSet<>(removeAdmins); @@ -171,16 +198,6 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { Set removeStatusesThisDomain = new TreeSet<>(removeStatuses); if (!nameservers.isEmpty() || !admins.isEmpty() || !techs.isEmpty() || !statuses.isEmpty()) { - DateTime now = DateTime.now(UTC); - Optional domainOptional = - loadByForeignKey(DomainBase.class, domain, now); - checkArgumentPresent(domainOptional, "Domain '%s' does not exist or is deleted", domain); - DomainBase domainBase = domainOptional.get(); - checkArgument( - !domainBase.getStatusValues().contains(SERVER_UPDATE_PROHIBITED), - "The domain '%s' has status SERVER_UPDATE_PROHIBITED. Verify that you are allowed " - + "to make updates, and if so, use the domain_unlock command to enable updates.", - domain); if (!nameservers.isEmpty()) { ImmutableSortedSet existingNameservers = domainBase.loadNameserverHostNames(); populateAddRemoveLists( @@ -232,33 +249,41 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { } boolean add = - !addNameserversThisDomain.isEmpty() + (!addNameserversThisDomain.isEmpty() || !addAdminsThisDomain.isEmpty() || !addTechsThisDomain.isEmpty() - || !addStatusesThisDomain.isEmpty(); + || !addStatusesThisDomain.isEmpty()); boolean remove = - !removeNameserversThisDomain.isEmpty() + (!removeNameserversThisDomain.isEmpty() || !removeAdminsThisDomain.isEmpty() || !removeTechsThisDomain.isEmpty() - || !removeStatusesThisDomain.isEmpty(); + || !removeStatusesThisDomain.isEmpty()); - boolean change = registrant != null || password != null; - - boolean secdns = - !addDsRecords.isEmpty() + boolean change = (registrant != null || password != null); + boolean secDns = + (!addDsRecords.isEmpty() || !removeDsRecords.isEmpty() || !dsRecords.isEmpty() - || clearDsRecords; + || clearDsRecords); - if (!add && !remove && !change && !secdns) { + if (!add && !remove && !change && !secDns && autorenews == null) { logger.atInfo().log("No changes need to be made to domain %s", domain); continue; } + // If autorenew is being turned off and this domain is already in the autorenew grace period, + // then we want to warn the user that they might want to delete it instead. + if (Boolean.FALSE.equals(autorenews)) { + if (domainBase.getGracePeriods().stream() + .map(GracePeriodBase::getType) + .anyMatch(isEqual(AUTO_RENEW))) { + autorenewGracePeriodWarningDomains.add(domain); + } + } + setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE); - addSoyRecord( - clientId, + SoyMapData soyMapData = new SoyMapData( "domain", domain, "add", add, @@ -274,14 +299,27 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { "change", change, "registrant", registrant, "password", password, - "secdns", secdns, + "secdns", secDns, "addDsRecords", DsRecord.convertToSoy(addDsRecords), "removeDsRecords", DsRecord.convertToSoy(removeDsRecords), - "removeAllDsRecords", clearDsRecords)); + "removeAllDsRecords", clearDsRecords); + if (autorenews != null) { + soyMapData.put("autorenews", autorenews.toString()); + } + addSoyRecord(clientId, soyMapData); + } + + ImmutableSet domainsToWarn = autorenewGracePeriodWarningDomains.build(); + if (!domainsToWarn.isEmpty()) { + logger.atWarning().log( + "The following domains are in autorenew grace periods. Consider aborting this command" + + " and running `nomulus delete_domain` instead to terminate autorenewal immediately" + + " rather than in one year, if desired:\n%s", + String.join(", ", domainsToWarn)); } } - protected void populateAddRemoveLists( + private void populateAddRemoveLists( Set targetSet, Set oldSet, Set addSet, Set removeSet) { addSet.addAll(Sets.difference(targetSet, oldSet)); removeSet.addAll(Sets.difference(oldSet, targetSet)); diff --git a/core/src/main/java/google/registry/xjc/XjcObject.java b/core/src/main/java/google/registry/xjc/XjcObject.java index 33a28271d..e8878ec64 100644 --- a/core/src/main/java/google/registry/xjc/XjcObject.java +++ b/core/src/main/java/google/registry/xjc/XjcObject.java @@ -42,10 +42,6 @@ public abstract class XjcObject { XjcXmlTransformer.marshalStrict(this, out, encoding); } - public void marshalLenient(OutputStream out, Charset encoding) throws XmlException { - XjcXmlTransformer.marshalLenient(this, out, encoding); - } - /** * Turns object into a formatted XML string by any means necessary. * diff --git a/core/src/main/java/google/registry/xml/xsd/superuser.xsd b/core/src/main/java/google/registry/xml/xsd/superuser.xsd index 369a5746d..4d00dbb27 100644 --- a/core/src/main/java/google/registry/xml/xsd/superuser.xsd +++ b/core/src/main/java/google/registry/xml/xsd/superuser.xsd @@ -47,4 +47,12 @@ + + + + + + + + diff --git a/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy b/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy index 825a30d30..62c984442 100644 --- a/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy +++ b/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy @@ -35,6 +35,7 @@ {@param addDsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} {@param removeDsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} {@param removeAllDsRecords: bool} + {@param? autorenews: string} @@ -96,39 +97,46 @@ {/if} - {if $secdns} + {if $secdns or $autorenews} - - {if $removeAllDsRecords} - - true - - {/if} - {if length($removeDsRecords) > 0} - - {for $dsRecord in $removeDsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - {if length($addDsRecords) > 0} - - {for $dsRecord in $addDsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - + {if $secdns} + + {if $removeAllDsRecords} + + true + + {/if} + {if length($removeDsRecords) > 0} + + {for $dsRecord in $removeDsRecords} + + {$dsRecord.keyTag} + {$dsRecord.alg} + {$dsRecord.digestType} + {$dsRecord.digest} + + {/for} + + {/if} + {if length($addDsRecords) > 0} + + {for $dsRecord in $addDsRecords} + + {$dsRecord.keyTag} + {$dsRecord.alg} + {$dsRecord.digestType} + {$dsRecord.digest} + + {/for} + + {/if} + + {/if} + {if $autorenews} + + {$autorenews} + + {/if} {/if} RegistryTool diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java index 57ed05365..efc49e0ba 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java @@ -572,6 +572,7 @@ class DomainTransferRequestFlowTest Duration expectedAutomaticTransferLength, BillingEvent.Cancellation.Builder... extraExpectedBillingEvents) throws Exception { + eppRequestSource = EppRequestSource.TOOL; setEppInput(commandFilename, substitutions); ImmutableSet originalGracePeriods = domain.getGracePeriods(); // Replace the ROID in the xml file with the one generated in our test. @@ -903,7 +904,6 @@ class DomainTransferRequestFlowTest @Test void testSuccess_superuserExtension_zeroPeriod_nonZeroAutomaticTransferLength() throws Exception { setupDomain("example", "tld"); - eppRequestSource = EppRequestSource.TOOL; clock.advanceOneMilli(); doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", @@ -918,7 +918,6 @@ class DomainTransferRequestFlowTest @Test void testSuccess_superuserExtension_zeroPeriod_zeroAutomaticTransferLength() throws Exception { setupDomain("example", "tld"); - eppRequestSource = EppRequestSource.TOOL; clock.advanceOneMilli(); doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", @@ -934,7 +933,6 @@ class DomainTransferRequestFlowTest void testSuccess_superuserExtension_nonZeroPeriod_nonZeroAutomaticTransferLength() throws Exception { setupDomain("example", "tld"); - eppRequestSource = EppRequestSource.TOOL; clock.advanceOneMilli(); doSuccessfulSuperuserExtensionTest( "domain_transfer_request_superuser_extension.xml", @@ -948,7 +946,6 @@ class DomainTransferRequestFlowTest @Test void testSuccess_superuserExtension_zeroPeriod_autorenewGraceActive() throws Exception { - eppRequestSource = EppRequestSource.TOOL; setupDomain("example", "tld"); VKey existingAutorenewEvent = domain.getAutorenewBillingEvent(); // Set domain to have auto-renewed just before the transfer request, so that it will have an diff --git a/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java index c0baae2ad..1668b4989 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainUpdateFlowTest.java @@ -89,12 +89,14 @@ import google.registry.model.host.HostResource; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; import google.registry.persistence.VKey; +import java.util.Optional; import org.joda.money.Money; +import org.joda.time.DateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** Unit tests for {@link DomainUpdateFlow}. */ -public class DomainUpdateFlowTest extends ResourceFlowTestCase { +class DomainUpdateFlowTest extends ResourceFlowTestCase { private static final DelegationSignerData SOME_DSDATA = DelegationSignerData.create(1, 2, 3, base16().decode("0123")); @@ -105,13 +107,12 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase builder = new ImmutableSet.Builder<>(); for (int i = 0; i < 7; ++i) { builder.add(DelegationSignerData.create(i, 2, 3, new byte[] {0, 1, 2})); @@ -573,7 +574,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase builder = new ImmutableSet.Builder<>(); for (int i = 0; i < 7; ++i) { builder.add(DelegationSignerData.create(i, 2, 3, new byte[] {0, 1, 2})); @@ -630,7 +631,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase builder = new ImmutableSet.Builder<>(); for (int i = 0; i < 8; ++i) { builder.add(DelegationSignerData.create(i, 2, 3, new byte[] {0, 1, 2})); @@ -794,7 +795,7 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase And hasValue(E expected, Optional actual, String name) { + check(name).about(optionals()).that(actual).hasValue(expected); + return andChainer(); + } + + protected And hasNoValue(Optional actual, String name) { + check(name).about(optionals()).that(actual).isEmpty(); + return andChainer(); + } + protected And doesNotHaveValue(E badValue, E actual, String name) { check(name).that(actual).isNotEqualTo(badValue); return andChainer(); diff --git a/core/src/test/java/google/registry/testing/DomainBaseSubject.java b/core/src/test/java/google/registry/testing/DomainBaseSubject.java index 35198e0ef..d4a73a314 100644 --- a/core/src/test/java/google/registry/testing/DomainBaseSubject.java +++ b/core/src/test/java/google/registry/testing/DomainBaseSubject.java @@ -17,6 +17,7 @@ package google.registry.testing; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.truth.Fact.simpleFact; import static com.google.common.truth.Truth.assertAbout; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import com.google.common.collect.ImmutableSet; import com.google.common.truth.FailureMetadata; @@ -102,6 +103,15 @@ public final class DomainBaseSubject return hasValue(smdId, actual.getSmdId(), "getSmdId()"); } + public And hasAutorenewEndTime(DateTime autorenewEndTime) { + checkArgumentNotNull(autorenewEndTime, "Use hasNoAutorenewEndTime() instead"); + return hasValue(autorenewEndTime, actual.getAutorenewEndTime(), "getAutorenewEndTime()"); + } + + public And hasNoAutorenewEndTime() { + return hasNoValue(actual.getAutorenewEndTime(), "getAutorenewEndTime()"); + } + public static SimpleSubjectBuilder assertAboutDomains() { return assertAbout(DomainBaseSubject::new); } diff --git a/core/src/test/java/google/registry/tools/CommandTestCase.java b/core/src/test/java/google/registry/tools/CommandTestCase.java index 88ae2deb8..6a173d1b6 100644 --- a/core/src/test/java/google/registry/tools/CommandTestCase.java +++ b/core/src/test/java/google/registry/tools/CommandTestCase.java @@ -19,6 +19,7 @@ import static com.google.common.collect.Iterables.toArray; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.ofy.ObjectifyService.ofy; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.JCommander; import com.google.common.base.Joiner; @@ -41,6 +42,7 @@ import java.io.PrintStream; import java.nio.file.Path; import java.util.List; import java.util.concurrent.locks.ReentrantLock; +import org.joda.time.DateTime; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; @@ -66,7 +68,7 @@ public abstract class CommandTestCase { protected C command; - public final FakeClock fakeClock = new FakeClock(); + protected final FakeClock fakeClock = new FakeClock(DateTime.now(UTC)); @RegisterExtension public final AppEngineExtension appEngine = diff --git a/core/src/test/java/google/registry/tools/CountDomainsCommandTest.java b/core/src/test/java/google/registry/tools/CountDomainsCommandTest.java index 8b38927f2..5ea7bed64 100644 --- a/core/src/test/java/google/registry/tools/CountDomainsCommandTest.java +++ b/core/src/test/java/google/registry/tools/CountDomainsCommandTest.java @@ -17,12 +17,9 @@ package google.registry.tools; import static google.registry.testing.DatastoreHelper.createTlds; import static google.registry.testing.DatastoreHelper.persistActiveDomain; import static google.registry.testing.DatastoreHelper.persistDeletedDomain; -import static org.joda.time.DateTimeZone.UTC; import google.registry.model.ofy.Ofy; -import google.registry.testing.FakeClock; import google.registry.testing.InjectExtension; -import org.joda.time.DateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -30,14 +27,12 @@ import org.junit.jupiter.api.extension.RegisterExtension; /** Unit tests for {@link CountDomainsCommand}. */ public class CountDomainsCommandTest extends CommandTestCase { - protected FakeClock clock = new FakeClock(DateTime.now(UTC)); - @RegisterExtension public final InjectExtension inject = new InjectExtension(); @BeforeEach final void beforeEach() { - inject.setStaticField(Ofy.class, "clock", clock); - command.clock = clock; + inject.setStaticField(Ofy.class, "clock", fakeClock); + command.clock = fakeClock; createTlds("foo", "bar", "baz", "qux"); } @@ -55,13 +50,12 @@ public class CountDomainsCommandTest extends CommandTestCase { + private DomainBase domain; + + @RegisterExtension public final InjectExtension inject = new InjectExtension(); + + @BeforeEach + void beforeEach() { + inject.setStaticField(Ofy.class, "clock", fakeClock); + command.clock = fakeClock; + domain = persistActiveDomain("example.tld"); + } + @Test void testSuccess_complete() throws Exception { runCommandForced( @@ -78,6 +103,8 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase runCommandForced())) + assertThat(assertThrows(IllegalArgumentException.class, this::runCommandForced)) .hasMessageThat() .isEqualTo("Must provide non-empty domain_roids argument"); } diff --git a/core/src/test/resources/google/registry/flows/domain/domain_update_superuser_extension.xml b/core/src/test/resources/google/registry/flows/domain/domain_update_superuser_extension.xml new file mode 100644 index 000000000..3dc180fbc --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_update_superuser_extension.xml @@ -0,0 +1,17 @@ + + + + + example.tld + + + + + + %AUTORENEWS% + + + ABC-12345 + + diff --git a/core/src/test/resources/google/registry/tools/server/domain_update_set_autorenew.xml b/core/src/test/resources/google/registry/tools/server/domain_update_set_autorenew.xml new file mode 100644 index 000000000..d5b734583 --- /dev/null +++ b/core/src/test/resources/google/registry/tools/server/domain_update_set_autorenew.xml @@ -0,0 +1,17 @@ + + + + + + example.tld + + + + + %AUTORENEWS% + + + RegistryTool + +