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

Compare commits

..

2 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
137 changed files with 7175 additions and 2734 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

@@ -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

@@ -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;
@@ -62,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,
@@ -109,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);
@@ -134,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

@@ -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

@@ -72,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;
@@ -359,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"));
@@ -1534,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

@@ -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>

View File

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

View File

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

View File

@@ -11,7 +11,7 @@
</domain:transfer>
</transfer>
<extension>
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee applied="delayed">11.00</fee:fee>
</fee:transfer>

View File

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

View File

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

View File

@@ -11,7 +11,7 @@
</domain:transfer>
</transfer>
<extension>
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee grace-period="P1D">11.00</fee:fee>
</fee:transfer>

View File

@@ -11,7 +11,7 @@
</domain:transfer>
</transfer>
<extension>
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee refundable="false">11.00</fee:fee>
</fee:transfer>

View File

@@ -16,7 +16,7 @@
</domain:trnData>
</resData>
<extension>
<fee:trnData xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:trnData xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="renew">11.00</fee:fee>
</fee:trnData>

View File

@@ -16,7 +16,7 @@
</domain:trnData>
</resData>
<extension>
<fee:trnData xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:trnData xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="transfer">100.00</fee:fee>

View File

@@ -11,7 +11,7 @@
</domain:transfer>
</transfer>
<extension>
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
<fee:transfer xmlns:fee="urn:ietf:params:xml:ns:%FEE_VERSION%">
<fee:currency>USD</fee:currency>
<fee:fee description="renew">11.00</fee:fee>
<fee:fee description="transfer">100.00</fee:fee>

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