1
0
mirror of https://github.com/google/nomulus synced 2026-01-16 02:33:16 +00:00

Compare commits

...

6 Commits

Author SHA1 Message Date
Ben McIlwain
c187c92ae4 Allow creation of hostnames on .zz-- style TLDs for RST (#2935)
This is a follow-on to PR #2909, which fixed the issue for domains, but
apparently not fully for hostnames.

BUG= http://b/476144993
2026-01-15 20:37:32 +00:00
gbrodman
22ca4e3f2b Disable old fee extensions in non-prod envs (#2933)
The primary annoyance with this is that it means we need (or at least,
should) split all tests that use the fee extension into two separate
tests -- one that simulates non-prod environments, and one that
simulates prod environments. This leads to duplication of many tests but
that's fine since this is theoretically temporary.
2026-01-14 19:04:22 +00:00
Nilay Shah
f27136458a Configure cloud scheduler to trigger MoSAPI SLA status to cloud monitoring (#2926)
* Configure cloud scheduler to trigger MoSAPI SLA status to cloud monitoring in production

- We have kept this job to trigger for every 3 minutes so that we get near to real time update for our task.
- This will not trigger metrics for now as we have not written Metrics triggering logic yet
- Logs are added

* Change Trigger scheduling from 3 minutes to 5 minutes
2026-01-13 18:48:43 +00:00
Ben McIlwain
d8e647316e Remove contact as a supported object type in EPP (#2932)
This primarily affects the EPP greeting. We already were erroring out when any
contact flows attempted to be run; this should just prevent registrars from even
trying them at all.

This PR is designed to be minimally invasive, and does not remove any of the
contact flows or Jakarta XML/XJC objects/files themselves. That can be done
later as a follow-up.

Also note that the contact namespace urn:ietf:params:xml:ns:contact-1.0 is still
present for now in RDE exports, but I'll remove that subsequently as well.

BUG= http://b/475506288
2026-01-13 17:21:03 +00:00
Ben McIlwain
d6e0a7b979 Change domain update commands to be varipotent by status (#2930)
This means that attempting to add a status that is already present will now
fail, and attempting to remove a status that is not present will also now fail.

This also refactors the existing checks into a single verify method, rather than
having to call three separate methods from every callsite.

BUG= http://b/474645068
2026-01-12 22:12:08 +00:00
Juan Celhay
5725eb95e0 Add Cloud java profiler to nomulus docker images (#2919)
* add cloud profiler to dockerfile and start script

* add apt-get update

* change in cb machine type for nomulus

* fix typo

* add max worker limit to gradle tests

* Switch to root before doing apt-get

* correct dockerfile

* jetty/Dockerfile

* profiler service conditional to kubernetes container name
2026-01-12 15:19:05 +00:00
171 changed files with 7348 additions and 2864 deletions

View File

@@ -98,8 +98,8 @@ PRESUBMITS = {
"File did not include the license header.",
# Files must end in a newline
PresubmitCheck(r".*\n$", ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"),
{"node_modules/"}, REQUIRED):
PresubmitCheck(r".*\n$", ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts", "xml"),
{"node_modules/", ".idea"}, REQUIRED):
"Source files must end in a newline.",
# System.(out|err).println should only appear in tools/ or load-testing/

View File

@@ -322,4 +322,15 @@
<service>bsa</service>
<schedule>23 8,20 * * *</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/triggerMosApiServiceState]]></url>
<name>triggerMosApiServiceState</name>
<description>
Fetches the service state from MosAPI and triggers the metrics status for all TLDs.
</description>
<!-- Runs every 5 minutes. -->
<schedule>*/5 * * * *</schedule>
</task>
</entries>

View File

@@ -194,27 +194,24 @@ public final class ResourceFlowUtils {
}
}
/** Check that the same values aren't being added and removed in an update command. */
public static <T> void checkSameValuesNotAddedAndRemoved(
ImmutableSet<T> fieldsToAdd, ImmutableSet<T> fieldsToRemove)
throws AddRemoveSameValueException {
/**
* Verifies the adds and removes on a resource.
*
* <p>This throws an exception in three different situations: if the same value is being both
* added and removed, if a value is being added that is already present, or if a value is being
* removed that isn't present.
*/
public static <T> void verifyAddsAndRemoves(
ImmutableSet<T> existingFields, ImmutableSet<T> fieldsToAdd, ImmutableSet<T> fieldsToRemove)
throws AddRemoveSameValueException,
AddExistingValueException,
RemoveNonexistentValueException {
if (!intersection(fieldsToAdd, fieldsToRemove).isEmpty()) {
throw new AddRemoveSameValueException();
}
}
/** Check that we aren't adding a value that is already present. */
public static <T> void checkExistingValueNotAdded(
ImmutableSet<T> fieldsToAdd, ImmutableSet<T> existingFields)
throws AddExistingValueException {
if (!intersection(fieldsToAdd, existingFields).isEmpty()) {
throw new AddExistingValueException();
}
}
public static <T> void checkNonexistentValueNotRemoved(
ImmutableSet<T> fieldsToRemove, ImmutableSet<T> existingFields)
throws RemoveNonexistentValueException {
if (intersection(fieldsToRemove, existingFields).size() != fieldsToRemove.size()) {
throw new RemoveNonexistentValueException();
}

View File

@@ -976,23 +976,21 @@ public class DomainFlowUtils {
throw new UrgentAttributeNotSupportedException();
}
// There must be at least one of add/rem/chg, and chg isn't actually supported.
if (secDnsUpdate.getChange() != null) {
if (secDnsUpdate.getChange().isPresent()) {
// The only thing you can change is maxSigLife, and we don't support that at all.
throw new MaxSigLifeChangeNotSupportedException();
}
Add add = secDnsUpdate.getAdd();
Remove remove = secDnsUpdate.getRemove();
if (add == null && remove == null) {
Optional<Add> add = secDnsUpdate.getAdd();
Optional<Remove> remove = secDnsUpdate.getRemove();
if (add.isEmpty() && remove.isEmpty()) {
throw new EmptySecDnsUpdateException();
}
if (remove != null && Boolean.FALSE.equals(remove.getAll())) {
if (remove.isPresent() && Boolean.FALSE.equals(remove.get().getAll())) {
throw new SecDnsAllUsageException(); // Explicit all=false is meaningless.
}
Set<DomainDsData> toAdd = (add == null) ? ImmutableSet.of() : add.getDsData();
Set<DomainDsData> toAdd = add.map(Add::getDsData).orElse(ImmutableSet.of());
Set<DomainDsData> toRemove =
(remove == null)
? ImmutableSet.of()
: (remove.getAll() == null) ? remove.getDsData() : oldDsData;
remove.map(r -> (r.getAll() == null) ? r.getDsData() : oldDsData).orElse(ImmutableSet.of());
// RFC 5910 specifies that removes are processed before adds.
return ImmutableSet.copyOf(union(difference(oldDsData, toRemove), toAdd));
}

View File

@@ -21,10 +21,8 @@ import static com.google.common.collect.Sets.union;
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
import static google.registry.flows.FlowUtils.persistEntityChanges;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.checkExistingValueNotAdded;
import static google.registry.flows.ResourceFlowUtils.checkNonexistentValueNotRemoved;
import static google.registry.flows.ResourceFlowUtils.checkSameValuesNotAddedAndRemoved;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyAddsAndRemoves;
import static google.registry.flows.ResourceFlowUtils.verifyAllStatusesAreClientSettable;
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
@@ -77,6 +75,8 @@ import google.registry.model.domain.fee.FeeUpdateCommandExtension;
import google.registry.model.domain.metadata.MetadataExtension;
import google.registry.model.domain.secdns.DomainDsData;
import google.registry.model.domain.secdns.SecDnsUpdateExtension;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension;
import google.registry.model.eppcommon.AuthInfo;
import google.registry.model.eppcommon.StatusValue;
@@ -247,14 +247,19 @@ public final class DomainUpdateFlow implements MutatingFlow {
private Domain performUpdate(Update command, Domain domain, DateTime now) throws EppException {
AddRemove add = command.getInnerAdd();
AddRemove remove = command.getInnerRemove();
checkSameValuesNotAddedAndRemoved(add.getNameservers(), remove.getNameservers());
checkSameValuesNotAddedAndRemoved(add.getContacts(), remove.getContacts());
checkSameValuesNotAddedAndRemoved(add.getStatusValues(), remove.getStatusValues());
checkExistingValueNotAdded(add.getNameservers(), domain.getNameservers());
checkNonexistentValueNotRemoved(remove.getNameservers(), domain.getNameservers());
Change change = command.getInnerChange();
Optional<SecDnsUpdateExtension> secDnsUpdate =
eppInput.getSingleExtension(SecDnsUpdateExtension.class);
verifyAddsAndRemoves(domain.getNameservers(), add.getNameservers(), remove.getNameservers());
verifyAddsAndRemoves(domain.getContacts(), add.getContacts(), remove.getContacts());
verifyAddsAndRemoves(domain.getStatusValues(), add.getStatusValues(), remove.getStatusValues());
if (secDnsUpdate.isPresent()) {
SecDnsUpdateExtension ext = secDnsUpdate.get();
verifyAddsAndRemoves(
domain.getDsData(),
ext.getAdd().map(Add::getDsData).orElse(ImmutableSet.of()),
ext.getRemove().map(Remove::getDsData).orElse(ImmutableSet.of()));
}
Change change = command.getInnerChange();
// We have to verify no duplicate contacts _before_ constructing the domain because it is
// illegal to construct a domain with duplicate contacts.

View File

@@ -17,6 +17,7 @@ package google.registry.flows.host;
import static google.registry.flows.domain.DomainFlowUtils.validateFirstLabel;
import static google.registry.model.EppResourceUtils.isActive;
import static google.registry.model.tld.Tlds.findTldForName;
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static java.util.stream.Collectors.joining;
@@ -34,7 +35,6 @@ import google.registry.flows.EppException.StatusProhibitsOperationException;
import google.registry.model.ForeignKeyUtils;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.StatusValue;
import google.registry.util.Idn;
import java.net.InetAddress;
import java.util.Optional;
import org.joda.time.DateTime;
@@ -57,7 +57,7 @@ public class HostFlowUtils {
throw new HostNameNotLowerCaseException(hostNameLowerCase);
}
try {
String hostNamePunyCoded = Idn.toASCII(name);
String hostNamePunyCoded = canonicalizeHostname(name);
if (!name.equals(hostNamePunyCoded)) {
throw new HostNameNotPunyCodedException(hostNamePunyCoded);
}

View File

@@ -20,10 +20,8 @@ import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
import static google.registry.dns.RefreshDnsOnHostRenameAction.PARAM_HOST_KEY;
import static google.registry.dns.RefreshDnsOnHostRenameAction.QUEUE_HOST_RENAME;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.checkExistingValueNotAdded;
import static google.registry.flows.ResourceFlowUtils.checkNonexistentValueNotRemoved;
import static google.registry.flows.ResourceFlowUtils.checkSameValuesNotAddedAndRemoved;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
import static google.registry.flows.ResourceFlowUtils.verifyAddsAndRemoves;
import static google.registry.flows.ResourceFlowUtils.verifyAllStatusesAreClientSettable;
import static google.registry.flows.ResourceFlowUtils.verifyNoDisallowedStatuses;
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
@@ -161,10 +159,10 @@ public final class HostUpdateFlow implements MutatingFlow {
}
AddRemove add = command.getInnerAdd();
AddRemove remove = command.getInnerRemove();
checkSameValuesNotAddedAndRemoved(add.getStatusValues(), remove.getStatusValues());
checkSameValuesNotAddedAndRemoved(add.getInetAddresses(), remove.getInetAddresses());
checkExistingValueNotAdded(add.getInetAddresses(), existingHost.getInetAddresses());
checkNonexistentValueNotRemoved(remove.getInetAddresses(), existingHost.getInetAddresses());
verifyAddsAndRemoves(
existingHost.getStatusValues(), add.getStatusValues(), remove.getStatusValues());
verifyAddsAndRemoves(
existingHost.getInetAddresses(), add.getInetAddresses(), remove.getInetAddresses());
HostFlowUtils.validateInetAddresses(add.getInetAddresses());
VKey<Domain> newSuperordinateDomainKey =
newSuperordinateDomain.map(Domain::createVKey).orElse(null);

View File

@@ -31,7 +31,7 @@ import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@Access(AccessType.FIELD)
public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {
@XmlTransient @Transient String domainRepoId;
@XmlTransient @Transient @Insignificant String domainRepoId;
/** The identifier for this particular key in the domain. */
@Transient int keyTag;

View File

@@ -24,6 +24,7 @@ import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlTransient;
import jakarta.xml.bind.annotation.XmlType;
import java.util.Optional;
import java.util.Set;
/** The EPP secDNS extension that may be present on domain update commands. */
@@ -55,16 +56,16 @@ public class SecDnsUpdateExtension extends ImmutableObject implements CommandExt
return urgent;
}
public Remove getRemove() {
return remove;
public Optional<Remove> getRemove() {
return Optional.ofNullable(remove);
}
public Add getAdd() {
return add;
public Optional<Add> getAdd() {
return Optional.ofNullable(add);
}
public Change getChange() {
return change;
public Optional<Change> getChange() {
return Optional.ofNullable(change);
}
@XmlTransient

View File

@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppOutput;
import google.registry.util.NonFinalForTesting;
import google.registry.util.RegistryEnvironment;
import google.registry.xml.ValidationMode;
import google.registry.xml.XmlException;
@@ -60,20 +61,35 @@ public class EppXmlTransformer {
// XML schemas that should not be used in production (yet)
private static final ImmutableSet<String> NON_PROD_SCHEMAS = ImmutableSet.of("fee-std-v1.xsd");
private static final XmlTransformer INPUT_TRANSFORMER =
// XML schemas that should only be used in production (for backcompat)
private static final ImmutableSet<String> ONLY_PROD_SCHEMAS =
ImmutableSet.of("fee06.xsd", "fee11.xsd", "fee12.xsd");
// TODO(gbrodman): make this final when we can actually remove the old fee extensions and aren't
// relying on switching by environment
@NonFinalForTesting
private static XmlTransformer INPUT_TRANSFORMER =
new XmlTransformer(getSchemas(), EppInput.class);
private static final XmlTransformer OUTPUT_TRANSFORMER =
// TODO(gbrodman): make this final when we can actually remove the old fee extensions and aren't
// relying on switching by environment
@NonFinalForTesting
private static XmlTransformer OUTPUT_TRANSFORMER =
new XmlTransformer(getSchemas(), EppOutput.class);
@VisibleForTesting
public static ImmutableList<String> getSchemas() {
if (RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)) {
return ALL_SCHEMAS.stream()
.filter(s -> !NON_PROD_SCHEMAS.contains(s))
.collect(toImmutableList());
}
return ALL_SCHEMAS;
ImmutableSet<String> schemasToSkip =
RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)
? NON_PROD_SCHEMAS
: ONLY_PROD_SCHEMAS;
return ALL_SCHEMAS.stream().filter(s -> !schemasToSkip.contains(s)).collect(toImmutableList());
}
@VisibleForTesting
public static void reloadTransformers() {
INPUT_TRANSFORMER = new XmlTransformer(getSchemas(), EppInput.class);
OUTPUT_TRANSFORMER = new XmlTransformer(getSchemas(), EppOutput.class);
}
public static void validateOutput(String xml) throws XmlException {

View File

@@ -17,6 +17,7 @@ package google.registry.model.eppcommon;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Maps.uniqueIndex;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.model.domain.fee06.FeeCheckCommandExtensionV06;
@@ -33,6 +34,7 @@ import google.registry.model.domain.rgp.RgpUpdateExtension;
import google.registry.model.domain.secdns.SecDnsCreateExtension;
import google.registry.model.eppinput.EppInput.CommandExtension;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.util.NonFinalForTesting;
import google.registry.util.RegistryEnvironment;
import jakarta.xml.bind.annotation.XmlSchema;
import java.util.EnumSet;
@@ -44,10 +46,7 @@ public class ProtocolDefinition {
public static final String LANGUAGE = "en";
public static final ImmutableSet<String> SUPPORTED_OBJECT_SERVICES =
ImmutableSet.of(
"urn:ietf:params:xml:ns:host-1.0",
"urn:ietf:params:xml:ns:domain-1.0",
"urn:ietf:params:xml:ns:contact-1.0");
ImmutableSet.of("urn:ietf:params:xml:ns:host-1.0", "urn:ietf:params:xml:ns:domain-1.0");
/** Enum representing which environments should have which service extensions enabled. */
private enum ServiceExtensionVisibility {
@@ -65,15 +64,15 @@ public class ProtocolDefinition {
FEE_0_6(
FeeCheckCommandExtensionV06.class,
FeeCheckResponseExtensionV06.class,
ServiceExtensionVisibility.ALL),
ServiceExtensionVisibility.ONLY_IN_PRODUCTION),
FEE_0_11(
FeeCheckCommandExtensionV11.class,
FeeCheckResponseExtensionV11.class,
ServiceExtensionVisibility.ALL),
ServiceExtensionVisibility.ONLY_IN_PRODUCTION),
FEE_0_12(
FeeCheckCommandExtensionV12.class,
FeeCheckResponseExtensionV12.class,
ServiceExtensionVisibility.ALL),
ServiceExtensionVisibility.ONLY_IN_PRODUCTION),
FEE_1_00(
FeeCheckCommandExtensionStdV1.class,
FeeCheckResponseExtensionStdV1.class,
@@ -112,7 +111,7 @@ public class ProtocolDefinition {
return clazz.getPackage().getAnnotation(XmlSchema.class).namespace();
}
private boolean isVisible() {
public boolean isVisible() {
return switch (visibility) {
case ALL -> true;
case ONLY_IN_PRODUCTION -> RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION);
@@ -137,14 +136,25 @@ public class ProtocolDefinition {
}
/** A set of all the visible extension URIs. */
private static final ImmutableSet<String> visibleServiceExtensionUris =
EnumSet.allOf(ServiceExtension.class).stream()
.filter(ServiceExtension::isVisible)
.map(ServiceExtension::getUri)
.collect(toImmutableSet());
// TODO(gbrodman): make this final when we can actually remove the old fee extensions and aren't
// relying on switching by environment
@NonFinalForTesting private static ImmutableSet<String> visibleServiceExtensionUris;
static {
reloadServiceExtensionUris();
}
/** Return the set of all visible service extension URIs. */
public static ImmutableSet<String> getVisibleServiceExtensionUris() {
return visibleServiceExtensionUris;
}
@VisibleForTesting
public static void reloadServiceExtensionUris() {
visibleServiceExtensionUris =
EnumSet.allOf(ServiceExtension.class).stream()
.filter(ServiceExtension::isVisible)
.map(ServiceExtension::getUri)
.collect(toImmutableSet());
}
}

View File

@@ -143,6 +143,7 @@ public class MosApiStateService {
if (!allStates.isEmpty()) {
try {
logger.atInfo().log("Triggering MoSAPI status to cloud monitoring for all TLDs.");
mosApiMetrics.recordStates(allStates);
} catch (Exception e) {
logger.atSevere().withCause(e).log("Failed to submit MoSAPI metrics batch.");

View File

@@ -48,6 +48,7 @@ public class TriggerServiceStateAction implements Runnable {
public void run() {
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
try {
logger.atInfo().log("Beginning to trigger MoSAPI metrics for all TLDs.");
stateService.triggerMetricsForAllServiceStateSummaries();
response.setStatus(200);
response.setPayload("MoSAPI metrics triggered successfully for all TLDs.");

View File

@@ -135,28 +135,6 @@ public abstract class FlowTestCase<F extends Flow> {
return TestDataHelper.loadFile(getClass(), filename, substitutions);
}
/**
* Converts an input or response EPP message with draft fee extension v12 to std v1.
*
* <p>There is no practical changes between draft v12 and the v1 standard. This method allows us
* to reuse v12 test data.
*/
protected String loadFeeV12FileAsStdV1(String filename) {
String content = loadFile(filename);
return content.replace("urn:ietf:params:xml:ns:fee-0.12", "urn:ietf:params:xml:ns:epp:fee-1.0");
}
/**
* Converts an input or response EPP message with draft fee extension v12 to std v1.
*
* <p>There is no practical changes between draft v12 and the v1 standard. This method allows us
* to reuse v12 test data.
*/
protected String loadFeeV12FileAsStdV1(String filename, Map<String, String> substitutions) {
String content = loadFile(filename, substitutions);
return content.replace("urn:ietf:params:xml:ns:fee-0.12", "urn:ietf:params:xml:ns:epp:fee-1.0");
}
@Nullable
protected String getClientTrid() throws Exception {
return eppLoader.getEpp().getCommandWrapper().getClTrid().orElse(null);

View File

@@ -209,6 +209,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
private static final String ENCODED_SMD =
TmchData.readEncodedSignedMark(TmchTestData.loadFile(SMD_FILE_PATH)).getEncodedData();
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
ImmutableMap.of(
"FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00", "CURRENCY", "USD", "FEE", "15.00");
private AllocationToken allocationToken;
DomainCreateFlowTest() {
@@ -640,132 +644,44 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_fee_v06() throws Exception {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
void testSuccess_fee_std_v1() throws Exception {
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
doSuccessfulTest(
"tld",
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.6", "FEE", "24.00"));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE", "24.00"));
}
@Test
void testSuccess_fee_v11() throws Exception {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
void testSuccess_fee_withDefaultAttributes_std_v1() throws Exception {
setEppInput("domain_create_fee_defaults.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
doSuccessfulTest(
"tld",
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.11", "FEE", "24.00"));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE", "24.00"));
}
@Test
void testSuccess_fee_v12() throws Exception {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
persistContactsAndHosts();
doSuccessfulTest(
"tld",
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "24.00"));
}
@Test
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
setEppInput("domain_create_fee_defaults.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
persistContactsAndHosts();
doSuccessfulTest(
"tld",
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.6", "FEE", "24.00"));
}
@Test
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
setEppInput("domain_create_fee_defaults.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
persistContactsAndHosts();
doSuccessfulTest(
"tld",
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.11", "FEE", "24.00"));
}
@Test
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
setEppInput("domain_create_fee_defaults.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
persistContactsAndHosts();
doSuccessfulTest(
"tld",
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "24.00"));
}
@Test
void testFailure_refundableFee_v06() {
setEppInput("domain_create_fee_refundable.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
void testFailure_refundableFee_std_v1() {
setEppInput("domain_create_fee_refundable.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v11() {
setEppInput("domain_create_fee_refundable.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
void testFailure_gracePeriodFee_std_v1() {
setEppInput("domain_create_fee_grace_period.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v12() {
setEppInput("domain_create_fee_refundable.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v06() {
setEppInput("domain_create_fee_grace_period.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v11() {
setEppInput("domain_create_fee_grace_period.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v12() {
setEppInput("domain_create_fee_grace_period.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v06() {
setEppInput("domain_create_fee_applied.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v11() {
setEppInput("domain_create_fee_applied.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v12() {
setEppInput("domain_create_fee_applied.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
void testFailure_appliedFee_std_v1() {
setEppInput("domain_create_fee_applied.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -796,9 +712,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_premiumAndEap() throws Exception {
void testSuccess_premiumAndEap_std_v1() throws Exception {
createTld("example");
setEppInput("domain_create_premium_eap.xml");
setEppInput("domain_create_premium_eap.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts("net");
persistResource(
Tld.get("example")
@@ -816,8 +732,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.NORMAL,
loadFile(
"domain_create_response_premium_eap.xml", ImmutableMap.of("DOMAIN", "rich.example")));
loadFile("domain_create_response_premium_eap.xml", FEE_STD_1_0_MAP));
assertSuccessfulCreate("example", ImmutableSet.of(), 200);
assertNoLordn();
}
@@ -1047,8 +962,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testFailure_wrongFeeAmount_v06() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
void testFailure_wrongFeeAmount_std_v1() {
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
@@ -1061,7 +976,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v06() throws Exception {
void testSuccess_wrongFeeAmountTooHigh_defaultToken_std_v1() throws Exception {
setupDefaultTokenWithDiscount();
persistResource(
Tld.get("tld")
@@ -1069,65 +984,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setCreateBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 8)))
.build());
// Expects fee of $24
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
persistContactsAndHosts();
// $15 is 50% off the first year registration ($8) and 0% 0ff the 2nd year (renewal at $11)
runFlowAssertResponse(
loadFile(
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.6", "FEE", "15.00")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v06() throws Exception {
setupDefaultTokenWithDiscount();
persistResource(
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 100)))
.build());
// Expects fee of $24
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
persistContactsAndHosts();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v11() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
persistResource(
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistContactsAndHosts();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v11() throws Exception {
setupDefaultTokenWithDiscount();
persistResource(
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 8)))
.build());
// Expects fee of $24
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
// $12 is equal to 50% off the first year registration and 0% 0ff the 2nd year
runFlowAssertResponse(
loadFile(
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.11", "FEE", "15.00")));
runFlowAssertResponse(loadFile("domain_create_response_fee.xml", FEE_STD_1_0_MAP));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v11() throws Exception {
void testFailure_wrongFeeAmountTooLow_defaultToken_std_v1() throws Exception {
setupDefaultTokenWithDiscount();
persistResource(
Tld.get("tld")
@@ -1136,79 +1000,16 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 100)))
.build());
// Expects fee of $24
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v12() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
persistResource(
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistContactsAndHosts();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v12() throws Exception {
setupDefaultTokenWithDiscount();
persistResource(
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 8)))
.build());
// Expects fee of $24
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
persistContactsAndHosts();
// $12 is equal to 50% off the first year registration and 0% 0ff the 2nd year
runFlowAssertResponse(
loadFile(
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "15.00")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v12() throws Exception {
setupDefaultTokenWithDiscount();
persistResource(
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 100)))
.build());
// Expects fee of $24
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
persistContactsAndHosts();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v06() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "EUR"));
persistContactsAndHosts();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v11() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "EUR"));
persistContactsAndHosts();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v12() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "EUR"));
void testFailure_wrongCurrency_std_v1() {
setEppInput(
"domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "CURRENCY", "EUR"));
persistContactsAndHosts();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -1216,7 +1017,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_unknownCurrency() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "BAD"));
setEppInput(
"domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "CURRENCY", "BAD"));
persistContactsAndHosts();
EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -1580,7 +1382,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_allocationToken_multiYearDiscount_worksForPremiums() throws Exception {
void testSuccess_allocationToken_multiYearDiscount_worksForPremiums_std_v1() throws Exception {
createTld("example");
persistContactsAndHosts();
persistResource(
@@ -1601,11 +1403,17 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
clock.advanceOneMilli();
setEppInput(
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("YEARS", "3", "FEE", "104.00"));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "YEARS", "3", "FEE", "104.00"));
runFlowAssertResponse(
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "104.00")));
ImmutableMap.of(
"FEE_VERSION",
"epp:fee-1.0",
"EXDATE",
"2002-04-03T22:00:00.0Z",
"FEE",
"104.00")));
BillingEvent billingEvent =
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class));
assertThat(billingEvent.getTargetId()).isEqualTo("rich.example");
@@ -1614,7 +1422,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_allocationToken_singleYearDiscount_worksForPremiums() throws Exception {
void testSuccess_allocationToken_singleYearDiscount_worksForPremiums_std_v1() throws Exception {
createTld("example");
persistContactsAndHosts();
persistResource(
@@ -1634,11 +1442,17 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
clock.advanceOneMilli();
setEppInput(
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("YEARS", "3", "FEE", "204.44"));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "YEARS", "3", "FEE", "204.44"));
runFlowAssertResponse(
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "204.44")));
ImmutableMap.of(
"FEE_VERSION",
"epp:fee-1.0",
"EXDATE",
"2002-04-03T22:00:00.0Z",
"FEE",
"204.44")));
BillingEvent billingEvent =
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class));
assertThat(billingEvent.getTargetId()).isEqualTo("rich.example");
@@ -1797,7 +1611,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName() throws Exception {
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName_std_v1() throws Exception {
persistContactsAndHosts();
createTld("example");
persistResource(
@@ -1805,11 +1619,17 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.asBuilder()
.setAllowedTlds(ImmutableSet.of("example"))
.build());
setEppInput("domain_create_premium.xml");
setEppInput("domain_create_premium.xml", FEE_STD_1_0_MAP);
runFlowAssertResponse(
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00")));
ImmutableMap.of(
"FEE_VERSION",
"epp:fee-1.0",
"EXDATE",
"2001-04-03T22:00:00.0Z",
"FEE",
"200.00")));
assertSuccessfulCreate("example", ImmutableSet.of(), 200);
}
@@ -1995,9 +1815,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_superuserOverridesPremiumNameBlock() throws Exception {
void testSuccess_superuserOverridesPremiumNameBlock_std_v1() throws Exception {
createTld("example");
setEppInput("domain_create_premium.xml");
setEppInput("domain_create_premium.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts("net");
// Modify the Registrar to block premium names.
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
@@ -2006,7 +1826,13 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
SUPERUSER,
loadFile(
"domain_create_response_premium.xml",
ImmutableMap.of("EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00")));
ImmutableMap.of(
"FEE_VERSION",
"epp:fee-1.0",
"EXDATE",
"2001-04-03T22:00:00.0Z",
"FEE",
"200.00")));
assertSuccessfulCreate("example", ImmutableSet.of(), 200);
}
@@ -2137,9 +1963,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testFailure_premiumBlocked() {
void testFailure_premiumBlocked_std_v1() {
createTld("example");
setEppInput("domain_create_premium.xml");
setEppInput("domain_create_premium.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts("net");
// Modify the Registrar to block premium names.
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
@@ -2157,60 +1983,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testFailure_omitFeeExtensionOnLogin_v06() {
void testFailure_omitFeeExtensionOnLogin_std_v1() {
for (String uri : FEE_EXTENSION_URIS) {
removeServiceExtensionUri(uri);
}
createTld("net");
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
EppException thrown = assertThrows(UndeclaredServiceExtensionException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_omitFeeExtensionOnLogin_v11() {
for (String uri : FEE_EXTENSION_URIS) {
removeServiceExtensionUri(uri);
}
createTld("net");
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
persistContactsAndHosts();
EppException thrown = assertThrows(UndeclaredServiceExtensionException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_omitFeeExtensionOnLogin_v12() {
for (String uri : FEE_EXTENSION_URIS) {
removeServiceExtensionUri(uri);
}
createTld("net");
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
persistContactsAndHosts();
EppException thrown = assertThrows(UndeclaredServiceExtensionException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v06() {
setEppInput("domain_create_fee_bad_scale.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
persistContactsAndHosts();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v11() {
setEppInput("domain_create_fee_bad_scale.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
persistContactsAndHosts();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v12() {
setEppInput("domain_create_fee_bad_scale.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
void testFailure_feeGivenInWrongScale_std_v1() {
setEppInput("domain_create_fee_bad_scale.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -2699,8 +2485,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testFailure_eapFee_combined() {
setEppInput("domain_create_eap_combined_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
void testFailure_eapFee_combined_std_v1() {
setEppInput("domain_create_eap_combined_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
setEapForTld("tld");
EppException thrown = assertThrows(FeeDescriptionParseException.class, this::runFlow);
@@ -2709,12 +2495,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testFailure_eapFee_description_swapped() {
void testFailure_eapFee_description_swapped_std_v1() {
setEppInput(
"domain_create_eap_fee.xml",
ImmutableMap.of(
"FEE_VERSION",
"0.6",
"epp:fee-1.0",
"DESCRIPTION_1",
"Early Access Period",
"DESCRIPTION_2",
@@ -2727,11 +2513,11 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testFailure_eapFee_totalAmountNotMatched() {
void testFailure_eapFee_totalAmountNotMatched_std_v1() {
setEppInput(
"domain_create_extra_fees.xml",
new ImmutableMap.Builder<String, String>()
.put("FEE_VERSION", "0.6")
.put("FEE_VERSION", "epp:fee-1.0")
.put("DESCRIPTION_1", "create")
.put("FEE_1", "24")
.put("DESCRIPTION_2", "Early Access Period")
@@ -2747,11 +2533,11 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_eapFee_multipleEAPfees_doNotAddToExpectedValue() {
void testSuccess_eapFee_multipleEAPfees_doNotAddToExpectedValue_std_v1() {
setEppInput(
"domain_create_extra_fees.xml",
new ImmutableMap.Builder<String, String>()
.put("FEE_VERSION", "0.6")
.put("FEE_VERSION", "epp:fee-1.0")
.put("DESCRIPTION_1", "create")
.put("FEE_1", "24")
.put("DESCRIPTION_2", "Early Access Period")
@@ -2767,11 +2553,11 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_eapFee_multipleEAPfees_addToExpectedValue() throws Exception {
void testSuccess_eapFee_multipleEAPfees_addToExpectedValue_std_v1() throws Exception {
setEppInput(
"domain_create_extra_fees.xml",
new ImmutableMap.Builder<String, String>()
.put("FEE_VERSION", "0.6")
.put("FEE_VERSION", "epp:fee-1.0")
.put("DESCRIPTION_1", "create")
.put("FEE_1", "24")
.put("DESCRIPTION_2", "Early Access Period")
@@ -2781,33 +2567,36 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.build());
persistContactsAndHosts();
setEapForTld("tld");
doSuccessfulTest(
"tld", "domain_create_response_eap_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_STD_1_0_MAP);
}
@Test
void testSuccess_eapFee_fullDescription_includingArbitraryExpiryTime() throws Exception {
void testSuccess_eapFee_fullDescription_includingArbitraryExpiryTime_std_v1() throws Exception {
setEppInput(
"domain_create_eap_fee.xml",
ImmutableMap.of(
"FEE_VERSION",
"0.6",
"epp:fee-1.0",
"DESCRIPTION_1",
"create",
"DESCRIPTION_2",
"Early Access Period, fee expires: 2022-03-01T00:00:00.000Z"));
persistContactsAndHosts();
setEapForTld("tld");
doSuccessfulTest(
"tld", "domain_create_response_eap_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_STD_1_0_MAP);
}
@Test
void testFailure_eapFee_description_multipleMatch() {
void testFailure_eapFee_description_multipleMatch_std_v1() {
setEppInput(
"domain_create_eap_fee.xml",
ImmutableMap.of(
"FEE_VERSION", "0.6", "DESCRIPTION_1", "create", "DESCRIPTION_2", "renew transfer"));
"FEE_VERSION",
"epp:fee-1.0",
"DESCRIPTION_1",
"create",
"DESCRIPTION_2",
"renew transfer"));
persistContactsAndHosts();
setEapForTld("tld");
EppException thrown = assertThrows(FeeDescriptionMultipleMatchesException.class, this::runFlow);
@@ -2816,54 +2605,17 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_eapFeeApplied_v06() throws Exception {
void testSuccess_eapFeeApplied_std_v1() throws Exception {
setEppInput(
"domain_create_eap_fee.xml",
ImmutableMap.of(
"FEE_VERSION",
"0.6",
"DESCRIPTION_1",
"create",
"DESCRIPTION_2",
"Early Access Period"));
new ImmutableMap.Builder<String, String>()
.putAll(FEE_STD_1_0_MAP)
.put("DESCRIPTION_1", "create")
.put("DESCRIPTION_2", "Early Access Period")
.build());
persistContactsAndHosts();
setEapForTld("tld");
doSuccessfulTest(
"tld", "domain_create_response_eap_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
}
@Test
void testSuccess_eapFeeApplied_v11() throws Exception {
setEppInput(
"domain_create_eap_fee.xml",
ImmutableMap.of(
"FEE_VERSION",
"0.11",
"DESCRIPTION_1",
"create",
"DESCRIPTION_2",
"Early Access Period"));
persistContactsAndHosts();
setEapForTld("tld");
doSuccessfulTest(
"tld", "domain_create_response_eap_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
}
@Test
void testSuccess_eapFeeApplied_v12() throws Exception {
setEppInput(
"domain_create_eap_fee.xml",
ImmutableMap.of(
"FEE_VERSION",
"0.12",
"DESCRIPTION_1",
"create",
"DESCRIPTION_2",
"Early Access Period"));
persistContactsAndHosts();
setEapForTld("tld");
doSuccessfulTest(
"tld", "domain_create_response_eap_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_STD_1_0_MAP);
}
@Test
@@ -3002,7 +2754,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_nonAnchorTenant_nonPremiumRenewal() throws Exception {
void testSuccess_nonAnchorTenant_nonPremiumRenewal_std_v1() throws Exception {
createTld("example");
AllocationToken token =
persistResource(
@@ -3016,13 +2768,13 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
// Creation is still $100 but it'll create a NONPREMIUM renewal
setEppInput(
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("YEARS", "2", "FEE", "111.00"));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "YEARS", "2", "FEE", "111.00"));
runFlow();
assertSuccessfulCreate("example", ImmutableSet.of(), token, 111);
}
@Test
void testSuccess_specifiedRenewalPriceToken_specifiedRecurrencePrice() throws Exception {
void testSuccess_specifiedRenewalPriceToken_specifiedRecurrencePrice_std_v1() throws Exception {
createTld("example");
AllocationToken token =
persistResource(
@@ -3037,7 +2789,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
// Creation is still $100 but it'll create a $1 renewal
setEppInput(
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("YEARS", "2", "FEE", "101.00"));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "YEARS", "2", "FEE", "101.00"));
runFlow();
assertSuccessfulCreate("example", ImmutableSet.of(), token, 101, 1);
}
@@ -3087,7 +2839,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
}
@Test
void testSuccess_nonpremiumCreateToken() throws Exception {
void testSuccess_nonpremiumCreateToken_std_v1() throws Exception {
createTld("example");
persistContactsAndHosts();
persistResource(
@@ -3098,8 +2850,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setDomainName("rich.example")
.build());
setEppInput(
"domain_create_premium_allocationtoken.xml", ImmutableMap.of("YEARS", "1", "FEE", "13.00"));
runFlowAssertResponse(loadFile("domain_create_nonpremium_token_response.xml"));
"domain_create_premium_allocationtoken.xml",
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "YEARS", "1", "FEE", "13.00"));
runFlowAssertResponse(loadFile("domain_create_nonpremium_token_response.xml", FEE_STD_1_0_MAP));
}
@Test
@@ -3495,7 +3248,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
void testTieredPricingPromoResponse() throws Exception {
sessionMetadata.setRegistrarId("NewRegistrar");
setupDefaultTokenWithDiscount("NewRegistrar");
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
// Fee in the result should be 24 (create cost of 13 plus renew cost of 11) even though the
@@ -3503,29 +3256,29 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
runFlowAssertResponse(
loadFile(
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "24.00")));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE", "24.00")));
// Expected cost is half off the create cost (13/2 == 6.50) plus one full-cost renew (11)
assertThat(Iterables.getOnlyElement(loadAllOf(BillingEvent.class)).getCost())
.isEqualTo(Money.of(USD, 17.50));
}
@Test
void testTieredPricingPromo_registrarNotIncluded_standardResponse() throws Exception {
void testTieredPricingPromo_registrarNotIncluded_standardResponse_std_v1() throws Exception {
setupDefaultTokenWithDiscount("NewRegistrar");
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
// For a registrar not included in the tiered pricing promo, costs should be 24
runFlowAssertResponse(
loadFile(
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "24.00")));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE", "24.00")));
assertThat(Iterables.getOnlyElement(loadAllOf(BillingEvent.class)).getCost())
.isEqualTo(Money.of(USD, 24));
}
@Test
void testTieredPricingPromo_registrarIncluded_noTokenActive() throws Exception {
void testTieredPricingPromo_registrarIncluded_noTokenActive_std_v1() throws Exception {
sessionMetadata.setRegistrarId("NewRegistrar");
persistActiveDomain("example1.tld");
@@ -3540,14 +3293,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
TokenStatus.VALID))
.build());
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
setEppInput("domain_create_fee.xml", FEE_STD_1_0_MAP);
persistContactsAndHosts();
// The token hasn't started yet, so the cost should be create (13) plus renew (11)
runFlowAssertResponse(
loadFile(
"domain_create_response_fee.xml",
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "24.00")));
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE", "24.00")));
assertThat(Iterables.getOnlyElement(loadAllOf(BillingEvent.class)).getCost())
.isEqualTo(Money.of(USD, 24));
}

View File

@@ -0,0 +1,440 @@
// Copyright 2026 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.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_DELETE;
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
import static google.registry.testing.DatabaseHelper.assertPollMessages;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
import static google.registry.testing.DatabaseHelper.getPollMessages;
import static google.registry.testing.DatabaseHelper.loadByKey;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DomainSubject.assertAboutDomains;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingCancellation;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.poll.PollMessage;
import google.registry.model.tld.Tld;
import google.registry.testing.DatabaseHelper;
import java.util.Map;
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;
/** Tests for {@link DomainDeleteFlow} that use the old fee extensions (0.6, 0.11, 0.12). */
public class DomainDeleteFlowOldFeeExtensionsTest
extends ProductionSimulatingFeeExtensionsTest<DomainDeleteFlow> {
private static final DateTime TIME_BEFORE_FLOW = DateTime.parse("2000-06-06T22:00:00.0Z");
private static final DateTime A_MONTH_AGO = TIME_BEFORE_FLOW.minusMonths(1);
private static final DateTime A_MONTH_FROM_NOW = TIME_BEFORE_FLOW.plusMonths(1);
private static final ImmutableMap<String, String> FEE_06_MAP =
ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee");
private static final ImmutableMap<String, String> FEE_11_MAP =
ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11");
private static final ImmutableMap<String, String> FEE_12_MAP =
ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12");
private Domain domain;
private DomainHistory earlierHistoryEntry;
@BeforeEach
void beforeEachomainDeleteFlowOldFeeExtensionsTest() {
clock.setTo(TIME_BEFORE_FLOW);
setEppInput("domain_delete.xml");
createTld("tld");
}
@Test
void testSuccess_renewGracePeriodCredit_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_06_MAP);
}
@Test
void testSuccess_renewGracePeriodCredit_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_11_MAP);
}
@Test
void testSuccess_renewGracePeriodCredit_v12() throws Exception {
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_12_MAP);
}
@Test
void testSuccess_addGracePeriodCredit_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_06_MAP);
}
@Test
void testSuccess_addGracePeriodCredit_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_11_MAP);
}
@Test
void testSuccess_addGracePeriodCredit_v12() throws Exception {
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_12_MAP);
}
@Test
void testSuccess_autoRenewGracePeriod_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_v12() throws Exception {
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_priceChanges_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
Money.of(USD, 11),
TIME_BEFORE_FLOW.minusDays(5),
Money.of(USD, 20)))
.build());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_priceChanges_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
Money.of(USD, 11),
TIME_BEFORE_FLOW.minusDays(5),
Money.of(USD, 20)))
.build());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_priceChanges_v12() throws Exception {
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
Money.of(USD, 11),
TIME_BEFORE_FLOW.minusDays(5),
Money.of(USD, 20)))
.build());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
}
@Test
void testSuccess_freeCreation_deletionDuringGracePeriod_v12() throws Exception {
// Deletion during the add grace period should still work even if the credit is 0
setUpSuccessfulTest();
BillingEvent graceBillingEvent =
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 0)));
setUpGracePeriods(
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, domain.getRepoId(), graceBillingEvent));
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace_v12.xml"));
}
private void createReferencedEntities(DateTime expirationTime) throws Exception {
// Persist a linked contact.
Contact contact = persistActiveContact("sh8013");
domain =
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setCreationTimeForTest(TIME_BEFORE_FLOW)
.setRegistrant(Optional.of(contact.createVKey()))
.setRegistrationExpirationTime(expirationTime)
.build());
earlierHistoryEntry =
persistResource(
new DomainHistory.Builder()
.setType(DOMAIN_CREATE)
.setDomain(domain)
.setModificationTime(clock.nowUtc())
.setRegistrarId(domain.getCreationRegistrarId())
.build());
}
private void setUpSuccessfulTest() throws Exception {
createReferencedEntities(A_MONTH_FROM_NOW);
BillingRecurrence autorenewBillingEvent =
persistResource(createAutorenewBillingEvent("TheRegistrar").build());
PollMessage.Autorenew autorenewPollMessage =
persistResource(createAutorenewPollMessage("TheRegistrar").build());
domain =
persistResource(
domain
.asBuilder()
.setAutorenewBillingEvent(autorenewBillingEvent.createVKey())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.build());
assertMutatingFlow(true);
}
private void doSuccessfulTest_noAddGracePeriod(String responseFilename) throws Exception {
doSuccessfulTest_noAddGracePeriod(responseFilename, ImmutableMap.of());
}
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();
BillingEvent renewBillingEvent =
persistResource(createBillingEvent(Reason.RENEW, Money.of(USD, 456)));
setUpGracePeriods(
GracePeriod.forBillingEvent(GracePeriodStatus.RENEW, domain.getRepoId(), renewBillingEvent),
// This grace period has no associated billing event, so it won't cause a cancellation.
GracePeriod.create(
GracePeriodStatus.TRANSFER,
domain.getRepoId(),
TIME_BEFORE_FLOW.plusDays(1),
"NewRegistrar",
null));
// We should see exactly one poll message, which is for the autorenew 1 month in the future.
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
DateTime expectedExpirationTime = domain.getRegistrationExpirationTime().minusYears(2);
clock.advanceOneMilli();
runFlowAssertResponse(loadFile(responseFilename, substitutions));
Domain resource = reloadResourceByForeignKey();
// Check that the domain is in the pending delete state.
assertAboutDomains()
.that(resource)
.hasStatusValue(StatusValue.PENDING_DELETE)
.and()
.hasDeletionTime(
clock
.nowUtc()
.plus(Tld.get("tld").getRedemptionGracePeriodLength())
.plus(Tld.get("tld").getPendingDeleteLength()))
.and()
.hasExactlyStatusValues(StatusValue.INACTIVE, StatusValue.PENDING_DELETE)
.and()
.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
// closed the autorenew recurrences immediately, there are no other autorenew effects.
assertAboutDomains().that(resource).hasRegistrationExpirationTime(expectedExpirationTime);
assertLastHistoryContainsResource(resource);
// All existing grace periods that were for billable actions should cause cancellations.
assertAutorenewClosedAndCancellationCreatedFor(
renewBillingEvent, getOnlyHistoryEntryOfType(resource, DOMAIN_DELETE, DomainHistory.class));
// All existing grace periods should be gone, and a new REDEMPTION one should be added.
assertThat(resource.getGracePeriods())
.containsExactly(
GracePeriod.create(
GracePeriodStatus.REDEMPTION,
domain.getRepoId(),
clock.nowUtc().plus(Tld.get("tld").getRedemptionGracePeriodLength()),
"TheRegistrar",
null,
resource.getGracePeriods().iterator().next().getGracePeriodId()));
assertDeletionPollMessageFor(resource, "Domain deleted.");
}
private void assertDeletionPollMessageFor(Domain domain, String expectedMessage) {
// There should be a future poll message at the deletion time. The previous autorenew poll
// message should now be deleted.
assertAboutDomains().that(domain).hasDeletePollMessage();
DateTime deletionTime = domain.getDeletionTime();
assertThat(getPollMessages("TheRegistrar", deletionTime.minusMinutes(1))).isEmpty();
assertThat(getPollMessages("TheRegistrar", deletionTime)).hasSize(1);
assertThat(domain.getDeletePollMessage())
.isEqualTo(getOnlyPollMessage("TheRegistrar").createVKey());
PollMessage.OneTime deletePollMessage = loadByKey(domain.getDeletePollMessage());
assertThat(deletePollMessage.getMsg()).isEqualTo(expectedMessage);
}
private void setUpGracePeriods(GracePeriod... gracePeriods) {
domain =
persistResource(
domain.asBuilder().setGracePeriods(ImmutableSet.copyOf(gracePeriods)).build());
}
private void assertAutorenewClosedAndCancellationCreatedFor(
BillingEvent graceBillingEvent, DomainHistory historyEntryDomainDelete) {
assertAutorenewClosedAndCancellationCreatedFor(
graceBillingEvent, historyEntryDomainDelete, clock.nowUtc());
}
private void assertAutorenewClosedAndCancellationCreatedFor(
BillingEvent graceBillingEvent, DomainHistory historyEntryDomainDelete, DateTime eventTime) {
assertBillingEvents(
createAutorenewBillingEvent("TheRegistrar").setRecurrenceEndTime(eventTime).build(),
graceBillingEvent,
new BillingCancellation.Builder()
.setReason(graceBillingEvent.getReason())
.setTargetId("example.tld")
.setRegistrarId("TheRegistrar")
.setEventTime(eventTime)
.setBillingTime(TIME_BEFORE_FLOW.plusDays(1))
.setBillingEvent(graceBillingEvent.createVKey())
.setDomainHistory(historyEntryDomainDelete)
.build());
}
private BillingRecurrence.Builder createAutorenewBillingEvent(String registrarId) {
return new BillingRecurrence.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId("example.tld")
.setRegistrarId(registrarId)
.setEventTime(A_MONTH_FROM_NOW)
.setRecurrenceEndTime(END_OF_TIME)
.setDomainHistory(earlierHistoryEntry);
}
private PollMessage.Autorenew.Builder createAutorenewPollMessage(String registrarId) {
return new PollMessage.Autorenew.Builder()
.setTargetId("example.tld")
.setRegistrarId(registrarId)
.setEventTime(A_MONTH_FROM_NOW)
.setAutorenewEndTime(END_OF_TIME)
.setHistoryEntry(earlierHistoryEntry);
}
private BillingEvent createBillingEvent(Reason reason, Money cost) {
return new BillingEvent.Builder()
.setReason(reason)
.setTargetId("example.tld")
.setRegistrarId("TheRegistrar")
.setCost(cost)
.setPeriodYears(2)
.setEventTime(TIME_BEFORE_FLOW.minusDays(4))
.setBillingTime(TIME_BEFORE_FLOW.plusDays(1))
.setDomainHistory(earlierHistoryEntry)
.build();
}
private void doAddGracePeriodDeleteTest(
GracePeriodStatus gracePeriodStatus, String responseFilename) throws Exception {
doAddGracePeriodDeleteTest(gracePeriodStatus, responseFilename, ImmutableMap.of());
}
private void doAddGracePeriodDeleteTest(
GracePeriodStatus gracePeriodStatus,
String responseFilename,
Map<String, String> substitutions)
throws Exception {
// Persist the billing event so it can be retrieved for cancellation generation and checking.
setUpSuccessfulTest();
BillingEvent graceBillingEvent =
persistResource(createBillingEvent(Reason.CREATE, Money.of(USD, 123)));
setUpGracePeriods(
GracePeriod.forBillingEvent(gracePeriodStatus, domain.getRepoId(), graceBillingEvent));
// We should see exactly one poll message, which is for the autorenew 1 month in the future.
assertPollMessages(createAutorenewPollMessage("TheRegistrar").build());
clock.advanceOneMilli();
runFlowAssertResponse(loadFile(responseFilename, substitutions));
// Check that the domain is fully deleted.
assertThat(reloadResourceByForeignKey()).isNull();
// The add grace period is for a billable action, so it should trigger a cancellation.
assertAutorenewClosedAndCancellationCreatedFor(
graceBillingEvent, getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE, DomainHistory.class));
assertDomainDnsRequests("example.tld");
// There should be no poll messages. The previous autorenew poll message should now be deleted.
assertThat(getPollMessages("TheRegistrar")).isEmpty();
}
private void setUpAutorenewGracePeriod() throws Exception {
createReferencedEntities(A_MONTH_AGO.plusYears(1));
BillingRecurrence autorenewBillingEvent =
persistResource(
createAutorenewBillingEvent("TheRegistrar").setEventTime(A_MONTH_AGO).build());
PollMessage.Autorenew autorenewPollMessage =
persistResource(
createAutorenewPollMessage("TheRegistrar").setEventTime(A_MONTH_AGO).build());
domain =
persistResource(
domain
.asBuilder()
.setGracePeriods(
ImmutableSet.of(
GracePeriod.createForRecurrence(
GracePeriodStatus.AUTO_RENEW,
domain.getRepoId(),
A_MONTH_AGO.plusDays(45),
"TheRegistrar",
autorenewBillingEvent.createVKey())))
.setAutorenewBillingEvent(autorenewBillingEvent.createVKey())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.build());
assertMutatingFlow(true);
}
}

View File

@@ -86,7 +86,6 @@ import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.Host;
@@ -122,12 +121,8 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
private static final DateTime A_MONTH_AGO = TIME_BEFORE_FLOW.minusMonths(1);
private static final DateTime A_MONTH_FROM_NOW = TIME_BEFORE_FLOW.plusMonths(1);
private static final ImmutableMap<String, String> FEE_06_MAP =
ImmutableMap.of("FEE_VERSION", "0.6", "FEE_NS", "fee");
private static final ImmutableMap<String, String> FEE_11_MAP =
ImmutableMap.of("FEE_VERSION", "0.11", "FEE_NS", "fee11");
private static final ImmutableMap<String, String> FEE_12_MAP =
ImmutableMap.of("FEE_VERSION", "0.12", "FEE_NS", "fee12");
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00");
DomainDeleteFlowTest() {
setEppInput("domain_delete.xml");
@@ -396,24 +391,9 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
}
@Test
void testSuccess_addGracePeriodCredit_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_06_MAP);
}
@Test
void testSuccess_addGracePeriodCredit_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_11_MAP);
}
@Test
void testSuccess_addGracePeriodCredit_v12() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
doAddGracePeriodDeleteTest(GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_12_MAP);
void testSuccess_addGracePeriodCredit_std_v1() throws Exception {
doAddGracePeriodDeleteTest(
GracePeriodStatus.ADD, "domain_delete_response_fee.xml", FEE_STD_1_0_MAP);
}
private void doSuccessfulTest_noAddGracePeriod(String responseFilename) throws Exception {
@@ -497,24 +477,8 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
}
@Test
void testSuccess_renewGracePeriodCredit_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_06_MAP);
}
@Test
void testSuccess_renewGracePeriodCredit_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_11_MAP);
}
@Test
void testSuccess_renewGracePeriodCredit_v12() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_12_MAP);
void testSuccess_renewGracePeriodCredit_std_v1() throws Exception {
doSuccessfulTest_noAddGracePeriod("domain_delete_response_pending_fee.xml", FEE_STD_1_0_MAP);
}
@Test
@@ -556,37 +520,14 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
}
@Test
void testSuccess_autoRenewGracePeriod_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
void testSuccess_autoRenewGracePeriod_std_v1() throws Exception {
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_STD_1_0_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_v12() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_priceChanges_v06() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_11.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
void testSuccess_autoRenewGracePeriod_priceChanges_std_v1() throws Exception {
persistResource(
Tld.get("tld")
.asBuilder()
@@ -599,44 +540,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
.build());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_06_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_priceChanges_v11() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_0_12.getUri());
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
Money.of(USD, 11),
TIME_BEFORE_FLOW.minusDays(5),
Money.of(USD, 20)))
.build());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_11_MAP));
}
@Test
void testSuccess_autoRenewGracePeriod_priceChanges_v12() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(
START_OF_TIME,
Money.of(USD, 11),
TIME_BEFORE_FLOW.minusDays(5),
Money.of(USD, 20)))
.build());
setUpAutorenewGracePeriod();
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_12_MAP));
runFlowAssertResponse(loadFile("domain_delete_response_autorenew_fee.xml", FEE_STD_1_0_MAP));
}
@Test
@@ -1296,7 +1200,6 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
@Test
void testSuccess_freeCreation_deletionDuringGracePeriod() throws Exception {
removeServiceExtensionUri(ServiceExtension.FEE_1_00.getUri());
// Deletion during the add grace period should still work even if the credit is 0
setUpSuccessfulTest();
BillingEvent graceBillingEvent =
@@ -1304,7 +1207,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
setUpGracePeriods(
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, domain.getRepoId(), graceBillingEvent));
clock.advanceOneMilli();
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace.xml"));
runFlowAssertResponse(loadFile("domain_delete_response_fee_free_grace_stdv1.xml"));
}
@Test

View File

@@ -0,0 +1,523 @@
// Copyright 2026 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.flows.domain;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistBillingRecurrenceForDomain;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.TestDataHelper.updateSubstitutions;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesException;
import google.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException;
import google.registry.flows.domain.DomainFlowUtils.TransfersAreAlwaysForOneYearException;
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.Host;
import google.registry.model.tld.Tld;
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Tests for {@link DomainInfoFlow} that use the old fee extensions (0.6, 0.11, 0.12). */
public class DomainInfoFlowOldFeeExtensionsTest
extends ProductionSimulatingFeeExtensionsTest<DomainInfoFlow> {
/**
* The domain_info_fee.xml default substitutions common to most tests.
*
* <p>It doesn't set a default value to the COMMAND and PERIOD keys, because they are different in
* every test.
*/
private static final ImmutableMap<String, String> SUBSTITUTION_BASE =
ImmutableMap.of(
"NAME", "example.tld",
"CURRENCY", "USD",
"UNIT", "y");
private static final Pattern OK_PATTERN = Pattern.compile("\"ok\"");
private Host host1;
private Host host2;
private Host host3;
private Domain domain;
@BeforeEach
void beforeEachDomainInfoFlowOldFeeExtensionsTest() {
setEppInput("domain_info.xml");
clock.setTo(DateTime.parse("2005-03-03T22:00:00.000Z"));
sessionMetadata.setRegistrarId("NewRegistrar");
createTld("tld");
persistResource(
JpaTransactionManagerExtension.makeRegistrar1()
.asBuilder()
.setRegistrarId("ClientZ")
.build());
}
private void persistTestEntities(String domainName, boolean inactive) {
host1 = persistActiveHost("ns1.example.tld");
host2 = persistActiveHost("ns1.example.net");
domain =
persistResource(
new Domain.Builder()
.setDomainName(domainName)
.setRepoId("2FF-TLD")
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
.setCreationRegistrarId("TheRegistrar")
.setLastEppUpdateRegistrarId("NewRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setLastEppUpdateTime(DateTime.parse("1999-12-03T09:00:00.0Z"))
.setLastTransferTime(DateTime.parse("2000-04-08T09:00:00.0Z"))
.setRegistrationExpirationTime(DateTime.parse("2005-04-03T22:00:00.0Z"))
.setNameservers(
inactive ? null : ImmutableSet.of(host1.createVKey(), host2.createVKey()))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("2fooBAR")))
.build());
// Set the superordinate domain of ns1.example.com to example.com. In reality, this would have
// happened in the flow that created it, but here we just overwrite it in the database.
host1 = persistResource(host1.asBuilder().setSuperordinateDomain(domain.createVKey()).build());
// Create a subordinate host that is not delegated to by anyone.
host3 =
persistResource(
new Host.Builder()
.setHostName("ns2.example.tld")
.setRepoId("3FF-TLD")
.setSuperordinateDomain(domain.createVKey())
.build());
// Add the subordinate host keys to the existing domain.
domain =
persistResource(
domain
.asBuilder()
.setSubordinateHosts(ImmutableSet.of(host1.getHostName(), host3.getHostName()))
.build());
}
private void persistTestEntities(boolean inactive) {
persistTestEntities("example.tld", inactive);
}
private void doSuccessfulTest(
String expectedXmlFilename,
boolean inactive,
ImmutableMap<String, String> substitutions,
boolean expectHistoryAndBilling)
throws Exception {
assertMutatingFlow(true);
String expected =
loadFile(expectedXmlFilename, updateSubstitutions(substitutions, "ROID", "2FF-TLD"));
if (inactive) {
expected = OK_PATTERN.matcher(expected).replaceAll("\"inactive\"");
}
runFlowAssertResponse(expected);
if (!expectHistoryAndBilling) {
assertNoHistory();
assertNoBillingEvents();
}
}
private void doSuccessfulTest(String expectedXmlFilename, boolean inactive) throws Exception {
doSuccessfulTest(expectedXmlFilename, inactive, ImmutableMap.of(), false);
}
private void doSuccessfulTest(String expectedXmlFilename) throws Exception {
persistTestEntities(false);
doSuccessfulTest(expectedXmlFilename, false);
}
private void doSuccessfulTestNoNameservers(String expectedXmlFilename) throws Exception {
persistTestEntities(true);
doSuccessfulTest(expectedXmlFilename, true);
}
/** sets up a sample recurring billing event as part of the domain creation process. */
private void setUpBillingEventForExistingDomain() {
setUpBillingEventForExistingDomain(DEFAULT, null);
}
private void setUpBillingEventForExistingDomain(
RenewalPriceBehavior renewalPriceBehavior, @Nullable Money renewalPrice) {
domain = persistBillingRecurrenceForDomain(domain, renewalPriceBehavior, renewalPrice);
}
@Test
void testFeeExtension_restoreCommand_pendingDelete_withRenewal() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
persistResource(
domain
.asBuilder()
.setDeletionTime(clock.nowUtc().plusDays(25))
.setRegistrationExpirationTime(clock.nowUtc().minusDays(1))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
doSuccessfulTest(
"domain_info_fee_restore_response_with_renewal.xml", false, ImmutableMap.of(), true);
}
/**
* Test create command. Fee extension version 6 is the only one which supports fee extensions on
* info commands and responses, so we don't need to test the other versions.
*/
@Test
void testFeeExtension_createCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "create", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of(
"COMMAND", "create",
"DESCRIPTION", "create",
"PERIOD", "2",
"FEE", "24.00"),
true);
}
/** Test renew command. */
@Test
void testFeeExtension_renewCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of(
"COMMAND", "renew",
"DESCRIPTION", "renew",
"PERIOD", "2",
"FEE", "22.00"),
true);
}
/** Test transfer command. */
@Test
void testFeeExtension_transferCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "transfer", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of(
"COMMAND", "transfer",
"DESCRIPTION", "renew",
"PERIOD", "1",
"FEE", "11.00"),
true);
}
/** Test restore command. */
@Test
void testFeeExtension_restoreCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest("domain_info_fee_restore_response.xml", false, ImmutableMap.of(), true);
}
@Test
void testFeeExtension_restoreCommand_pendingDelete_noRenewal() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
persistResource(
domain
.asBuilder()
.setDeletionTime(clock.nowUtc().plusDays(25))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
doSuccessfulTest(
"domain_info_fee_restore_response_no_renewal.xml", false, ImmutableMap.of(), true);
}
/** Test create command on a premium label. */
@Test
void testFeeExtension_createCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "create", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_premium_response.xml",
false,
ImmutableMap.of("COMMAND", "create", "DESCRIPTION", "create"),
true);
}
/** Test renew command on a premium label. */
@Test
void testFeeExtension_renewCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_premium_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_anchorTenant() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(NONPREMIUM, null);
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "11.00", "PERIOD", "1"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_internalRegistration() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(SPECIFIED, Money.of(USD, 3));
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "3.00", "PERIOD", "1"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_anchorTenant_multiYear() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "3"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(NONPREMIUM, null);
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "33.00", "PERIOD", "3"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_internalRegistration_multiYear() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "3"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(SPECIFIED, Money.of(USD, 3));
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "9.00", "PERIOD", "3"),
true);
}
@Test
void testFeeExtension_renewCommandStandard_internalRegistration() throws Exception {
createTld("tld");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(SPECIFIED, Money.of(USD, 3));
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "3.00", "PERIOD", "1"),
true);
}
/** Test transfer command on a premium label. */
@Test
void testFeeExtension_transferCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "transfer", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_premium_response.xml",
false,
ImmutableMap.of("COMMAND", "transfer", "DESCRIPTION", "renew"),
true);
}
/** Test restore command on a premium label. */
@Test
void testFeeExtension_restoreCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_restore_premium_response.xml", false, ImmutableMap.of(), true);
}
/** Test setting the currency explicitly to a wrong value. */
@Test
void testFeeExtension_wrongCurrency() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "COMMAND", "create", "CURRENCY", "EUR", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test requesting a period that isn't in years. */
@Test
void testFeeExtension_periodNotInYears() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "create", "PERIOD", "2", "UNIT", "m"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a command that specifies a phase. */
@Test
void testFeeExtension_commandPhase() {
setEppInput("domain_info_fee_command_phase.xml");
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a command that specifies a subphase. */
@Test
void testFeeExtension_commandSubphase() {
setEppInput("domain_info_fee_command_subphase.xml");
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a restore for more than one year. */
@Test
void testFeeExtension_multiyearRestore() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "restore", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a transfer for more than one year. */
@Test
void testFeeExtension_multiyearTransfer() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "transfer", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFeeExtension_unknownCurrency() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "COMMAND", "create", "CURRENCY", "BAD", "PERIOD", "1"));
EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
}

View File

@@ -17,15 +17,12 @@ package google.registry.flows.domain;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.eppcommon.EppXmlTransformer.marshal;
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistBillingRecurrenceForDomain;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.TestDataHelper.updateSubstitutions;
@@ -41,15 +38,9 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.flows.EppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException;
import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesException;
import google.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException;
import google.registry.flows.domain.DomainFlowUtils.TransfersAreAlwaysForOneYearException;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
@@ -586,353 +577,6 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/**
* Test create command. Fee extension version 6 is the only one which supports fee extensions on
* info commands and responses, so we don't need to test the other versions.
*/
@Test
void testFeeExtension_createCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "create", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of(
"COMMAND", "create",
"DESCRIPTION", "create",
"PERIOD", "2",
"FEE", "24.00"),
true);
}
/** Test renew command. */
@Test
void testFeeExtension_renewCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of(
"COMMAND", "renew",
"DESCRIPTION", "renew",
"PERIOD", "2",
"FEE", "22.00"),
true);
}
/** Test transfer command. */
@Test
void testFeeExtension_transferCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "transfer", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of(
"COMMAND", "transfer",
"DESCRIPTION", "renew",
"PERIOD", "1",
"FEE", "11.00"),
true);
}
/** Test restore command. */
@Test
void testFeeExtension_restoreCommand() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
doSuccessfulTest("domain_info_fee_restore_response.xml", false, ImmutableMap.of(), true);
}
@Test
void testFeeExtension_restoreCommand_pendingDelete_noRenewal() throws Exception {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
persistResource(
domain
.asBuilder()
.setDeletionTime(clock.nowUtc().plusDays(25))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
doSuccessfulTest(
"domain_info_fee_restore_response_no_renewal.xml", false, ImmutableMap.of(), true);
}
@Test
void testFeeExtension_restoreCommand_pendingDelete_withRenewal() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
persistResource(
domain
.asBuilder()
.setDeletionTime(clock.nowUtc().plusDays(25))
.setRegistrationExpirationTime(clock.nowUtc().minusDays(1))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
doSuccessfulTest(
"domain_info_fee_restore_response_with_renewal.xml", false, ImmutableMap.of(), true);
}
/** Test create command on a premium label. */
@Test
void testFeeExtension_createCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "create", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_premium_response.xml",
false,
ImmutableMap.of("COMMAND", "create", "DESCRIPTION", "create"),
true);
}
/** Test renew command on a premium label. */
@Test
void testFeeExtension_renewCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_premium_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_anchorTenant() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(NONPREMIUM, null);
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "11.00", "PERIOD", "1"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_internalRegistration() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(SPECIFIED, Money.of(USD, 3));
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "3.00", "PERIOD", "1"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_anchorTenant_multiYear() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "3"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(NONPREMIUM, null);
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "33.00", "PERIOD", "3"),
true);
}
@Test
void testFeeExtension_renewCommandPremium_internalRegistration_multiYear() throws Exception {
createTld("tld");
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 70"))
.build());
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "3"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(SPECIFIED, Money.of(USD, 3));
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "9.00", "PERIOD", "3"),
true);
}
@Test
void testFeeExtension_renewCommandStandard_internalRegistration() throws Exception {
createTld("tld");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "renew", "PERIOD", "1"));
persistTestEntities("example.tld", false);
setUpBillingEventForExistingDomain(SPECIFIED, Money.of(USD, 3));
doSuccessfulTest(
"domain_info_fee_response.xml",
false,
ImmutableMap.of("COMMAND", "renew", "DESCRIPTION", "renew", "FEE", "3.00", "PERIOD", "1"),
true);
}
/** Test transfer command on a premium label. */
@Test
void testFeeExtension_transferCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "transfer", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_premium_response.xml",
false,
ImmutableMap.of("COMMAND", "transfer", "DESCRIPTION", "renew"),
true);
}
/** Test restore command on a premium label. */
@Test
void testFeeExtension_restoreCommandPremium() throws Exception {
createTld("example");
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "NAME", "rich.example", "COMMAND", "restore", "PERIOD", "1"));
persistTestEntities("rich.example", false);
setUpBillingEventForExistingDomain();
doSuccessfulTest(
"domain_info_fee_restore_premium_response.xml", false, ImmutableMap.of(), true);
}
/** Test setting the currency explicitly to a wrong value. */
@Test
void testFeeExtension_wrongCurrency() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "COMMAND", "create", "CURRENCY", "EUR", "PERIOD", "1"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFeeExtension_unknownCurrency() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(
SUBSTITUTION_BASE, "COMMAND", "create", "CURRENCY", "BAD", "PERIOD", "1"));
EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test requesting a period that isn't in years. */
@Test
void testFeeExtension_periodNotInYears() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "create", "PERIOD", "2", "UNIT", "m"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a command that specifies a phase. */
@Test
void testFeeExtension_commandPhase() {
setEppInput("domain_info_fee_command_phase.xml");
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a command that specifies a subphase. */
@Test
void testFeeExtension_commandSubphase() {
setEppInput("domain_info_fee_command_subphase.xml");
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a restore for more than one year. */
@Test
void testFeeExtension_multiyearRestore() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "restore", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/** Test a transfer for more than one year. */
@Test
void testFeeExtension_multiyearTransfer() {
setEppInput(
"domain_info_fee.xml",
updateSubstitutions(SUBSTITUTION_BASE, "COMMAND", "transfer", "PERIOD", "2"));
persistTestEntities(false);
setUpBillingEventForExistingDomain();
EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testIcannActivityReportField_getsLogged() throws Exception {
persistTestEntities(false);

View File

@@ -0,0 +1,827 @@
// Copyright 2026 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.flows.domain;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
import static google.registry.testing.DatabaseHelper.assertPollMessages;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatabaseHelper.loadByKey;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistResources;
import static google.registry.testing.DomainSubject.assertAboutDomains;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static google.registry.testing.TestDataHelper.updateSubstitutions;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.EUR;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import google.registry.flows.EppException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Tld;
import google.registry.testing.DatabaseHelper;
import java.util.Map;
import javax.annotation.Nullable;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Tests for {@link DomainRenewFlow} that use the old fee extensions (0.6, 0.11, 0.12). */
public class DomainRenewFlowOldFeeExtensionsTest
extends ProductionSimulatingFeeExtensionsTest<DomainRenewFlow> {
private static final ImmutableMap<String, String> FEE_BASE_MAP =
ImmutableMap.of(
"NAME", "example.tld",
"PERIOD", "5",
"EX_DATE", "2005-04-03T22:00:00.0Z",
"FEE", "55.00",
"CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_06_MAP =
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.6", "FEE_NS", "fee");
private static final ImmutableMap<String, String> FEE_11_MAP =
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.11", "FEE_NS", "fee11");
private static final ImmutableMap<String, String> FEE_12_MAP =
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.12", "FEE_NS", "fee12");
private final DateTime expirationTime = DateTime.parse("2000-04-03T22:00:00.0Z");
@BeforeEach
void beforeEachDomainRenewFlowOldFeeExtensionsTest() {
clock.setTo(expirationTime.minusMillis(20));
createTld("tld");
persistResource(
loadRegistrar("TheRegistrar")
.asBuilder()
.setBillingAccountMap(ImmutableMap.of(USD, "123", EUR, "567"))
.build());
setEppInput("domain_renew.xml", ImmutableMap.of("DOMAIN", "example.tld", "YEARS", "5"));
}
private void persistDomain(StatusValue... statusValues) throws Exception {
persistDomain(DEFAULT, null, statusValues);
}
private void persistDomain(
RenewalPriceBehavior renewalPriceBehavior,
@Nullable Money renewalPrice,
StatusValue... statusValues)
throws Exception {
Domain domain = DatabaseHelper.newDomain(getUniqueIdFromCommand());
try {
DomainHistory historyEntryDomainCreate =
new DomainHistory.Builder()
.setDomain(domain)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setModificationTime(clock.nowUtc())
.setRegistrarId(domain.getCreationRegistrarId())
.build();
BillingRecurrence autorenewEvent =
new BillingRecurrence.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(getUniqueIdFromCommand())
.setRegistrarId("TheRegistrar")
.setEventTime(expirationTime)
.setRecurrenceEndTime(END_OF_TIME)
.setDomainHistory(historyEntryDomainCreate)
.setRenewalPriceBehavior(renewalPriceBehavior)
.setRenewalPrice(renewalPrice)
.build();
PollMessage.Autorenew autorenewPollMessage =
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setRegistrarId("TheRegistrar")
.setEventTime(expirationTime)
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setHistoryEntry(historyEntryDomainCreate)
.build();
Domain newDomain =
domain
.asBuilder()
.setRegistrationExpirationTime(expirationTime)
.setStatusValues(ImmutableSet.copyOf(statusValues))
.setAutorenewBillingEvent(autorenewEvent.createVKey())
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.build();
persistResources(
ImmutableSet.of(
historyEntryDomainCreate, autorenewEvent,
autorenewPollMessage, newDomain));
} catch (Exception e) {
throw new RuntimeException("Error persisting domain", e);
}
clock.advanceOneMilli();
}
@Test
void testFailure_wrongFeeAmount_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistDomain();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistDomain();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistDomain();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
persistDomain();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
persistDomain();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
persistDomain();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v06() throws Exception {
setEppInput("domain_renew_fee_bad_scale.xml", FEE_06_MAP);
persistDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v11() throws Exception {
setEppInput("domain_renew_fee_bad_scale.xml", FEE_11_MAP);
persistDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v12() throws Exception {
setEppInput("domain_renew_fee_bad_scale.xml", FEE_12_MAP);
persistDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName_v06() throws Exception {
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "500");
setEppInput("domain_renew_fee.xml", customFeeMap);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.setPremiumList(persistPremiumList("tld", USD, "example,USD 100"))
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response_fee.xml",
ImmutableMap.of(
"NAME",
"example.tld",
"PERIOD",
"5",
"EX_DATE",
"2005-04-03T22:00:00.0Z",
"FEE",
"500.00",
"CURRENCY",
"USD",
"FEE_VERSION",
"fee-0.6",
"FEE_NS",
"fee")));
BillingEvent billingEvent =
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
assertThat(billingEvent.getAllocationToken()).isEmpty();
}
@Test
void testSuccess_internalRegiration_premiumDomain_v06() throws Exception {
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 100"))
.build());
persistDomain(SPECIFIED, Money.of(USD, 2));
setRegistrarIdForFlow("NewRegistrar");
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "10.00");
setEppInput("domain_renew_fee.xml", customFeeMap);
doSuccessfulTest(
"domain_renew_response_fee.xml",
5,
"NewRegistrar",
UserPrivileges.SUPERUSER,
customFeeMap,
Money.of(USD, 10),
SPECIFIED,
Money.of(USD, 2));
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response_fee.xml",
ImmutableMap.of(
"NAME",
"example.tld",
"PERIOD",
"5",
"EX_DATE",
"2005-04-03T22:00:00.0Z",
"FEE",
"49.50",
"CURRENCY",
"USD",
"FEE_VERSION",
"fee-0.6",
"FEE_NS",
"fee")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response_fee.xml",
ImmutableMap.of(
"NAME",
"example.tld",
"PERIOD",
"5",
"EX_DATE",
"2005-04-03T22:00:00.0Z",
"FEE",
"49.50",
"CURRENCY",
"USD",
"FEE_VERSION",
"fee-0.11",
"FEE_NS",
"fee")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response_fee.xml",
ImmutableMap.of(
"NAME",
"example.tld",
"PERIOD",
"5",
"EX_DATE",
"2005-04-03T22:00:00.0Z",
"FEE",
"49.50",
"CURRENCY",
"USD",
"FEE_VERSION",
"fee-0.12",
"FEE_NS",
"fee")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v06() throws Exception {
setEppInput("domain_renew_fee_refundable.xml", FEE_06_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v11() throws Exception {
setEppInput("domain_renew_fee_refundable.xml", FEE_11_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v12() throws Exception {
setEppInput("domain_renew_fee_refundable.xml", FEE_12_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v06() throws Exception {
setEppInput("domain_renew_fee_grace_period.xml", FEE_06_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v11() throws Exception {
setEppInput("domain_renew_fee_grace_period.xml", FEE_11_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v12() throws Exception {
setEppInput("domain_renew_fee_grace_period.xml", FEE_12_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v06() throws Exception {
setEppInput("domain_renew_fee_applied.xml", FEE_06_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v11() throws Exception {
setEppInput("domain_renew_fee_applied.xml", FEE_11_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v12() throws Exception {
setEppInput("domain_renew_fee_applied.xml", FEE_12_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_fee_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
}
@Test
void testSuccess_fee_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
}
@Test
void testSuccess_fee_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
setEppInput("domain_renew_fee_defaults.xml", FEE_06_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
setEppInput("domain_renew_fee_defaults.xml", FEE_11_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
setEppInput("domain_renew_fee_defaults.xml", FEE_12_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
}
@Test
void testSuccess_anchorTenant_premiumDomain_v06() throws Exception {
persistResource(
Tld.get("tld")
.asBuilder()
.setPremiumList(persistPremiumList("tld", USD, "example,USD 100"))
.build());
persistDomain(NONPREMIUM, null);
setRegistrarIdForFlow("NewRegistrar");
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "55.00");
setEppInput("domain_renew_fee.xml", customFeeMap);
doSuccessfulTest(
"domain_renew_response_fee.xml",
5,
"NewRegistrar",
UserPrivileges.SUPERUSER,
customFeeMap,
Money.of(USD, 55),
NONPREMIUM,
null);
}
@Test
void testSuccess_customLogicFee_v06() throws Exception {
// The "costly-renew" domain has an additional RENEW fee of 100 from custom logic on top of the
// normal $11 standard renew price for this TLD.
ImmutableMap<String, String> customFeeMap =
updateSubstitutions(
FEE_06_MAP,
"NAME",
"costly-renew.tld",
"PERIOD",
"1",
"EX_DATE",
"2001-04-03T22:00:00.0Z",
"FEE",
"111.00");
setEppInput("domain_renew_fee.xml", customFeeMap);
persistDomain();
doSuccessfulTest(
"domain_renew_response_fee.xml",
1,
"TheRegistrar",
UserPrivileges.NORMAL,
customFeeMap,
Money.of(USD, 111));
}
private void doSuccessfulTest(String responseFilename, int renewalYears) throws Exception {
doSuccessfulTest(responseFilename, renewalYears, ImmutableMap.of());
}
private void doSuccessfulTest(
String responseFilename, int renewalYears, Map<String, String> substitutions)
throws Exception {
doSuccessfulTest(
responseFilename,
renewalYears,
"TheRegistrar",
UserPrivileges.NORMAL,
substitutions,
Money.of(USD, 11).multipliedBy(renewalYears),
DEFAULT,
null);
}
private void doSuccessfulTest(
String responseFilename,
int renewalYears,
String renewalClientId,
UserPrivileges userPrivileges,
Map<String, String> substitutions,
Money totalRenewCost)
throws Exception {
doSuccessfulTest(
responseFilename,
renewalYears,
renewalClientId,
userPrivileges,
substitutions,
totalRenewCost,
DEFAULT,
null);
}
private void doSuccessfulTest(
String responseFilename,
int renewalYears,
String renewalClientId,
UserPrivileges userPrivileges,
Map<String, String> substitutions,
Money totalRenewCost,
RenewalPriceBehavior renewalPriceBehavior,
@Nullable Money renewalPrice)
throws Exception {
assertMutatingFlow(true);
DateTime currentExpiration = reloadResourceByForeignKey().getRegistrationExpirationTime();
DateTime newExpiration = currentExpiration.plusYears(renewalYears);
runFlowAssertResponse(
CommitMode.LIVE, userPrivileges, loadFile(responseFilename, substitutions));
Domain domain = reloadResourceByForeignKey();
assertLastHistoryContainsResource(domain);
DomainHistory historyEntryDomainRenew =
getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_RENEW, DomainHistory.class);
assertThat(loadByKey(domain.getAutorenewBillingEvent()).getEventTime())
.isEqualTo(newExpiration);
assertAboutDomains()
.that(domain)
.isActiveAt(clock.nowUtc())
.and()
.hasRegistrationExpirationTime(newExpiration)
.and()
.hasOneHistoryEntryEachOfTypes(
HistoryEntry.Type.DOMAIN_CREATE, HistoryEntry.Type.DOMAIN_RENEW)
.and()
.hasLastEppUpdateTime(clock.nowUtc())
.and()
.hasLastEppUpdateRegistrarId(renewalClientId);
assertAboutHistoryEntries().that(historyEntryDomainRenew).hasPeriodYears(renewalYears);
BillingEvent renewBillingEvent =
new BillingEvent.Builder()
.setReason(Reason.RENEW)
.setTargetId(getUniqueIdFromCommand())
.setRegistrarId(renewalClientId)
.setCost(totalRenewCost)
.setPeriodYears(renewalYears)
.setEventTime(clock.nowUtc())
.setBillingTime(clock.nowUtc().plus(Tld.get("tld").getRenewGracePeriodLength()))
.setDomainHistory(historyEntryDomainRenew)
.build();
assertBillingEvents(
renewBillingEvent,
new BillingRecurrence.Builder()
.setReason(Reason.RENEW)
.setRenewalPriceBehavior(renewalPriceBehavior)
.setRenewalPrice(renewalPrice)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(getUniqueIdFromCommand())
.setRegistrarId("TheRegistrar")
.setEventTime(expirationTime)
.setRecurrenceEndTime(clock.nowUtc())
.setDomainHistory(
getOnlyHistoryEntryOfType(
domain, HistoryEntry.Type.DOMAIN_CREATE, DomainHistory.class))
.build(),
new BillingRecurrence.Builder()
.setReason(Reason.RENEW)
.setRenewalPriceBehavior(renewalPriceBehavior)
.setRenewalPrice(renewalPrice)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(getUniqueIdFromCommand())
.setRegistrarId("TheRegistrar")
.setEventTime(domain.getRegistrationExpirationTime())
.setRecurrenceEndTime(END_OF_TIME)
.setDomainHistory(historyEntryDomainRenew)
.build());
// There should only be the new autorenew poll message, as the old one will have been deleted
// since it had no messages left to deliver.
assertPollMessages(
new PollMessage.Autorenew.Builder()
.setTargetId(getUniqueIdFromCommand())
.setRegistrarId("TheRegistrar")
.setEventTime(domain.getRegistrationExpirationTime())
.setAutorenewEndTime(END_OF_TIME)
.setMsg("Domain was auto-renewed.")
.setHistoryEntry(historyEntryDomainRenew)
.build());
assertGracePeriods(
domain.getGracePeriods(),
ImmutableMap.of(
GracePeriod.create(
GracePeriodStatus.RENEW,
domain.getRepoId(),
clock.nowUtc().plus(Tld.get("tld").getRenewGracePeriodLength()),
renewalClientId,
null),
renewBillingEvent));
}
}

View File

@@ -119,12 +119,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
"FEE", "55.00",
"CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_06_MAP =
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "0.6", "FEE_NS", "fee");
private static final ImmutableMap<String, String> FEE_11_MAP =
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "0.11", "FEE_NS", "fee11");
private static final ImmutableMap<String, String> FEE_12_MAP =
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "0.12", "FEE_NS", "fee12");
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00");
private final DateTime expirationTime = DateTime.parse("2000-04-03T22:00:00.0Z");
@@ -390,7 +386,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testSuccess_internalRegiration_premiumDomain() throws Exception {
void testSuccess_internalRegiration_premiumDomain_std_v1() throws Exception {
persistResource(
Tld.get("tld")
.asBuilder()
@@ -398,7 +394,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
.build());
persistDomain(SPECIFIED, Money.of(USD, 2));
setRegistrarIdForFlow("NewRegistrar");
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "10.00");
ImmutableMap<String, String> customFeeMap =
updateSubstitutions(FEE_STD_1_0_MAP, "FEE", "10.00");
setEppInput("domain_renew_fee.xml", customFeeMap);
doSuccessfulTest(
"domain_renew_response_fee.xml",
@@ -427,7 +424,7 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testSuccess_anchorTenant_premiumDomain() throws Exception {
void testSuccess_anchorTenant_premiumDomain_std_v1() throws Exception {
persistResource(
Tld.get("tld")
.asBuilder()
@@ -435,7 +432,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
.build());
persistDomain(NONPREMIUM, null);
setRegistrarIdForFlow("NewRegistrar");
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "55.00");
ImmutableMap<String, String> customFeeMap =
updateSubstitutions(FEE_STD_1_0_MAP, "FEE", "55.00");
setEppInput("domain_renew_fee.xml", customFeeMap);
doSuccessfulTest(
"domain_renew_response_fee.xml",
@@ -449,12 +447,12 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testSuccess_customLogicFee() throws Exception {
void testSuccess_customLogicFee_std_v1() throws Exception {
// The "costly-renew" domain has an additional RENEW fee of 100 from custom logic on top of the
// normal $11 standard renew price for this TLD.
ImmutableMap<String, String> customFeeMap =
updateSubstitutions(
FEE_06_MAP,
FEE_STD_1_0_MAP,
"NAME",
"costly-renew.tld",
"PERIOD",
@@ -475,121 +473,45 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testSuccess_fee_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
void testSuccess_fee_std_v1() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_STD_1_0_MAP);
}
@Test
void testSuccess_fee_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
void testSuccess_fee_withDefaultAttributes_std_v1() throws Exception {
setEppInput("domain_renew_fee_defaults.xml", FEE_STD_1_0_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
}
@Test
void testSuccess_fee_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
setEppInput("domain_renew_fee_defaults.xml", FEE_06_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_06_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
setEppInput("domain_renew_fee_defaults.xml", FEE_11_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_11_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
setEppInput("domain_renew_fee_defaults.xml", FEE_12_MAP);
persistDomain();
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_12_MAP);
doSuccessfulTest("domain_renew_response_fee.xml", 5, FEE_STD_1_0_MAP);
}
@Test
void testFailure_fee_unknownCurrency() {
setEppInput("domain_renew_fee.xml", updateSubstitutions(FEE_06_MAP, "CURRENCY", "BAD"));
setEppInput("domain_renew_fee.xml", updateSubstitutions(FEE_STD_1_0_MAP, "CURRENCY", "BAD"));
EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::persistDomain);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v06() throws Exception {
setEppInput("domain_renew_fee_refundable.xml", FEE_06_MAP);
void testFailure_refundableFee_std_v1() throws Exception {
setEppInput("domain_renew_fee_refundable.xml", FEE_STD_1_0_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v11() throws Exception {
setEppInput("domain_renew_fee_refundable.xml", FEE_11_MAP);
void testFailure_gracePeriodFee_std_v1() throws Exception {
setEppInput("domain_renew_fee_grace_period.xml", FEE_STD_1_0_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v12() throws Exception {
setEppInput("domain_renew_fee_refundable.xml", FEE_12_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v06() throws Exception {
setEppInput("domain_renew_fee_grace_period.xml", FEE_06_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v11() throws Exception {
setEppInput("domain_renew_fee_grace_period.xml", FEE_11_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v12() throws Exception {
setEppInput("domain_renew_fee_grace_period.xml", FEE_12_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v06() throws Exception {
setEppInput("domain_renew_fee_applied.xml", FEE_06_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v11() throws Exception {
setEppInput("domain_renew_fee_applied.xml", FEE_11_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v12() throws Exception {
setEppInput("domain_renew_fee_applied.xml", FEE_12_MAP);
void testFailure_appliedFee_std_v1() throws Exception {
setEppInput("domain_renew_fee_applied.xml", FEE_STD_1_0_MAP);
persistDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -991,8 +913,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testFailure_wrongFeeAmount_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
void testFailure_wrongFeeAmount_std_v1() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
@@ -1004,34 +926,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testFailure_wrongFeeAmount_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistDomain();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
persistDomain();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
void testFailure_wrongCurrency_std_v1() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
@@ -1050,64 +946,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testFailure_wrongCurrency_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
persistDomain();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistResource(
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 20))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.build());
persistDomain();
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v06() throws Exception {
setEppInput("domain_renew_fee_bad_scale.xml", FEE_06_MAP);
persistDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v11() throws Exception {
setEppInput("domain_renew_fee_bad_scale.xml", FEE_11_MAP);
persistDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v12() throws Exception {
setEppInput("domain_renew_fee_bad_scale.xml", FEE_12_MAP);
void testFailure_feeGivenInWrongScale_std_v1() throws Exception {
setEppInput("domain_renew_fee_bad_scale.xml", FEE_STD_1_0_MAP);
persistDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -1522,8 +1362,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName() throws Exception {
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_06_MAP, "FEE", "500");
void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName_std_v1() throws Exception {
ImmutableMap<String, String> customFeeMap = updateSubstitutions(FEE_STD_1_0_MAP, "FEE", "500");
setEppInput("domain_renew_fee.xml", customFeeMap);
persistDomain();
AllocationToken defaultToken1 =
@@ -1556,9 +1396,9 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
"CURRENCY",
"USD",
"FEE_VERSION",
"0.6",
"epp:fee-1.0",
"FEE_NS",
"fee")));
"fee1_00")));
BillingEvent billingEvent =
Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class));
assertThat(billingEvent.getTargetId()).isEqualTo("example.tld");
@@ -1662,8 +1502,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
void testSuccess_wrongFeeAmountTooHigh_defaultToken_std_v1() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
@@ -1694,136 +1534,14 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
"CURRENCY",
"USD",
"FEE_VERSION",
"0.6",
"epp:fee-1.0",
"FEE_NS",
"fee")));
"fee1_00")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v06() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response_fee.xml",
ImmutableMap.of(
"NAME",
"example.tld",
"PERIOD",
"5",
"EX_DATE",
"2005-04-03T22:00:00.0Z",
"FEE",
"49.50",
"CURRENCY",
"USD",
"FEE_VERSION",
"0.11",
"FEE_NS",
"fee")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v11() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_11_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_12_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(
new AllocationToken.Builder()
.setToken("aaaaa")
.setTokenType(DEFAULT_PROMO)
.setDiscountFraction(0.5)
.setDiscountYears(1)
.setAllowedTlds(ImmutableSet.of("tld"))
.build());
persistResource(
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken1.createVKey()))
.build());
runFlowAssertResponse(
loadFile(
"domain_renew_response_fee.xml",
ImmutableMap.of(
"NAME",
"example.tld",
"PERIOD",
"5",
"EX_DATE",
"2005-04-03T22:00:00.0Z",
"FEE",
"49.50",
"CURRENCY",
"USD",
"FEE_VERSION",
"0.12",
"FEE_NS",
"fee")));
}
@Test
void testFailure_wrongFeeAmountTooLow_defaultToken_v12() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_06_MAP);
void testFailure_wrongFeeAmountTooLow_defaultToken_std_v1() throws Exception {
setEppInput("domain_renew_fee.xml", FEE_STD_1_0_MAP);
persistDomain();
AllocationToken defaultToken1 =
persistResource(

View File

@@ -0,0 +1,351 @@
// Copyright 2026 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.flows.domain;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.EUR;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.flows.EppException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Tld;
import google.registry.testing.DatabaseHelper;
import java.util.Map;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Tests for {@link DomainRestoreRequestFlow} that use the old fee extensions (0.6, 0.11, 0.12). */
public class DomainRestoreRequestFlowOldFeeExtensionsTest
extends ProductionSimulatingFeeExtensionsTest<DomainRestoreRequestFlow> {
private static final ImmutableMap<String, String> FEE_06_MAP =
ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee", "CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_11_MAP =
ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11", "CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_12_MAP =
ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12", "CURRENCY", "USD");
@BeforeEach
void beforeEachDomainRestoreRequestFlowOldFeeExtensionsTest() {
createTld("tld");
persistResource(
loadRegistrar("TheRegistrar")
.asBuilder()
.setBillingAccountMap(ImmutableMap.of(USD, "123", EUR, "567"))
.build());
setEppInput("domain_update_restore_request.xml", ImmutableMap.of("DOMAIN", "example.tld"));
}
@Test
void testFailure_wrongFeeAmount_v06() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
persistPendingDeleteDomain();
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v11() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
persistPendingDeleteDomain();
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v12() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
persistPendingDeleteDomain();
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_06_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_11_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_12_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_06_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_11_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_12_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_06_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
}
@Test
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_11_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
}
@Test
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_12_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
}
@Test
void testSuccess_fee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
}
@Test
void testSuccess_fee_v06_noRenewal() throws Exception {
setEppInput("domain_update_restore_request_fee_no_renewal.xml", FEE_06_MAP);
persistPendingDeleteDomain(clock.nowUtc().plusMonths(6));
runFlowAssertResponse(
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_06_MAP));
}
@Test
void testSuccess_fee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
}
@Test
void testSuccess_fee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
}
@Test
void testFailure_refundableFee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_06_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_11_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_12_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_06_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_11_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_12_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
private void runWrongCurrencyTest(Map<String, String> substitutions) throws Exception {
setEppInput("domain_update_restore_request_fee.xml", substitutions);
persistPendingDeleteDomain();
persistResource(
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 13)))
.setRestoreBillingCost(Money.of(EUR, 11))
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 7)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(EUR)))
.setServerStatusChangeBillingCost(Money.of(EUR, 19))
.setRegistryLockOrUnlockBillingCost(Money.of(EUR, 0))
.build());
EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v06() throws Exception {
runWrongCurrencyTest(FEE_06_MAP);
}
@Test
void testFailure_wrongCurrency_v11() throws Exception {
runWrongCurrencyTest(FEE_11_MAP);
}
@Test
void testFailure_wrongCurrency_v12() throws Exception {
runWrongCurrencyTest(FEE_12_MAP);
}
@Test
void testFailure_premiumBlocked_v12() throws Exception {
createTld("example");
setEppInput("domain_update_restore_request_premium.xml", FEE_12_MAP);
persistPendingDeleteDomain();
// Modify the Registrar to block premium names.
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
EppException thrown = assertThrows(PremiumNameBlockedException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_premiumNotBlocked_andNoRenewal_v12() throws Exception {
createTld("example");
setEppInput("domain_update_restore_request_premium_no_renewal.xml", FEE_12_MAP);
persistPendingDeleteDomain(clock.nowUtc().plusYears(2));
runFlowAssertResponse(
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_12_MAP));
}
@Test
void testFailure_fee_unknownCurrency_v12() {
ImmutableMap<String, String> substitutions =
ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12", "CURRENCY", "BAD");
setEppInput("domain_update_restore_request_fee.xml", substitutions);
EppException thrown =
assertThrows(UnknownCurrencyEppException.class, this::persistPendingDeleteDomain);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
private Domain persistPendingDeleteDomain() throws Exception {
// The domain is now past what had been its expiration date at the time of deletion.
return persistPendingDeleteDomain(clock.nowUtc().minusDays(5));
}
private Domain persistPendingDeleteDomain(DateTime expirationTime) throws Exception {
Domain domain = persistResource(DatabaseHelper.newDomain(getUniqueIdFromCommand()));
HistoryEntry historyEntry =
persistResource(
new DomainHistory.Builder()
.setType(HistoryEntry.Type.DOMAIN_DELETE)
.setModificationTime(clock.nowUtc())
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
.setDomain(domain)
.build());
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(expirationTime)
.setDeletionTime(clock.nowUtc().plusDays(35))
.addGracePeriod(
GracePeriod.create(
GracePeriodStatus.REDEMPTION,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"TheRegistrar",
null))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.setDeletePollMessage(
persistResource(
new PollMessage.OneTime.Builder()
.setRegistrarId("TheRegistrar")
.setEventTime(clock.nowUtc().plusDays(5))
.setHistoryEntry(historyEntry)
.build())
.createVKey())
.build());
clock.advanceOneMilli();
return domain;
}
}

View File

@@ -86,12 +86,8 @@ import org.junit.jupiter.api.Test;
/** Unit tests for {@link DomainRestoreRequestFlow}. */
class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreRequestFlow, Domain> {
private static final ImmutableMap<String, String> FEE_06_MAP =
ImmutableMap.of("FEE_VERSION", "0.6", "FEE_NS", "fee", "CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_11_MAP =
ImmutableMap.of("FEE_VERSION", "0.11", "FEE_NS", "fee11", "CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_12_MAP =
ImmutableMap.of("FEE_VERSION", "0.12", "FEE_NS", "fee12", "CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00", "CURRENCY", "USD");
@BeforeEach
void initDomainTest() {
@@ -308,7 +304,7 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
@Test
void testSuccess_autorenewEndTimeIsCleared() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
setEppInput("domain_update_restore_request_fee.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
persistResource(
reloadResourceByForeignKey()
@@ -316,64 +312,31 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
.setAutorenewEndTime(Optional.of(clock.nowUtc().plusYears(2)))
.build());
assertThat(reloadResourceByForeignKey().getAutorenewEndTime()).isPresent();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
runFlowAssertResponse(
loadFile("domain_update_restore_request_response_fee.xml", FEE_STD_1_0_MAP));
assertThat(reloadResourceByForeignKey().getAutorenewEndTime()).isEmpty();
}
@Test
void testSuccess_fee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
void testSuccess_fee_std_v1() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
}
@Test
void testSuccess_fee_v06_noRenewal() throws Exception {
setEppInput("domain_update_restore_request_fee_no_renewal.xml", FEE_06_MAP);
persistPendingDeleteDomain(clock.nowUtc().plusMonths(6));
runFlowAssertResponse(
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_06_MAP));
loadFile("domain_update_restore_request_response_fee.xml", FEE_STD_1_0_MAP));
}
@Test
void testSuccess_fee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
void testSuccess_fee_withDefaultAttributes_std_v1() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
runFlowAssertResponse(
loadFile("domain_update_restore_request_response_fee.xml", FEE_STD_1_0_MAP));
}
@Test
void testSuccess_fee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
}
@Test
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_06_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_06_MAP));
}
@Test
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_11_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_11_MAP));
}
@Test
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_defaults.xml", FEE_12_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_fee.xml", FEE_12_MAP));
}
@Test
void testFailure_fee_unknownCurrency() {
void testFailure_fee_unknownCurrency_std_v1() {
ImmutableMap<String, String> substitutions =
ImmutableMap.of("FEE_VERSION", "0.12", "FEE_NS", "fee12", "CURRENCY", "BAD");
ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00", "CURRENCY", "BAD");
setEppInput("domain_update_restore_request_fee.xml", substitutions);
EppException thrown =
assertThrows(UnknownCurrencyEppException.class, this::persistPendingDeleteDomain);
@@ -381,72 +344,24 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
}
@Test
void testFailure_refundableFee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_06_MAP);
void testFailure_refundableFee_std_v1() throws Exception {
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_11_MAP);
void testFailure_gracePeriodFee_std_v1() throws Exception {
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_refundable.xml", FEE_12_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_06_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_11_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_grace_period.xml", FEE_12_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_06_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_11_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_12_MAP);
void testFailure_appliedFee_std_v1() throws Exception {
setEppInput("domain_update_restore_request_fee_applied.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -455,18 +370,19 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
@Test
void testSuccess_premiumNotBlocked() throws Exception {
createTld("example");
setEppInput("domain_update_restore_request_premium.xml");
setEppInput("domain_update_restore_request_premium.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
runFlowAssertResponse(loadFile("domain_update_restore_request_response_premium.xml"));
runFlowAssertResponse(
loadFile("domain_update_restore_request_response_premium.xml", FEE_STD_1_0_MAP));
}
@Test
void testSuccess_premiumNotBlocked_andNoRenewal() throws Exception {
void testSuccess_premiumNotBlocked_andNoRenewal_std_v1() throws Exception {
createTld("example");
setEppInput("domain_update_restore_request_premium_no_renewal.xml");
setEppInput("domain_update_restore_request_premium_no_renewal.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain(clock.nowUtc().plusYears(2));
runFlowAssertResponse(
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_12_MAP));
loadFile("domain_update_restore_request_response_fee_no_renewal.xml", FEE_STD_1_0_MAP));
}
@Test
@@ -484,14 +400,14 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
@Test
void testSuccess_superuserOverridesPremiumNameBlock() throws Exception {
createTld("example");
setEppInput("domain_update_restore_request_premium.xml");
setEppInput("domain_update_restore_request_premium.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
// Modify the Registrar to block premium names.
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());
runFlowAssertResponse(
CommitMode.LIVE,
UserPrivileges.SUPERUSER,
loadFile("domain_update_restore_request_response_premium.xml"));
loadFile("domain_update_restore_request_response_premium.xml", FEE_STD_1_0_MAP));
}
@Test
@@ -538,26 +454,8 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
}
@Test
void testFailure_wrongFeeAmount_v06() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_06_MAP);
persistPendingDeleteDomain();
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v11() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_11_MAP);
persistPendingDeleteDomain();
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongFeeAmount_v12() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_12_MAP);
void testFailure_wrongFeeAmount_std_v1() throws Exception {
setEppInput("domain_update_restore_request_fee.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
persistResource(Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 100)).build());
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
@@ -584,39 +482,13 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
}
@Test
void testFailure_wrongCurrency_v06() throws Exception {
runWrongCurrencyTest(FEE_06_MAP);
void testFailure_wrongCurrency_std_v1() throws Exception {
runWrongCurrencyTest(FEE_STD_1_0_MAP);
}
@Test
void testFailure_wrongCurrency_v11() throws Exception {
runWrongCurrencyTest(FEE_11_MAP);
}
@Test
void testFailure_wrongCurrency_v12() throws Exception {
runWrongCurrencyTest(FEE_12_MAP);
}
@Test
void testFailure_feeGivenInWrongScale_v06() throws Exception {
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_06_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v11() throws Exception {
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_11_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v12() throws Exception {
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_12_MAP);
void testFailure_feeGivenInWrongScale_std_v1() throws Exception {
setEppInput("domain_update_restore_request_fee_bad_scale.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -738,9 +610,9 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
}
@Test
void testFailure_premiumBlocked() throws Exception {
void testFailure_premiumBlocked_std_v1() throws Exception {
createTld("example");
setEppInput("domain_update_restore_request_premium.xml");
setEppInput("domain_update_restore_request_premium.xml", FEE_STD_1_0_MAP);
persistPendingDeleteDomain();
// Modify the Registrar to block premium names.
persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build());

View File

@@ -0,0 +1,897 @@
// Copyright 2026 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.flows.domain;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_REQUESTED_TIME;
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
import static google.registry.testing.DatabaseHelper.assertBillingEventsEqual;
import static google.registry.testing.DatabaseHelper.assertPollMessagesEqual;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatabaseHelper.getOnlyPollMessage;
import static google.registry.testing.DatabaseHelper.getPollMessages;
import static google.registry.testing.DatabaseHelper.loadByKey;
import static google.registry.testing.DatabaseHelper.loadByKeys;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DomainSubject.assertAboutDomains;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static google.registry.testing.HostSubject.assertAboutHosts;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.cloud.tasks.v2.HttpMethod;
import com.google.common.base.Ascii;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import google.registry.batch.ResaveEntityAction;
import google.registry.flows.EppException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import google.registry.flows.domain.DomainFlowUtils.UnsupportedFeeAttributeException;
import google.registry.flows.exceptions.TransferPeriodZeroAndFeeTransferExtensionException;
import google.registry.model.billing.BillingBase;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingCancellation;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.Period;
import google.registry.model.domain.Period.Unit;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.Host;
import google.registry.model.poll.PendingActionNotificationResponse;
import google.registry.model.poll.PollMessage;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.tld.Tld;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.CloudTasksHelper.TaskMatcher;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Tests for {@link DomainTransferRequestFlow} that use the old fee extensions (0.6, 0.11, 0.12).
*/
public class DomainTransferRequestFlowOldFeeExtensionsTest
extends ProductionSimulatingFeeExtensionsTest<DomainTransferRequestFlow> {
private static final ImmutableMap<String, String> BASE_FEE_MAP =
new ImmutableMap.Builder<String, String>()
.put("DOMAIN", "example.tld")
.put("YEARS", "1")
.put("AMOUNT", "11.00")
.put("CURRENCY", "USD")
.build();
private static final ImmutableMap<String, String> FEE_06_MAP =
new ImmutableMap.Builder<String, String>()
.putAll(BASE_FEE_MAP)
.put("FEE_VERSION", "fee-0.6")
.put("FEE_NS", "fee")
.build();
private static final ImmutableMap<String, String> FEE_11_MAP =
new ImmutableMap.Builder<String, String>()
.putAll(BASE_FEE_MAP)
.put("FEE_VERSION", "fee-0.11")
.put("FEE_NS", "fee11")
.build();
private static final ImmutableMap<String, String> FEE_12_MAP =
new ImmutableMap.Builder<String, String>()
.putAll(BASE_FEE_MAP)
.put("FEE_VERSION", "fee-0.12")
.put("FEE_NS", "fee12")
.build();
private static final ImmutableMap<String, String> RICH_DOMAIN_MAP =
ImmutableMap.<String, String>builder()
.put("DOMAIN", "rich.example")
.put("YEARS", "1")
.put("AMOUNT", "100.00")
.put("CURRENCY", "USD")
.put("FEE_VERSION", "fee-0.12")
.put("FEE_NS", "fee12")
.build();
private static final DateTime TRANSFER_REQUEST_TIME = DateTime.parse("2000-06-06T22:00:00.0Z");
private static final DateTime TRANSFER_EXPIRATION_TIME =
TRANSFER_REQUEST_TIME.plus(Tld.DEFAULT_AUTOMATIC_TRANSFER_LENGTH);
private static final Duration TIME_SINCE_REQUEST = Duration.standardDays(3);
private static final int EXTENDED_REGISTRATION_YEARS = 1;
private static final DateTime REGISTRATION_EXPIRATION_TIME =
DateTime.parse("2001-09-08T22:00:00.0Z");
private static final DateTime EXTENDED_REGISTRATION_EXPIRATION_TIME =
REGISTRATION_EXPIRATION_TIME.plusYears(EXTENDED_REGISTRATION_YEARS);
private Contact contact;
private Domain domain;
private Host subordinateHost;
private DomainHistory historyEntryDomainCreate;
@BeforeEach
void beforeEachDomainTransferRequestFlowOldFeeExtensionsTest() {
setEppInput("domain_transfer_request.xml");
setRegistrarIdForFlow("NewRegistrar");
clock.setTo(TRANSFER_REQUEST_TIME.plus(TIME_SINCE_REQUEST));
}
@Test
void testFailure_wrongFeeAmount_v06() {
setupDomain("example", "tld");
runWrongFeeAmountTest(FEE_06_MAP);
}
@Test
void testFailure_wrongFeeAmount_v11() {
setupDomain("example", "tld");
runWrongFeeAmountTest(FEE_11_MAP);
}
@Test
void testFailure_wrongFeeAmount_v12() {
setupDomain("example", "tld");
runWrongFeeAmountTest(FEE_12_MAP);
}
@Test
void testFailure_appliedFee_v06() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_06_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v11() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_11_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v12() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_12_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v06() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_06_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v11() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_11_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v12() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_12_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee_defaults.xml",
"domain_transfer_request_response_fee.xml",
FEE_06_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee_defaults.xml",
"domain_transfer_request_response_fee.xml",
FEE_11_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee_defaults.xml",
"domain_transfer_request_response_fee.xml",
FEE_12_MAP);
}
@Test
void testFailure_refundableFee_v06() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_06_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v11() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_11_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v12() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_12_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_wrongCurrency_v06() {
runWrongCurrencyTest(FEE_06_MAP);
}
@Test
void testFailure_wrongCurrency_v11() {
runWrongCurrencyTest(FEE_11_MAP);
}
@Test
void testFailure_wrongCurrency_v12() {
runWrongCurrencyTest(FEE_12_MAP);
}
@Test
void testFailure_feeGivenInWrongScale_v06() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
CurrencyValueScaleException.class,
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_06_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v11() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
CurrencyValueScaleException.class,
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_11_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v12() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
CurrencyValueScaleException.class,
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_12_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_fee_v06() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_06_MAP);
}
@Test
void testSuccess_fee_v11() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_11_MAP);
}
@Test
void testSuccess_fee_v12() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_12_MAP);
}
@Test
void testSuccess_customLogicFee_v06() throws Exception {
setupDomain("expensive-domain", "foo");
clock.advanceOneMilli();
doSuccessfulTest(
"domain_transfer_request_separate_fees.xml",
"domain_transfer_request_response_fees.xml",
domain.getRegistrationExpirationTime().plusYears(1),
new ImmutableMap.Builder<String, String>()
.put("DOMAIN", "expensive-domain.foo")
.put("YEARS", "1")
.put("AMOUNT", "111.00")
.put("EXDATE", "2002-09-08T22:00:00.0Z")
.put("FEE_VERSION", "fee-0.6")
.put("FEE_NS", "fee")
.build(),
Optional.of(Money.of(USD, 111)));
}
@Test
void testSuccess_premiumNotBlocked_v12() throws Exception {
setupDomain("rich", "example");
clock.advanceOneMilli();
// We don't verify the results; just check that the flow doesn't fail.
runTest("domain_transfer_request_fee.xml", UserPrivileges.NORMAL, RICH_DOMAIN_MAP);
}
@Test
void testFailure_superuserExtension_zeroPeriod_feeTransferExtension_v12() {
setupDomain("example", "tld");
eppRequestSource = EppRequestSource.TOOL;
clock.advanceOneMilli();
assertThrows(
TransferPeriodZeroAndFeeTransferExtensionException.class,
() ->
runTest(
"domain_transfer_request_fee_and_superuser_extension.xml",
UserPrivileges.SUPERUSER,
new ImmutableMap.Builder<String, String>()
.putAll(FEE_12_MAP)
.put("PERIOD", "0")
.put("AUTOMATIC_TRANSFER_LENGTH", "5")
.build()));
}
@Test
void testSuccess_premiumNotBlockedInSuperuserMode_v12() throws Exception {
setupDomain("rich", "example");
clock.advanceOneMilli();
// Modify the Registrar to block premium names.
persistResource(loadRegistrar("NewRegistrar").asBuilder().setBlockPremiumNames(true).build());
// We don't verify the results; just check that the flow doesn't fail.
runTest("domain_transfer_request_fee.xml", UserPrivileges.SUPERUSER, RICH_DOMAIN_MAP);
}
private void runWrongCurrencyTest(Map<String, String> substitutions) {
Map<String, String> fullSubstitutions = Maps.newHashMap();
fullSubstitutions.putAll(substitutions);
fullSubstitutions.put("CURRENCY", "EUR");
setupDomain("example", "tld");
EppException thrown =
assertThrows(
CurrencyUnitMismatchException.class,
() -> doFailingTest("domain_transfer_request_fee.xml", fullSubstitutions));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
/**
* Runs a successful test. The extraExpectedBillingEvents parameter consists of cancellation
* billing event builders that have had all of their attributes set except for the parent history
* entry, which is filled in during the execution of this method.
*/
private void doSuccessfulTest(
String commandFilename,
String expectedXmlFilename,
DateTime expectedExpirationTime,
Map<String, String> substitutions,
Optional<Money> transferCost,
BillingCancellation.Builder... extraExpectedBillingEvents)
throws Exception {
setEppInput(commandFilename, substitutions);
ImmutableSet<GracePeriod> originalGracePeriods = domain.getGracePeriods();
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// For all of the other transfer flow tests, 'now' corresponds to day 3 of the transfer, but
// for the request test we want that same 'now' to be the initial request time, so we shift
// the transfer timeline 3 days later by adjusting the implicit transfer time here.
Tld registry = Tld.get(domain.getTld());
DateTime implicitTransferTime = clock.nowUtc().plus(registry.getAutomaticTransferLength());
// Setup done; run the test.
assertMutatingFlow(true);
runFlowAssertResponse(loadFile(expectedXmlFilename, substitutions));
// Transfer should have been requested.
domain = reloadResourceByForeignKey();
// Verify that HistoryEntry was created.
assertAboutDomains()
.that(domain)
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_TRANSFER_REQUEST);
assertLastHistoryContainsResource(domain);
final HistoryEntry historyEntryTransferRequest =
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST);
assertAboutHistoryEntries()
.that(historyEntryTransferRequest)
.hasPeriodYears(1)
.and()
.hasOtherRegistrarId("TheRegistrar");
// Verify correct fields were set.
assertTransferRequested(
domain, implicitTransferTime, Period.create(1, Unit.YEARS), expectedExpirationTime);
subordinateHost = reloadResourceAndCloneAtTime(subordinateHost, clock.nowUtc());
assertAboutHosts().that(subordinateHost).hasNoHistoryEntries();
assertHistoryEntriesContainBillingEventsAndGracePeriods(
expectedExpirationTime,
implicitTransferTime,
transferCost,
originalGracePeriods,
/* expectTransferBillingEvent= */ true,
extraExpectedBillingEvents);
assertPollMessagesEmitted(expectedExpirationTime, implicitTransferTime);
assertAboutDomainAfterAutomaticTransfer(
expectedExpirationTime, implicitTransferTime, Period.create(1, Unit.YEARS));
cloudTasksHelper.assertTasksEnqueued(
QUEUE_ASYNC_ACTIONS,
new TaskMatcher()
.path(ResaveEntityAction.PATH)
.method(HttpMethod.POST)
.service("backend")
.header("content-type", "application/x-www-form-urlencoded")
.param(PARAM_RESOURCE_KEY, domain.createVKey().stringify())
.param(PARAM_REQUESTED_TIME, clock.nowUtc().toString())
.scheduleTime(clock.nowUtc().plus(registry.getAutomaticTransferLength())));
}
private void doSuccessfulTest(
String commandFilename, String expectedXmlFilename, Map<String, String> substitutions)
throws Exception {
clock.advanceOneMilli();
doSuccessfulTest(
commandFilename,
expectedXmlFilename,
domain.getRegistrationExpirationTime().plusYears(1),
substitutions,
Optional.empty());
}
private void doSuccessfulTest(String commandFilename, String expectedXmlFilename)
throws Exception {
clock.advanceOneMilli();
doSuccessfulTest(
commandFilename, expectedXmlFilename, domain.getRegistrationExpirationTime().plusYears(1));
}
private void doSuccessfulTest(
String commandFilename,
String expectedXmlFilename,
DateTime expectedExpirationTime,
BillingCancellation.Builder... extraExpectedBillingEvents)
throws Exception {
doSuccessfulTest(
commandFilename,
expectedXmlFilename,
expectedExpirationTime,
ImmutableMap.of(),
Optional.empty(),
extraExpectedBillingEvents);
}
private void runWrongFeeAmountTest(Map<String, String> substitutions) {
persistResource(
Tld.get("tld")
.asBuilder()
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 20)))
.build());
EppException thrown =
assertThrows(
FeesMismatchException.class,
() -> doFailingTest("domain_transfer_request_fee.xml", substitutions));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
private void runTest(
String commandFilename, UserPrivileges userPrivileges, Map<String, String> substitutions)
throws Exception {
setEppInput(commandFilename, substitutions);
// Replace the ROID in the xml file with the one generated in our test.
eppLoader.replaceAll("JD1234-REP", contact.getRepoId());
// Setup done; run the test.
assertMutatingFlow(true);
runFlow(CommitMode.LIVE, userPrivileges);
}
private void runTest(String commandFilename, UserPrivileges userPrivileges) throws Exception {
runTest(commandFilename, userPrivileges, ImmutableMap.of());
}
private void doFailingTest(String commandFilename, Map<String, String> substitutions)
throws Exception {
runTest(commandFilename, UserPrivileges.NORMAL, substitutions);
}
private void doFailingTest(String commandFilename) throws Exception {
runTest(commandFilename, UserPrivileges.NORMAL, ImmutableMap.of());
}
/** Adds a domain with no pending transfer on it. */
void setupDomain(String label, String tld) {
createTld(tld);
contact = persistActiveContact("jd1234");
domain =
persistDomainWithDependentResources(
label,
tld,
contact,
clock.nowUtc(),
DateTime.parse("1999-04-03T22:00:00.0Z"),
REGISTRATION_EXPIRATION_TIME);
subordinateHost =
persistResource(
new Host.Builder()
.setRepoId("2-".concat(Ascii.toUpperCase(tld)))
.setHostName("ns1." + label + "." + tld)
.setPersistedCurrentSponsorRegistrarId("TheRegistrar")
.setCreationRegistrarId("TheRegistrar")
.setCreationTimeForTest(DateTime.parse("1999-04-03T22:00:00.0Z"))
.setSuperordinateDomain(domain.createVKey())
.build());
domain =
persistResource(
domain.asBuilder().addSubordinateHost(subordinateHost.getHostName()).build());
historyEntryDomainCreate =
getOnlyHistoryEntryOfType(domain, DOMAIN_CREATE, DomainHistory.class);
}
private void assertPollMessagesEmitted(
DateTime expectedExpirationTime, DateTime implicitTransferTime) {
// Assert that there exists a poll message to notify the losing registrar that a transfer was
// requested. If the implicit transfer time is now (i.e. the automatic transfer length is zero)
// then also expect a server approved poll message.
assertThat(getPollMessages("TheRegistrar", clock.nowUtc()))
.hasSize(implicitTransferTime.equals(clock.nowUtc()) ? 2 : 1);
// Two poll messages on the gaining registrar's side at the expected expiration time: a
// (OneTime) transfer approved message, and an Autorenew poll message.
assertThat(getPollMessages("NewRegistrar", expectedExpirationTime)).hasSize(2);
PollMessage transferApprovedPollMessage =
getOnlyPollMessage("NewRegistrar", implicitTransferTime, PollMessage.OneTime.class);
PollMessage autorenewPollMessage =
getOnlyPollMessage("NewRegistrar", expectedExpirationTime, PollMessage.Autorenew.class);
assertThat(transferApprovedPollMessage.getEventTime()).isEqualTo(implicitTransferTime);
assertThat(autorenewPollMessage.getEventTime()).isEqualTo(expectedExpirationTime);
assertThat(
transferApprovedPollMessage.getResponseData().stream()
.filter(TransferResponse.class::isInstance)
.map(TransferResponse.class::cast)
.collect(onlyElement())
.getTransferStatus())
.isEqualTo(TransferStatus.SERVER_APPROVED);
PendingActionNotificationResponse panData =
transferApprovedPollMessage.getResponseData().stream()
.filter(PendingActionNotificationResponse.class::isInstance)
.map(PendingActionNotificationResponse.class::cast)
.collect(onlyElement());
assertThat(panData.getTrid().getClientTransactionId()).hasValue("ABC-12345");
assertThat(panData.getActionResult()).isTrue();
// Two poll messages on the losing registrar's side at the implicit transfer time: a
// transfer pending message, and a transfer approved message (both OneTime messages).
assertThat(getPollMessages("TheRegistrar", implicitTransferTime)).hasSize(2);
PollMessage losingTransferPendingPollMessage =
getPollMessages("TheRegistrar", clock.nowUtc()).stream()
.filter(pollMessage -> TransferStatus.PENDING.getMessage().equals(pollMessage.getMsg()))
.collect(onlyElement());
PollMessage losingTransferApprovedPollMessage =
getPollMessages("TheRegistrar", implicitTransferTime).stream()
.filter(Predicates.not(Predicates.equalTo(losingTransferPendingPollMessage)))
.collect(onlyElement());
assertThat(losingTransferPendingPollMessage.getEventTime()).isEqualTo(clock.nowUtc());
assertThat(losingTransferApprovedPollMessage.getEventTime()).isEqualTo(implicitTransferTime);
assertThat(
losingTransferPendingPollMessage.getResponseData().stream()
.filter(TransferResponse.class::isInstance)
.map(TransferResponse.class::cast)
.collect(onlyElement())
.getTransferStatus())
.isEqualTo(TransferStatus.PENDING);
assertThat(
losingTransferApprovedPollMessage.getResponseData().stream()
.filter(TransferResponse.class::isInstance)
.map(TransferResponse.class::cast)
.collect(onlyElement())
.getTransferStatus())
.isEqualTo(TransferStatus.SERVER_APPROVED);
// Assert that the poll messages show up in the TransferData server approve entities.
assertPollMessagesEqual(
loadByKey(domain.getTransferData().getServerApproveAutorenewPollMessage()),
autorenewPollMessage);
// Assert that the full set of server-approve poll messages is exactly the server approve
// OneTime messages to gaining and losing registrars plus the gaining client autorenew.
assertPollMessagesEqual(
Iterables.filter(
loadByKeys(domain.getTransferData().getServerApproveEntities()), PollMessage.class),
ImmutableList.of(
transferApprovedPollMessage, losingTransferApprovedPollMessage, autorenewPollMessage));
}
private void assertHistoryEntriesContainBillingEventsAndGracePeriods(
DateTime expectedExpirationTime,
DateTime implicitTransferTime,
Optional<Money> transferCost,
ImmutableSet<GracePeriod> originalGracePeriods,
boolean expectTransferBillingEvent,
BillingCancellation.Builder... extraExpectedBillingEvents) {
Tld registry = Tld.get(domain.getTld());
final DomainHistory historyEntryTransferRequest =
getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST, DomainHistory.class);
// Construct the billing events we expect to exist, starting with the (optional) billing
// event for the transfer itself.
Optional<BillingEvent> optionalTransferBillingEvent;
if (expectTransferBillingEvent) {
// For normal transfers, a BillingEvent should be created AUTOMATIC_TRANSFER_DAYS in the
// future, for the case when the transfer is implicitly acked.
optionalTransferBillingEvent =
Optional.of(
new BillingEvent.Builder()
.setReason(Reason.TRANSFER)
.setTargetId(domain.getDomainName())
.setEventTime(implicitTransferTime)
.setBillingTime(
implicitTransferTime.plus(registry.getTransferGracePeriodLength()))
.setRegistrarId("NewRegistrar")
.setCost(transferCost.orElse(Money.of(USD, 11)))
.setPeriodYears(1)
.setDomainHistory(historyEntryTransferRequest)
.build());
} else {
// Superuser transfers with no bundled renewal have no transfer billing event.
optionalTransferBillingEvent = Optional.empty();
}
// Construct the autorenew events for the losing/existing client and the gaining one. Note that
// all of the other transfer flow tests happen on day 3 of the transfer, but the initial
// request by definition takes place on day 1, so we need to edit the times in the
// autorenew events from the base test case.
BillingRecurrence losingClientAutorenew =
getLosingClientAutorenewEvent()
.asBuilder()
.setRecurrenceEndTime(implicitTransferTime)
.build();
BillingRecurrence gainingClientAutorenew =
getGainingClientAutorenewEvent()
.asBuilder()
.setEventTime(expectedExpirationTime)
.setRecurrenceLastExpansion(expectedExpirationTime.minusYears(1))
.build();
// Construct extra billing events expected by the specific test.
ImmutableSet<BillingBase> extraBillingBases =
Stream.of(extraExpectedBillingEvents)
.map(builder -> builder.setDomainHistory(historyEntryTransferRequest).build())
.collect(toImmutableSet());
// Assert that the billing events we constructed above actually exist in the database.
ImmutableSet<BillingBase> expectedBillingBases =
Streams.concat(
Stream.of(losingClientAutorenew, gainingClientAutorenew),
optionalTransferBillingEvent.stream())
.collect(toImmutableSet());
assertBillingEvents(Sets.union(expectedBillingBases, extraBillingBases));
// Assert that the domain's TransferData server-approve billing events match the above.
if (expectTransferBillingEvent) {
assertBillingEventsEqual(
loadByKey(domain.getTransferData().getServerApproveBillingEvent()),
optionalTransferBillingEvent.get());
} else {
assertThat(domain.getTransferData().getServerApproveBillingEvent()).isNull();
}
assertBillingEventsEqual(
loadByKey(domain.getTransferData().getServerApproveAutorenewEvent()),
gainingClientAutorenew);
// Assert that the full set of server-approve billing events is exactly the extra ones plus
// the transfer billing event (if present) and the gaining client autorenew.
ImmutableSet<BillingBase> expectedServeApproveBillingBases =
Streams.concat(Stream.of(gainingClientAutorenew), optionalTransferBillingEvent.stream())
.collect(toImmutableSet());
assertBillingEventsEqual(
Iterables.filter(
loadByKeys(domain.getTransferData().getServerApproveEntities()), BillingBase.class),
Sets.union(expectedServeApproveBillingBases, extraBillingBases));
// The domain's autorenew billing event should still point to the losing client's event.
BillingRecurrence domainAutorenewEvent = loadByKey(domain.getAutorenewBillingEvent());
assertThat(domainAutorenewEvent.getRegistrarId()).isEqualTo("TheRegistrar");
assertThat(domainAutorenewEvent.getRecurrenceEndTime()).isEqualTo(implicitTransferTime);
// The original grace periods should remain untouched.
assertThat(domain.getGracePeriods()).containsExactlyElementsIn(originalGracePeriods);
// If we fast forward AUTOMATIC_TRANSFER_DAYS, the transfer should have cleared out all other
// grace periods, but expect a transfer grace period (if there was a transfer billing event).
Domain domainAfterAutomaticTransfer = domain.cloneProjectedAtTime(implicitTransferTime);
if (expectTransferBillingEvent) {
assertGracePeriods(
domainAfterAutomaticTransfer.getGracePeriods(),
ImmutableMap.of(
GracePeriod.create(
GracePeriodStatus.TRANSFER,
domain.getRepoId(),
implicitTransferTime.plus(registry.getTransferGracePeriodLength()),
"NewRegistrar",
null),
optionalTransferBillingEvent.get()));
} else {
assertGracePeriods(domainAfterAutomaticTransfer.getGracePeriods(), ImmutableMap.of());
}
}
private BillingRecurrence getGainingClientAutorenewEvent() {
return new BillingRecurrence.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(domain.getDomainName())
.setRegistrarId("NewRegistrar")
.setEventTime(EXTENDED_REGISTRATION_EXPIRATION_TIME)
.setRecurrenceEndTime(END_OF_TIME)
.setDomainHistory(
getOnlyHistoryEntryOfType(
domain, HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST, DomainHistory.class))
.build();
}
/** Get the autorenew event that the losing client will have after a SERVER_APPROVED transfer. */
private BillingRecurrence getLosingClientAutorenewEvent() {
return new BillingRecurrence.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(domain.getDomainName())
.setRegistrarId("TheRegistrar")
.setEventTime(REGISTRATION_EXPIRATION_TIME)
.setRecurrenceEndTime(TRANSFER_EXPIRATION_TIME)
.setDomainHistory(historyEntryDomainCreate)
.build();
}
private void assertAboutDomainAfterAutomaticTransfer(
DateTime expectedExpirationTime, DateTime implicitTransferTime, Period expectedPeriod)
throws Exception {
Tld registry = Tld.get(domain.getTld());
Domain domainAfterAutomaticTransfer = domain.cloneProjectedAtTime(implicitTransferTime);
assertTransferApproved(domainAfterAutomaticTransfer, implicitTransferTime, expectedPeriod);
assertAboutDomains()
.that(domainAfterAutomaticTransfer)
.hasRegistrationExpirationTime(expectedExpirationTime)
.and()
.hasLastEppUpdateTime(implicitTransferTime)
.and()
.hasLastEppUpdateRegistrarId("NewRegistrar");
assertThat(loadByKey(domainAfterAutomaticTransfer.getAutorenewBillingEvent()).getEventTime())
.isEqualTo(expectedExpirationTime);
// And after the expected grace time, the grace period should be gone.
Domain afterGracePeriod =
domain.cloneProjectedAtTime(
clock
.nowUtc()
.plus(registry.getAutomaticTransferLength())
.plus(registry.getTransferGracePeriodLength()));
assertThat(afterGracePeriod.getGracePeriods()).isEmpty();
}
private void assertTransferApproved(
Domain domain, DateTime automaticTransferTime, Period expectedPeriod) throws Exception {
assertAboutDomains()
.that(domain)
.hasCurrentSponsorRegistrarId("NewRegistrar")
.and()
.hasLastTransferTime(automaticTransferTime)
.and()
.doesNotHaveStatusValue(StatusValue.PENDING_TRANSFER);
Trid expectedTrid =
Trid.create(
getClientTrid(),
domain.getTransferData().getTransferRequestTrid().getServerTransactionId());
assertThat(domain.getTransferData())
.isEqualTo(
new DomainTransferData.Builder()
.setGainingRegistrarId("NewRegistrar")
.setLosingRegistrarId("TheRegistrar")
.setTransferRequestTrid(expectedTrid)
.setTransferRequestTime(clock.nowUtc())
.setTransferPeriod(expectedPeriod)
.setTransferStatus(TransferStatus.SERVER_APPROVED)
.setPendingTransferExpirationTime(automaticTransferTime)
.setTransferredRegistrationExpirationTime(domain.getRegistrationExpirationTime())
// Server-approve entity fields should all be nulled out.
.build());
}
private void assertTransferRequested(
Domain domain,
DateTime automaticTransferTime,
Period expectedPeriod,
DateTime expectedExpirationTime)
throws Exception {
assertAboutDomains()
.that(domain)
.hasCurrentSponsorRegistrarId("TheRegistrar")
.and()
.hasStatusValue(StatusValue.PENDING_TRANSFER)
.and()
.hasLastEppUpdateTime(clock.nowUtc())
.and()
.hasLastEppUpdateRegistrarId("NewRegistrar");
Trid expectedTrid =
Trid.create(
getClientTrid(),
domain.getTransferData().getTransferRequestTrid().getServerTransactionId());
assertThat(domain.getTransferData())
.isEqualTo(
// Compare against only the following fields by rebuilding the existing TransferData.
// Equivalent to assertThat(transferData.getGainingClientId()).isEqualTo("NewReg")
// and similar individual assertions, but produces a nicer error message this way.
domain
.getTransferData()
.asBuilder()
.setGainingRegistrarId("NewRegistrar")
.setLosingRegistrarId("TheRegistrar")
.setTransferRequestTrid(expectedTrid)
.setTransferRequestTime(clock.nowUtc())
.setTransferPeriod(expectedPeriod)
.setTransferStatus(TransferStatus.PENDING)
.setPendingTransferExpirationTime(automaticTransferTime)
.setTransferredRegistrationExpirationTime(expectedExpirationTime)
// Don't compare the server-approve entity fields; they're hard to reconstruct
// and logic later will check them.
.build());
}
}

View File

@@ -46,6 +46,7 @@ import static google.registry.testing.DomainSubject.assertAboutDomains;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static google.registry.testing.HostSubject.assertAboutHosts;
import static google.registry.testing.TestDataHelper.updateSubstitutions;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.joda.money.CurrencyUnit.JPY;
import static org.joda.money.CurrencyUnit.USD;
@@ -146,32 +147,16 @@ class DomainTransferRequestFlowTest
.put("AMOUNT", "11.00")
.put("CURRENCY", "USD")
.build();
private static final ImmutableMap<String, String> FEE_06_MAP =
new ImmutableMap.Builder<String, String>()
.putAll(BASE_FEE_MAP)
.put("FEE_VERSION", "0.6")
.put("FEE_NS", "fee")
.build();
private static final ImmutableMap<String, String> FEE_11_MAP =
new ImmutableMap.Builder<String, String>()
.putAll(BASE_FEE_MAP)
.put("FEE_VERSION", "0.11")
.put("FEE_NS", "fee11")
.build();
private static final ImmutableMap<String, String> FEE_12_MAP =
new ImmutableMap.Builder<String, String>()
.putAll(BASE_FEE_MAP)
.put("FEE_VERSION", "0.12")
.put("FEE_NS", "fee12")
.build();
private static final ImmutableMap<String, String> FEE_STD_1_0_MAP =
updateSubstitutions(BASE_FEE_MAP, "FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00");
private static final ImmutableMap<String, String> RICH_DOMAIN_MAP =
ImmutableMap.<String, String>builder()
.put("DOMAIN", "rich.example")
.put("YEARS", "1")
.put("AMOUNT", "100.00")
.put("CURRENCY", "USD")
.put("FEE_VERSION", "0.12")
.put("FEE_NS", "fee12")
.put("FEE_VERSION", "epp:fee-1.0")
.put("FEE_NS", "fee")
.build();
@BeforeEach
@@ -665,140 +650,50 @@ class DomainTransferRequestFlowTest
}
@Test
void testSuccess_fee_v06() throws Exception {
void testSuccess_fee_std_v1() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_06_MAP);
"domain_transfer_request_fee.xml",
"domain_transfer_request_response_fee.xml",
FEE_STD_1_0_MAP);
}
@Test
void testSuccess_fee_v11() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_11_MAP);
}
@Test
void testSuccess_fee_v12() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee.xml", "domain_transfer_request_response_fee.xml", FEE_12_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v06() throws Exception {
void testSuccess_fee_withDefaultAttributes_std_v1() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee_defaults.xml",
"domain_transfer_request_response_fee.xml",
FEE_06_MAP);
FEE_STD_1_0_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v11() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee_defaults.xml",
"domain_transfer_request_response_fee.xml",
FEE_11_MAP);
}
@Test
void testSuccess_fee_withDefaultAttributes_v12() throws Exception {
setupDomain("example", "tld");
doSuccessfulTest(
"domain_transfer_request_fee_defaults.xml",
"domain_transfer_request_response_fee.xml",
FEE_12_MAP);
}
@Test
void testFailure_refundableFee_v06() {
void testFailure_refundableFee_std_v1() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_06_MAP));
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_STD_1_0_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v11() {
void testFailure_gracePeriodFee_std_v1() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_11_MAP));
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_STD_1_0_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_refundableFee_v12() {
void testFailure_appliedFee_std_v1() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_refundable.xml", FEE_12_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v06() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_06_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v11() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_11_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_gracePeriodFee_v12() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_grace_period.xml", FEE_12_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v06() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_06_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v11() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_11_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_appliedFee_v12() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
UnsupportedFeeAttributeException.class,
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_12_MAP));
() -> doFailingTest("domain_transfer_request_fee_applied.xml", FEE_STD_1_0_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@@ -991,7 +886,7 @@ class DomainTransferRequestFlowTest
}
@Test
void testFailure_superuserExtension_zeroPeriod_feeTransferExtension() {
void testFailure_superuserExtension_zeroPeriod_feeTransferExtension_std_v1() {
setupDomain("example", "tld");
eppRequestSource = EppRequestSource.TOOL;
clock.advanceOneMilli();
@@ -1001,7 +896,11 @@ class DomainTransferRequestFlowTest
runTest(
"domain_transfer_request_fee_and_superuser_extension.xml",
UserPrivileges.SUPERUSER,
ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "5")));
new ImmutableMap.Builder<String, String>()
.putAll(FEE_STD_1_0_MAP)
.put("PERIOD", "0")
.put("AUTOMATIC_TRANSFER_LENGTH", "5")
.build()));
}
@Test
@@ -1063,7 +962,7 @@ class DomainTransferRequestFlowTest
}
@Test
void testSuccess_customLogicFee() throws Exception {
void testSuccess_customLogicFee_std_v1() throws Exception {
setupDomain("expensive-domain", "foo");
clock.advanceOneMilli();
doSuccessfulTest(
@@ -1075,7 +974,7 @@ class DomainTransferRequestFlowTest
.put("YEARS", "1")
.put("AMOUNT", "111.00")
.put("EXDATE", "2002-09-08T22:00:00.0Z")
.put("FEE_VERSION", "0.6")
.put("FEE_VERSION", "epp:fee-1.0")
.put("FEE_NS", "fee")
.build(),
Optional.of(Money.of(USD, 111)));
@@ -1227,7 +1126,7 @@ class DomainTransferRequestFlowTest
}
@Test
void testSuccess_premiumNotBlocked() throws Exception {
void testSuccess_premiumNotBlocked_v06() throws Exception {
setupDomain("rich", "example");
clock.advanceOneMilli();
// We don't verify the results; just check that the flow doesn't fail.
@@ -1235,7 +1134,7 @@ class DomainTransferRequestFlowTest
}
@Test
void testSuccess_premiumNotBlockedInSuperuserMode() throws Exception {
void testSuccess_premiumNotBlockedInSuperuserMode_std_v1() throws Exception {
setupDomain("rich", "example");
clock.advanceOneMilli();
// Modify the Registrar to block premium names.
@@ -1525,24 +1424,14 @@ class DomainTransferRequestFlowTest
}
@Test
void testFailure_wrongCurrency_v06() {
runWrongCurrencyTest(FEE_06_MAP);
}
@Test
void testFailure_wrongCurrency_v11() {
runWrongCurrencyTest(FEE_11_MAP);
}
@Test
void testFailure_wrongCurrency_v12() {
runWrongCurrencyTest(FEE_12_MAP);
void testFailure_wrongCurrency_std_v1() {
runWrongCurrencyTest(FEE_STD_1_0_MAP);
}
@Test
void testFailure_unknownCurrency() {
Map<String, String> substitutions = Maps.newHashMap();
substitutions.putAll(FEE_06_MAP);
substitutions.putAll(FEE_STD_1_0_MAP);
substitutions.put("CURRENCY", "BAD");
setupDomain("example", "tld");
setEppInput("domain_transfer_request_fee.xml", substitutions);
@@ -1551,32 +1440,12 @@ class DomainTransferRequestFlowTest
}
@Test
void testFailure_feeGivenInWrongScale_v06() {
void testFailure_feeGivenInWrongScale_std_v1() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
CurrencyValueScaleException.class,
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_06_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v11() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
CurrencyValueScaleException.class,
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_11_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_feeGivenInWrongScale_v12() {
setupDomain("example", "tld");
EppException thrown =
assertThrows(
CurrencyValueScaleException.class,
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_12_MAP));
() -> doFailingTest("domain_transfer_request_fee_bad_scale.xml", FEE_STD_1_0_MAP));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@@ -1594,21 +1463,9 @@ class DomainTransferRequestFlowTest
}
@Test
void testFailure_wrongFeeAmount_v06() {
void testFailure_wrongFeeAmount_std_v1() {
setupDomain("example", "tld");
runWrongFeeAmountTest(FEE_06_MAP);
}
@Test
void testFailure_wrongFeeAmount_v11() {
setupDomain("example", "tld");
runWrongFeeAmountTest(FEE_11_MAP);
}
@Test
void testFailure_wrongFeeAmount_v12() {
setupDomain("example", "tld");
runWrongFeeAmountTest(FEE_12_MAP);
runWrongFeeAmountTest(FEE_STD_1_0_MAP);
}
@Test

View File

@@ -63,8 +63,6 @@ import google.registry.config.RegistryConfig;
import google.registry.flows.EppException;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppRequestSource;
import google.registry.flows.FlowTestCase.CommitMode;
import google.registry.flows.FlowTestCase.UserPrivileges;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.flows.ResourceFlowUtils.AddExistingValueException;
@@ -74,7 +72,6 @@ import google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException;
import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
import google.registry.flows.ResourceFlowUtils.StatusNotClientSettableException;
import google.registry.flows.domain.DomainFlowUtils.EmptySecDnsUpdateException;
import google.registry.flows.domain.DomainFlowUtils.FeesMismatchException;
import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeOperationException;
import google.registry.flows.domain.DomainFlowUtils.InvalidDsRecordException;
import google.registry.flows.domain.DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException;
@@ -361,10 +358,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
}
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setNameservers(nameservers.build())
.build());
reloadResourceByForeignKey().asBuilder().setNameservers(nameservers.build()).build());
clock.advanceOneMilli();
assertMutatingFlow(true);
runFlowAssertResponse(loadFile("generic_success_response.xml"));
@@ -454,6 +448,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
@Test
void testSuccess_removeClientUpdateProhibited() throws Exception {
setEppInput("domain_update_remove_client_update_prohibited.xml");
persistReferencedEntities();
persistResource(
persistDomain()
@@ -549,21 +544,26 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testSuccess_secDnsAddSameDoesNothing() throws Exception {
doSecDnsSuccessfulTest(
"domain_update_dsdata_add.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
ImmutableMap.of(
"KEY_TAG",
"1",
"ALG",
"2",
"DIGEST_TYPE",
"2",
"DIGEST",
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
false);
void testFailure_secDnsAddSame_throwsException() throws Exception {
EppException thrown =
assertThrows(
AddExistingValueException.class,
() ->
doSecDnsSuccessfulTest(
"domain_update_dsdata_add.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
ImmutableMap.of(
"KEY_TAG",
"1",
"ALG",
"2",
"DIGEST_TYPE",
"2",
"DIGEST",
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
false));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
@@ -681,7 +681,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
2,
2,
base16()
.decode("9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"))),
.decode("0F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"))),
ImmutableMap.of(
"KEY_TAG",
"1",
@@ -690,8 +690,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
"DIGEST_TYPE",
"2",
"DIGEST",
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
false);
"0F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"),
true);
}
@Test
@@ -799,29 +799,43 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testSuccess_secDnsAddRemoveSame() throws Exception {
// Adding and removing the same dsData is a no-op because removes are processed first.
doSecDnsSuccessfulTest(
"domain_update_dsdata_add_rem_same.xml",
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
false);
void testFailure_secDnsAddRemoveSame_throwsException() throws Exception {
EppException thrown =
assertThrows(
AddRemoveSameValueException.class,
() ->
doSecDnsSuccessfulTest(
"domain_update_dsdata_add_rem_same.xml",
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345,
3,
1,
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345,
3,
1,
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
false));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_secDnsRemoveAlreadyNotThere() throws Exception {
// Removing a dsData that isn't there is a no-op.
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
false);
void testFailure_secDnsRemoveAlreadyNotThere_throwsException() throws Exception {
EppException thrown =
assertThrows(
RemoveNonexistentValueException.class,
() ->
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(SOME_DSDATA),
false));
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
void doServerStatusBillingTest(String xmlFilename, boolean isBillable) throws Exception {
@@ -856,14 +870,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doServerStatusBillingTest("domain_update_add_server_status.xml", true);
}
@Test
void testSuccess_noBillingOnPreExistingServerStatus() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
Domain addStatusDomain = persistActiveDomain(getUniqueIdFromCommand());
persistResource(addStatusDomain.asBuilder().addStatusValue(SERVER_RENEW_PROHIBITED).build());
doServerStatusBillingTest("domain_update_add_server_status.xml", false);
}
@Test
void testSuccess_removeServerStatusBillingEvent() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
@@ -1390,7 +1396,20 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testFailure_removeNonexistentValue() throws Exception {
void testFailure_addExistingStatusValue() throws Exception {
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setStatusValues(ImmutableSet.of(CLIENT_RENEW_PROHIBITED))
.build());
setEppInput("domain_update_add_non_server_status.xml");
assertAboutEppExceptions()
.that(assertThrows(AddExistingValueException.class, this::runFlow))
.marshalsToXml();
}
@Test
void testFailure_removeNonexistentNameserver() throws Exception {
persistActiveDomain(getUniqueIdFromCommand());
persistActiveHost("ns1.example.foo");
setEppInput("domain_update_remove_nameserver.xml");
@@ -1399,6 +1418,15 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
.marshalsToXml();
}
@Test
void testFailure_removeNonexistentStatusValue() throws Exception {
persistActiveDomain(getUniqueIdFromCommand());
setEppInput("domain_update_remove_client_update_prohibited.xml");
assertAboutEppExceptions()
.that(assertThrows(RemoveNonexistentValueException.class, this::runFlow))
.marshalsToXml();
}
@Test
void testFailure_minimumDataset_addingNewRegistrantFails() throws Exception {
persistReferencedEntities();
@@ -1502,15 +1530,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
assertAboutDomains().that(reloadResourceByForeignKey()).hasExactlyStatusValues(CLIENT_HOLD);
}
@Test
void testFailure_freePremium_wrongFee() throws Exception {
setEppInput("domain_update_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
persistReferencedEntities();
persistDomain();
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
// This test should throw an exception, because the fee extension is required when the fee is not
// zero.
@Test

View File

@@ -0,0 +1,56 @@
// Copyright 2026 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.flows.domain;
import google.registry.flows.Flow;
import google.registry.flows.ResourceFlowTestCase;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.EppXmlTransformer;
import google.registry.model.eppcommon.ProtocolDefinition;
import google.registry.util.RegistryEnvironment;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
/**
* Abstract class for tests that require old versions of the fee extension (0.6, 0.11, 0.12).
*
* <p>These are enabled only in the production environment so in order to test them, we need to
* simulate the production environment for each test (and reset it to the test environment
* afterward).
*/
public abstract class ProductionSimulatingFeeExtensionsTest<F extends Flow>
extends ResourceFlowTestCase<F, Domain> {
private RegistryEnvironment previousEnvironment;
@BeforeEach
void beforeEach() {
previousEnvironment = RegistryEnvironment.get();
RegistryEnvironment.PRODUCTION.setup();
reloadServiceExtensionUris();
}
@AfterEach
void afterEach() {
previousEnvironment.setup();
reloadServiceExtensionUris();
}
void reloadServiceExtensionUris() {
ProtocolDefinition.reloadServiceExtensionUris();
sessionMetadata.setServiceExtensionUris(ProtocolDefinition.getVisibleServiceExtensionUris());
EppXmlTransformer.reloadTransformers();
}
}

View File

@@ -45,6 +45,7 @@ import google.registry.flows.host.HostFlowUtils.HostNameNotNormalizedException;
import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException;
import google.registry.flows.host.HostFlowUtils.HostNameTooLongException;
import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException;
import google.registry.flows.host.HostFlowUtils.InvalidHostNameException;
import google.registry.flows.host.HostFlowUtils.LoopbackIpNotValidForHostException;
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException;
import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException;
@@ -82,9 +83,14 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
}
private void doSuccessfulTest() throws Exception {
doSuccessfulTest("host_create_response.xml", ImmutableMap.of());
}
private void doSuccessfulTest(String responseFile, ImmutableMap<String, String> substitutions)
throws Exception {
clock.advanceOneMilli();
assertMutatingFlow(true);
runFlowAssertResponse(loadFile("host_create_response.xml"));
runFlowAssertResponse(loadFile(responseFile, substitutions));
Host host = reloadResourceByForeignKey();
// Check that the host was created and persisted with a history entry.
assertAboutHosts()
@@ -134,6 +140,28 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
assertHostDnsRequests("ns1.example.tld");
}
@Test
void testSuccess_tldWithHyphenOn3And4() throws Exception {
setEppHostCreateInput("ns1.example.zz--main-2262", null);
doSuccessfulTest(
"host_create_response_wildcard.xml",
ImmutableMap.of("HOSTNAME", "ns1.example.zz--main-2262"));
}
@Test
void testFailure_domainWithHyphenOn3And4() throws Exception {
setEppHostCreateInput("ns1.zz--main-2262.tld", null);
EppException thrown = assertThrows(InvalidHostNameException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_hostnameWithHyphenOn3And4() throws Exception {
setEppHostCreateInput("zz--ns1.domain.tld", null);
EppException thrown = assertThrows(InvalidHostNameException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_multipartTLDsAndInvalidHost() {
createTlds("bar.tld", "tld");

View File

@@ -24,12 +24,9 @@ import google.registry.testing.EppLoader;
/** Unit tests for {@code ResourceCommand}. */
public abstract class ResourceCommandTestCase extends EntityTestCase {
protected void doXmlRoundtripTest(String inputFilename, String... ignoredPaths)
throws Exception {
protected void doXmlRoundtripTest(String inputFilename) throws Exception {
EppLoader eppLoader = new EppLoader(this, inputFilename);
assertXmlEquals(
eppLoader.getEppXml(),
new String(marshalInput(eppLoader.getEpp(), STRICT), UTF_8),
ignoredPaths);
eppLoader.getEppXml(), new String(marshalInput(eppLoader.getEpp(), STRICT), UTF_8));
}
}

View File

@@ -113,11 +113,6 @@ class DomainCommandTest extends ResourceCommandTestCase {
doXmlRoundtripTest("domain_update.xml");
}
@Test
void testUpdate_fee() throws Exception {
doXmlRoundtripTest("domain_update_fee.xml");
}
@Test
void testUpdate_emptyCommand() throws Exception {
// This EPP command wouldn't be allowed for policy reasons, but should marshal/unmarshal fine.
@@ -153,11 +148,6 @@ class DomainCommandTest extends ResourceCommandTestCase {
doXmlRoundtripTest("domain_info_sunrise.xml");
}
@Test
void testInfo_feeExtension() throws Exception {
doXmlRoundtripTest("domain_info_fee.xml");
}
@Test
void testCheck() throws Exception {
doXmlRoundtripTest("domain_check.xml");

View File

@@ -68,10 +68,7 @@ class EppInputTest {
assertThat(loginCommand.options.version).isEqualTo("1.0");
assertThat(loginCommand.options.language).isEqualTo("en");
assertThat(loginCommand.services.objectServices)
.containsExactly(
"urn:ietf:params:xml:ns:host-1.0",
"urn:ietf:params:xml:ns:domain-1.0",
"urn:ietf:params:xml:ns:contact-1.0");
.containsExactly("urn:ietf:params:xml:ns:host-1.0", "urn:ietf:params:xml:ns:domain-1.0");
assertThat(loginCommand.services.serviceExtensions)
.containsExactly("urn:ietf:params:xml:ns:launch-1.0", "urn:ietf:params:xml:ns:rgp-1.0");
}

View File

@@ -0,0 +1,92 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="true">example1.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="false">example2.example</domain:name>
<domain:reason>Alloc token invalid for domain</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">reserved.tld</domain:name>
<domain:reason>Alloc token invalid for domain</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">rich.example</domain:name>
<domain:reason>Alloc token invalid for domain</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">0.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example2.example</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>reserved.tld</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>rich.example</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:class>premium</fee:class>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example2.example</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>reserved.tld</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>rich.example</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -0,0 +1,91 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="true">example1.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="true">example2.example</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="false">reserved.tld</domain:name>
<domain:reason>Reserved</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="true">rich.example</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:objID>example2.example</fee:objID>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>reserved.tld</fee:objID>
<fee:class>reserved</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>rich.example</fee:objID>
<fee:class>premium</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">100.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example2.example</fee:objID>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>reserved.tld</fee:objID>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>rich.example</fee:objID>
<fee:class>premium</fee:class>
<fee:command name="renew">
<fee:period unit="y">1</fee:period>
<fee:fee description="renew">100.00</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -0,0 +1,63 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="false">example1.tld</domain:name>
<domain:reason>Alloc token invalid for domain</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example2.example</domain:name>
<domain:reason>Alloc token invalid for domain</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">reserved.tld</domain:name>
<domain:reason>Alloc token invalid for domain</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="true">specificuse.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example2.example</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>reserved.tld</fee:objID>
<fee:class>token-not-supported</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>specificuse.tld</fee:objID>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -0,0 +1,26 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
<domain:name>example2.example</domain:name>
<domain:name>reserved.tld</domain:name>
<domain:name>specificuse.tld</domain:name>
</domain:check>
</check>
<extension>
<allocationToken:allocationToken
xmlns:allocationToken=
"urn:ietf:params:xml:ns:allocationToken-1.0">
abc123
</allocationToken:allocationToken>
<fee:check
xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:command name="create" />
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,27 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
<domain:name>example2.example</domain:name>
<domain:name>reserved.tld</domain:name>
<domain:name>rich.example</domain:name>
</domain:check>
</check>
<extension>
<allocationToken:allocationToken
xmlns:allocationToken=
"urn:ietf:params:xml:ns:allocationToken-1.0">
abc123
</allocationToken:allocationToken>
<fee:check
xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:command name="create" />
<fee:command name="renew" />
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -17,4 +17,4 @@
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
</epp>

View File

@@ -0,0 +1,47 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="true">%DOMAIN%</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:objID>%DOMAIN%</fee:objID>
%FEE_CLASS%
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">%COST_1YR%</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>%DOMAIN%</fee:objID>
%FEE_CLASS%
<fee:command name="create">
<fee:period unit="y">2</fee:period>
<fee:fee description="create">%COST_2YR%</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>%DOMAIN%</fee:objID>
%FEE_CLASS%
<fee:command name="create">
<fee:period unit="y">5</fee:period>
<fee:fee description="create">%COST_5YR%</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -0,0 +1,31 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
</domain:check>
</check>
<extension>
<allocationToken:allocationToken
xmlns:allocationToken=
"urn:ietf:params:xml:ns:allocationToken-1.0">
abc123
</allocationToken:allocationToken>
<fee:check
xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
<fee:command name="create">
<fee:period unit="y">2</fee:period>
</fee:command>
<fee:command name="create">
<fee:period unit="y">5</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,23 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
<domain:name>example2.tld</domain:name>
<domain:name>example3.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>BAD</fee:currency>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,18 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
</domain:check>
</check>
<extension>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:command name="create">
<fee:period unit="m">1</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,15 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
</domain:check>
</check>
<extension>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:command name="create" phase="claims" />
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,15 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
</domain:check>
</check>
<extension>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:command name="create" subphase="landrush" />
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,23 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
<domain:name>example2.tld</domain:name>
<domain:name>example3.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>EUR</fee:currency>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,30 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="1">example1.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">11.10</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -0,0 +1,21 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -6,7 +6,7 @@
</domain:check>
</check>
<extension>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:fee-0.12">
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:command name="Create">
<fee:period unit="y">1</fee:period>
</fee:command>

View File

@@ -36,4 +36,4 @@
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
</epp>

View File

@@ -42,4 +42,4 @@
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
</epp>

View File

@@ -0,0 +1,20 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:command name="restore">
<fee:period unit="y">2</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,20 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:command name="transfer">
<fee:period unit="y">2</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,378 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:cd>
<domain:name avail="true">example-00.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-01.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-02.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-03.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-04.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-05.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-06.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-07.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-08.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-09.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-10.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-11.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-12.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-13.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-14.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-15.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-16.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-17.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-18.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-19.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-20.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-21.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-22.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-23.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-24.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-25.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-26.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-27.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-28.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="false">example-29.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:objID>example-00.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-01.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-02.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-03.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-04.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-05.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-06.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-07.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-08.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-09.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-10.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-11.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-12.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-13.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-14.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-15.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-16.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-17.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-18.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-19.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-20.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-21.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-22.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-23.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-24.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-25.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-26.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-27.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-28.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example-29.tld</fee:objID>
<fee:command name="restore">
<fee:period unit="y">1</fee:period>
<fee:fee description="restore">17.00</fee:fee>
<fee:fee description="renew">11.00</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -0,0 +1,48 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example-00.tld</domain:name>
<domain:name>example-01.tld</domain:name>
<domain:name>example-02.tld</domain:name>
<domain:name>example-03.tld</domain:name>
<domain:name>example-04.tld</domain:name>
<domain:name>example-05.tld</domain:name>
<domain:name>example-06.tld</domain:name>
<domain:name>example-07.tld</domain:name>
<domain:name>example-08.tld</domain:name>
<domain:name>example-09.tld</domain:name>
<domain:name>example-10.tld</domain:name>
<domain:name>example-11.tld</domain:name>
<domain:name>example-12.tld</domain:name>
<domain:name>example-13.tld</domain:name>
<domain:name>example-14.tld</domain:name>
<domain:name>example-15.tld</domain:name>
<domain:name>example-16.tld</domain:name>
<domain:name>example-17.tld</domain:name>
<domain:name>example-18.tld</domain:name>
<domain:name>example-19.tld</domain:name>
<domain:name>example-20.tld</domain:name>
<domain:name>example-21.tld</domain:name>
<domain:name>example-22.tld</domain:name>
<domain:name>example-23.tld</domain:name>
<domain:name>example-24.tld</domain:name>
<domain:name>example-25.tld</domain:name>
<domain:name>example-26.tld</domain:name>
<domain:name>example-27.tld</domain:name>
<domain:name>example-28.tld</domain:name>
<domain:name>example-29.tld</domain:name>
</domain:check>
</check>
<extension>
<launch:check xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="avail">
<launch:phase name="foo">custom</launch:phase>
</launch:check>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:command name="restore"/>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,17 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example1.tld</domain:name>
</domain:check>
</check>
<extension>
<fee:check xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:command name="delete">
<fee:period unit="y">1</fee:period>
</fee:command>
</fee:check>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View File

@@ -0,0 +1,78 @@
<epp xmlns:domain="urn:ietf:params:xml:ns:domain-1.0" xmlns:contact="urn:ietf:params:xml:ns:contact-1.0" xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0" xmlns:bulkToken="urn:google:params:xml:ns:bulkToken-1.0" xmlns:fee="urn:ietf:params:xml:ns:fee-0.12" xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1" xmlns:host="urn:ietf:params:xml:ns:host-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:chkData>
<domain:cd>
<domain:name avail="false">example1.tld</domain:name>
<domain:reason>In use</domain:reason>
</domain:cd>
<domain:cd>
<domain:name avail="true">example2.tld</domain:name>
</domain:cd>
<domain:cd>
<domain:name avail="true">example3.tld</domain:name>
</domain:cd>
</domain:chkData>
</resData>
<extension>
<fee:chkData xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:class>STANDARD</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example1.tld</fee:objID>
<fee:class>STANDARD PROMOTION</fee:class>
<fee:command name="custom">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example2.tld</fee:objID>
<fee:class>STANDARD</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example2.tld</fee:objID>
<fee:class>STANDARD PROMOTION</fee:class>
<fee:command name="custom">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example3.tld</fee:objID>
<fee:class>STANDARD</fee:class>
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
</fee:command>
</fee:cd>
<fee:cd>
<fee:objID>example3.tld</fee:objID>
<fee:class>STANDARD PROMOTION</fee:class>
<fee:command name="custom">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">6.50</fee:fee>
</fee:command>
</fee:cd>
</fee:chkData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -88,4 +88,4 @@
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
</epp>

View File

@@ -10,7 +10,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee>126.00</fee:fee>
</fee:create>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="%DESCRIPTION_1%">24.00</fee:fee>
<fee:fee description="%DESCRIPTION_2%">100.00</fee:fee>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="%DESCRIPTION_1%">%FEE_1%</fee:fee>
<fee:fee description="%DESCRIPTION_2%">%FEE_2%</fee:fee>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>%CURRENCY%</fee:currency>
<fee:fee>24.00</fee:fee>
</fee:create>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee applied="delayed">26.00</fee:fee>
</fee:create>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee>26.000</fee:fee>
</fee:create>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee refundable="true" grace-period="P0D" applied="immediate">24.00</fee:fee>
</fee:create>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee grace-period="P1D">26.00</fee:fee>
</fee:create>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee refundable="false">26.00</fee:fee>
</fee:create>

View File

@@ -1,7 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<epp xmlns:domain="urn:ietf:params:xml:ns:domain-1.0" xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:fee12="urn:ietf:params:xml:ns:fee-0.12"
>
<epp xmlns:domain="urn:ietf:params:xml:ns:domain-1.0" xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
@@ -14,14 +11,14 @@
</domain:creData>
</resData>
<extension>
<fee12:creData>
<fee12:currency>USD</fee12:currency>
<fee12:fee description="create">13.00</fee12:fee>
</fee12:creData>
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="create">13.00</fee:fee>
</fee:creData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>
</epp>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-0.12">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee>200.00</fee:fee>
</fee:create>

View File

@@ -20,7 +20,7 @@
"urn:ietf:params:xml:ns:allocationToken-1.0">
abc123
</allocationToken:allocationToken>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-0.12">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee>%FEE%</fee:fee>
</fee:create>

View File

@@ -15,7 +15,7 @@
</domain:create>
</create>
<extension>
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:create xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="create">200.00</fee:fee>
<fee:fee description="Early Access Period">100.00</fee:fee>

View File

@@ -12,7 +12,7 @@
</domain:creData>
</resData>
<extension>
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="create">24.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 1999-04-04T22:00:00.023Z">100.00</fee:fee>

View File

@@ -12,7 +12,7 @@
</domain:creData>
</resData>
<extension>
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="create">%FEE%</fee:fee>
</fee:creData>

View File

@@ -12,7 +12,7 @@
</domain:creData>
</resData>
<extension>
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:fee-0.12">
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="create">%FEE%</fee:fee>
</fee:creData>

View File

@@ -6,13 +6,13 @@
<resData>
<domain:creData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:name>rich.example</domain:name>
<domain:crDate>1999-04-03T22:00:00.0Z</domain:crDate>
<domain:exDate>2001-04-03T22:00:00.0Z</domain:exDate>
</domain:creData>
</resData>
<extension>
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="create">200.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 1999-04-04T22:00:00.027Z">100.00

View File

@@ -5,7 +5,7 @@
</result>
<extension>
<fee:delData
xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:credit description="autoRenewPeriod credit">-11.00</fee:credit>
</fee:delData>

View File

@@ -5,7 +5,7 @@
</result>
<extension>
<fee:delData
xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:credit description="addPeriod credit">-123.00</fee:credit>
</fee:delData>

View File

@@ -0,0 +1,18 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<extension>
<fee:delData
xmlns:fee="urn:ietf:params:xml:ns:epp:fee-1.0">
<fee:currency>USD</fee:currency>
<fee:credit description="addPeriod credit">0.00</fee:credit>
</fee:delData>
</extension>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View File

@@ -5,7 +5,7 @@
</result>
<extension>
<fee:delData
xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:credit description="renewPeriod credit">-456.00</fee:credit>
</fee:delData>

View File

@@ -9,7 +9,7 @@
</domain:renew>
</renew>
<extension>
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>%CURRENCY%</fee:currency>
<fee:fee>%FEE%</fee:fee>
</fee:renew>

View File

@@ -9,7 +9,7 @@
</domain:renew>
</renew>
<extension>
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee applied="delayed">55.00</fee:fee>
</fee:renew>

View File

@@ -9,7 +9,7 @@
</domain:renew>
</renew>
<extension>
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee>55.000</fee:fee>
</fee:renew>

View File

@@ -9,7 +9,7 @@
</domain:renew>
</renew>
<extension>
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee refundable="true" grace-period="P0D" applied="immediate">55.00</fee:fee>
</fee:renew>

View File

@@ -9,7 +9,7 @@
</domain:renew>
</renew>
<extension>
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee grace-period="P1D">55.00</fee:fee>
</fee:renew>

View File

@@ -9,7 +9,7 @@
</domain:renew>
</renew>
<extension>
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:renew xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee refundable="false">55.00</fee:fee>
</fee:renew>

View File

@@ -11,7 +11,7 @@
</domain:renData>
</resData>
<extension>
<%FEE_NS%:renData xmlns:%FEE_NS%="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<%FEE_NS%:renData xmlns:%FEE_NS%="urn:ietf:params:xml:ns:%FEE_VERSION%">
<%FEE_NS%:currency>%CURRENCY%</%FEE_NS%:currency>
<%FEE_NS%:fee description="renew">%FEE%</%FEE_NS%:fee>
</%FEE_NS%:renData>

View File

@@ -15,4 +15,4 @@
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
</epp>

Some files were not shown because too many files have changed in this diff Show More