From a1388061995078be342bc03388c61667e36f4e9f Mon Sep 17 00:00:00 2001 From: gbrodman Date: Wed, 21 Jan 2026 16:49:29 -0500 Subject: [PATCH] Re-enable old fee extensions in sandbox (#2939) Now that we've passed the RST testing (or at least the EPP portion of it) we are no longer bound by the restriction to only use the fee extension version 1.0 on sandbox. For now, in order to avoid changing prod behavior, this does not enable advertisement of the fee extension version 1.0 in production. We can change this at any point in the future. --- .../model/eppcommon/EppXmlTransformer.java | 32 +- .../model/eppcommon/ProtocolDefinition.java | 8 +- .../DomainCheckFlowOldFeeExtensionsTest.java | 1321 ----------------- .../flows/domain/DomainCheckFlowTest.java | 1190 +++++++++++++++ .../DomainCreateFlowOldFeeExtensionsTest.java | 1153 -------------- .../flows/domain/DomainCreateFlowTest.java | 793 ++++++++++ .../DomainDeleteFlowOldFeeExtensionsTest.java | 440 ------ .../flows/domain/DomainDeleteFlowTest.java | 147 ++ .../DomainInfoFlowOldFeeExtensionsTest.java | 523 ------- .../flows/domain/DomainInfoFlowTest.java | 358 +++++ .../DomainRenewFlowOldFeeExtensionsTest.java | 827 ----------- .../flows/domain/DomainRenewFlowTest.java | 540 +++++++ ...estoreRequestFlowOldFeeExtensionsTest.java | 351 ----- .../domain/DomainRestoreRequestFlowTest.java | 224 +++ ...ansferRequestFlowOldFeeExtensionsTest.java | 897 ----------- .../domain/DomainTransferRequestFlowTest.java | 264 ++++ ...ProductionSimulatingFeeExtensionsTest.java | 57 +- ...check_fee_premium_stdv1_transfer_only.xml} | 7 +- ...check_fee_response_domain_exists_stdv1.xml | 60 + ...sponse_domain_exists_stdv1_renew_only.xml} | 15 +- ...nse_domain_exists_stdv1_transfer_only.xml} | 15 +- ...n_check_fee_response_domain_exists_v06.xml | 52 - .../google/registry/flows/greeting.xml | 3 + .../registry/flows/session/greeting.xml | 3 + 24 files changed, 3649 insertions(+), 5631 deletions(-) delete mode 100644 core/src/test/java/google/registry/flows/domain/DomainCheckFlowOldFeeExtensionsTest.java delete mode 100644 core/src/test/java/google/registry/flows/domain/DomainCreateFlowOldFeeExtensionsTest.java delete mode 100644 core/src/test/java/google/registry/flows/domain/DomainDeleteFlowOldFeeExtensionsTest.java delete mode 100644 core/src/test/java/google/registry/flows/domain/DomainInfoFlowOldFeeExtensionsTest.java delete mode 100644 core/src/test/java/google/registry/flows/domain/DomainRenewFlowOldFeeExtensionsTest.java delete mode 100644 core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowOldFeeExtensionsTest.java delete mode 100644 core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowOldFeeExtensionsTest.java rename core/src/test/resources/google/registry/flows/domain/{domain_check_fee_premium_v06_transfer_only.xml => domain_check_fee_premium_stdv1_transfer_only.xml} (62%) create mode 100644 core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1.xml rename core/src/test/resources/google/registry/flows/domain/{domain_check_fee_response_domain_exists_v06_renew_only.xml => domain_check_fee_response_domain_exists_stdv1_renew_only.xml} (62%) rename core/src/test/resources/google/registry/flows/domain/{domain_check_fee_response_domain_exists_v06_transfer_only.xml => domain_check_fee_response_domain_exists_stdv1_transfer_only.xml} (62%) delete mode 100644 core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06.xml diff --git a/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java b/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java index 569ddf092..165046612 100644 --- a/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java +++ b/core/src/main/java/google/registry/model/eppcommon/EppXmlTransformer.java @@ -23,7 +23,6 @@ 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; @@ -61,35 +60,20 @@ public class EppXmlTransformer { // XML schemas that should not be used in production (yet) private static final ImmutableSet NON_PROD_SCHEMAS = ImmutableSet.of("fee-std-v1.xsd"); - // XML schemas that should only be used in production (for backcompat) - private static final ImmutableSet 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 = + private static final XmlTransformer INPUT_TRANSFORMER = new XmlTransformer(getSchemas(), EppInput.class); - // 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 = + private static final XmlTransformer OUTPUT_TRANSFORMER = new XmlTransformer(getSchemas(), EppOutput.class); @VisibleForTesting public static ImmutableList getSchemas() { - ImmutableSet 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); + if (RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)) { + return ALL_SCHEMAS.stream() + .filter(s -> !NON_PROD_SCHEMAS.contains(s)) + .collect(toImmutableList()); + } + return ALL_SCHEMAS; } public static void validateOutput(String xml) throws XmlException { diff --git a/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java b/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java index 8a5d8191d..1e6d8f1c5 100644 --- a/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java +++ b/core/src/main/java/google/registry/model/eppcommon/ProtocolDefinition.java @@ -54,7 +54,6 @@ public class ProtocolDefinition { /** Enum representing which environments should have which service extensions enabled. */ private enum ServiceExtensionVisibility { ALL, - ONLY_IN_PRODUCTION, ONLY_IN_NON_PRODUCTION, NONE } @@ -67,15 +66,15 @@ public class ProtocolDefinition { FEE_0_6( FeeCheckCommandExtensionV06.class, FeeCheckResponseExtensionV06.class, - ServiceExtensionVisibility.ONLY_IN_PRODUCTION), + ServiceExtensionVisibility.ALL), FEE_0_11( FeeCheckCommandExtensionV11.class, FeeCheckResponseExtensionV11.class, - ServiceExtensionVisibility.ONLY_IN_PRODUCTION), + ServiceExtensionVisibility.ALL), FEE_0_12( FeeCheckCommandExtensionV12.class, FeeCheckResponseExtensionV12.class, - ServiceExtensionVisibility.ONLY_IN_PRODUCTION), + ServiceExtensionVisibility.ALL), FEE_1_00( FeeCheckCommandExtensionStdV1.class, FeeCheckResponseExtensionStdV1.class, @@ -117,7 +116,6 @@ public class ProtocolDefinition { public boolean isVisible() { return switch (visibility) { case ALL -> true; - case ONLY_IN_PRODUCTION -> RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION); case ONLY_IN_NON_PRODUCTION -> !RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION); case NONE -> false; diff --git a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowOldFeeExtensionsTest.java b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowOldFeeExtensionsTest.java deleted file mode 100644 index b904ab26a..000000000 --- a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowOldFeeExtensionsTest.java +++ /dev/null @@ -1,1321 +0,0 @@ -// 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.flows.domain.DomainCheckFlowTest.createReservedList; -import static google.registry.flows.domain.DomainCheckFlowTest.setUpDefaultToken; -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.model.domain.token.AllocationToken.TokenType.SINGLE_USE; -import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE; -import static google.registry.model.eppoutput.CheckData.DomainCheck.create; -import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE; -import static google.registry.testing.DatabaseHelper.assertNoBillingEvents; -import static google.registry.testing.DatabaseHelper.createTld; -import static google.registry.testing.DatabaseHelper.persistActiveDomain; -import static google.registry.testing.DatabaseHelper.persistBillingRecurrenceForDomain; -import static google.registry.testing.DatabaseHelper.persistPremiumList; -import static google.registry.testing.DatabaseHelper.persistReservedList; -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.JPY; -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.Ordering; -import google.registry.flows.EppException; -import google.registry.flows.FlowUtils.NotLoggedInException; -import google.registry.flows.domain.DomainCheckFlow.OnlyCheckedNamesCanBeFeeCheckedException; -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.flows.domain.DomainFlowUtils.UnknownFeeCommandException; -import google.registry.model.billing.BillingBase.Flag; -import google.registry.model.billing.BillingBase.Reason; -import google.registry.model.billing.BillingRecurrence; -import google.registry.model.domain.Domain; -import google.registry.model.domain.DomainHistory; -import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName; -import google.registry.model.domain.token.AllocationToken; -import google.registry.model.domain.token.AllocationToken.TokenStatus; -import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppoutput.CheckData; -import google.registry.model.registrar.Registrar; -import google.registry.model.reporting.HistoryEntry; -import google.registry.model.tld.Tld; -import google.registry.model.tld.Tld.TldState; -import google.registry.testing.DatabaseHelper; -import java.math.BigDecimal; -import org.joda.money.Money; -import org.joda.time.DateTime; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** Tests for {@link DomainCheckFlow} that use the older (0.6, 0.11, 0.12) fee extensions. */ -public class DomainCheckFlowOldFeeExtensionsTest - extends ProductionSimulatingFeeExtensionsTest { - - @BeforeEach - void oldFeeExtensionTestBeforeEach() { - createTld("tld", TldState.QUIET_PERIOD); - persistResource(Tld.get("tld").asBuilder().setReservedLists(createReservedList()).build()); - } - - @Test - void testNotLoggedIn_takesPrecedenceOverUndeclaredExtensions() { - // Attempt to use the fee extension, but there is no login session and no supported extensions. - setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")); - sessionMetadata.setRegistrarId(null); - sessionMetadata.setServiceExtensionUris(ImmutableSet.of()); - // NotLoggedIn should be thrown, not UndeclaredServiceExtensionException. - EppException thrown = assertThrows(NotLoggedInException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - /** Test the same as {@link #testFeeExtension_multipleCommands_v06} with premium labels. */ - @Test - void testFeeExtension_premiumLabels_v06() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v06.xml")); - } - - /** Test the same as {@link #testFeeExtension_multipleCommands_v06} with premium labels. */ - @Test - void testFeeExtension_premiumLabels_doesNotApplyDefaultToken_v06() throws Exception { - createTld("example"); - AllocationToken defaultToken = - persistResource( - new AllocationToken.Builder() - .setToken("bbbbb") - .setTokenType(DEFAULT_PROMO) - .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) - .setAllowedTlds(ImmutableSet.of("example")) - .setDiscountPremiums(false) - .setDiscountFraction(0.5) - .build()); - persistResource( - Tld.get("example") - .asBuilder() - .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) - .build()); - setEppInput("domain_check_fee_premium_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v06.xml")); - } - - @Test - void testFeeExtension_existingPremiumDomain_withNonPremiumRenewalBehavior() throws Exception { - createTld("example"); - persistBillingRecurrenceForDomain(persistActiveDomain("rich.example"), NONPREMIUM, null); - setEppInput("domain_check_fee_premium_v06.xml"); - runFlowAssertResponse( - loadFile( - "domain_check_fee_response_domain_exists_v06.xml", - ImmutableMap.of("RENEWPRICE", "11.00"))); - } - - @Test - void testFeeExtension_existingPremiumDomain_withNonPremiumRenewalBehavior_renewPriceOnly() - throws Exception { - createTld("example"); - persistBillingRecurrenceForDomain(persistActiveDomain("rich.example"), NONPREMIUM, null); - setEppInput("domain_check_fee_premium_v06_renew_only.xml"); - runFlowAssertResponse( - loadFile( - "domain_check_fee_response_domain_exists_v06_renew_only.xml", - ImmutableMap.of("RENEWPRICE", "11.00"))); - } - - @Test - void testFeeExtension_existingPremiumDomain_withNonPremiumRenewalBehavior_transferPriceOnly() - throws Exception { - createTld("example"); - persistBillingRecurrenceForDomain(persistActiveDomain("rich.example"), NONPREMIUM, null); - setEppInput("domain_check_fee_premium_v06_transfer_only.xml"); - runFlowAssertResponse( - loadFile( - "domain_check_fee_response_domain_exists_v06_transfer_only.xml", - ImmutableMap.of("RENEWPRICE", "11.00"))); - } - - @Test - void testFeeExtension_existingPremiumDomain_withSpecifiedRenewalBehavior() throws Exception { - createTld("example"); - persistBillingRecurrenceForDomain( - persistActiveDomain("rich.example"), SPECIFIED, Money.of(USD, new BigDecimal("15.55"))); - setEppInput("domain_check_fee_premium_v06.xml"); - runFlowAssertResponse( - loadFile( - "domain_check_fee_response_domain_exists_v06.xml", - ImmutableMap.of("RENEWPRICE", "15.55"))); - } - - @Test - void testFeeExtension_premium_eap_v06() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v06.xml"); - clock.setTo(DateTime.parse("2010-01-01T10:00:00Z")); - persistResource( - Tld.get("example") - .asBuilder() - .setEapFeeSchedule( - new ImmutableSortedMap.Builder(Ordering.natural()) - .put(START_OF_TIME, Money.of(USD, 0)) - .put(clock.nowUtc().minusDays(1), Money.of(USD, 100)) - .put(clock.nowUtc().plusDays(1), Money.of(USD, 50)) - .put(clock.nowUtc().plusDays(2), Money.of(USD, 0)) - .build()) - .build()); - - runFlowAssertResponse(loadFile("domain_check_fee_premium_eap_response_v06.xml")); - } - - @Test - void testFeeExtension_premium_eap_v06_withRenewalOnRestore() throws Exception { - createTld("example"); - DateTime startTime = DateTime.parse("2010-01-01T10:00:00Z"); - clock.setTo(startTime); - persistResource( - persistActiveDomain("rich.example") - .asBuilder() - .setDeletionTime(clock.nowUtc().plusDays(25)) - .setRegistrationExpirationTime(clock.nowUtc().minusDays(1)) - .setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE)) - .build()); - persistPendingDeleteDomain("rich.example"); - setEppInput("domain_check_fee_premium_v06.xml"); - persistResource( - Tld.get("example") - .asBuilder() - .setEapFeeSchedule( - new ImmutableSortedMap.Builder(Ordering.natural()) - .put(START_OF_TIME, Money.of(USD, 0)) - .put(startTime.minusDays(1), Money.of(USD, 100)) - .put(startTime.plusDays(1), Money.of(USD, 50)) - .put(startTime.plusDays(2), Money.of(USD, 0)) - .build()) - .build()); - runFlowAssertResponse(loadFile("domain_check_fee_premium_eap_response_v06_with_renewal.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v11_create() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v11_create.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_create.xml")); - } - - @Test - void testFeeExtension_premiumLabels_doesNotApplyDefaultToken_v11() throws Exception { - createTld("example"); - AllocationToken defaultToken = - persistResource( - new AllocationToken.Builder() - .setToken("bbbbb") - .setTokenType(DEFAULT_PROMO) - .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) - .setAllowedTlds(ImmutableSet.of("example")) - .setDiscountPremiums(false) - .setDiscountFraction(0.5) - .build()); - persistResource( - Tld.get("example") - .asBuilder() - .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) - .build()); - setEppInput("domain_check_fee_premium_v11_create.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_create.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v11_renew() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v11_renew.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_renew.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v11_transfer() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v11_transfer.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_transfer.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v11_restore() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v11_restore.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_restore.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v11_restore_withRenewal() throws Exception { - setEppInput("domain_check_fee_premium_v11_restore.xml"); - createTld("example"); - persistPendingDeleteDomain("rich.example"); - runFlowAssertResponse( - loadFile("domain_check_fee_premium_response_v11_restore_with_renewal.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v11_update() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v11_update.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_update.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v12() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12.xml")); - } - - @Test - void testFeeExtension_multipleCommands_defaultTokenOnlyOnCreate_v12() throws Exception { - setUpDefaultToken(); - setEppInput("domain_check_fee_multiple_commands_v12.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_multiple_commands_default_token_response_v12.xml")); - } - - @Disabled("TODO(b/454680236): broken test") - @Test - void testFeeExtension_defaultToken_notValidForAllLabels_v06() throws Exception { - createTld("example"); - AllocationToken defaultToken = - persistResource( - new AllocationToken.Builder() - .setToken("bbbbb") - .setTokenType(DEFAULT_PROMO) - .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) - .setAllowedTlds(ImmutableSet.of("example")) - .setDiscountPremiums(false) - .setDiscountFraction(0.5) - .build()); - persistResource( - Tld.get("example") - .asBuilder() - .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) - .build()); - setEppInput("domain_check_fee_default_token_multiple_names_v06.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_default_token_multiple_names_response_v06.xml")); - } - - @Disabled("TODO(b/454680236): broken") - @Test - void testFeeExtension_defaultToken_notValidForAllLabels_v11() throws Exception { - createTld("example"); - AllocationToken defaultToken = - persistResource( - new AllocationToken.Builder() - .setToken("bbbbb") - .setTokenType(DEFAULT_PROMO) - .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) - .setAllowedTlds(ImmutableSet.of("example")) - .setDiscountPremiums(false) - .setDiscountFraction(0.5) - .build()); - persistResource( - Tld.get("example") - .asBuilder() - .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) - .build()); - setEppInput("domain_check_fee_default_token_multiple_names_v11.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_default_token_multiple_names_response_v11.xml")); - } - - @Disabled("TODO(b/454680236): broken test") - @Test - void testFeeExtension_defaultToken_notValidForAllLabels_v12() throws Exception { - createTld("example"); - AllocationToken defaultToken = - persistResource( - new AllocationToken.Builder() - .setToken("bbbbb") - .setTokenType(DEFAULT_PROMO) - .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) - .setAllowedTlds(ImmutableSet.of("example")) - .setDiscountPremiums(false) - .setDiscountFraction(0.5) - .build()); - persistResource( - Tld.get("example") - .asBuilder() - .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) - .build()); - setEppInput("domain_check_fee_default_token_multiple_names_v12.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_default_token_multiple_names_response_v12.xml")); - } - - /** - * Test commands for create, renew, transfer, restore and update with implicit period and - * currency. - */ - @Test - void testFeeExtension_multipleCommands_v06() throws Exception { - setEppInput("domain_check_fee_multiple_commands_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_multiple_commands_response_v06.xml")); - } - - @Test - void testFeeExtension_multipleCommands_tokenNotValidForSome_v06() throws Exception { - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(UNLIMITED_USE) - .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.TRANSFER)) - .setDiscountFraction(0.1) - .build()); - setEppInput("domain_check_fee_multiple_commands_allocationtoken_v06.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_multiple_commands_allocationtoken_response_v06.xml")); - } - - @Test - void testFeeExtension_multipleCommands_defaultTokenOnlyOnCreate_v06() throws Exception { - setUpDefaultToken(); - setEppInput("domain_check_fee_multiple_commands_v06.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_multiple_commands_default_token_response_v06.xml")); - } - - // Version 11 cannot have multiple commands. - - @Test - void testFeeExtension_multipleCommands_v12() throws Exception { - setEppInput("domain_check_fee_multiple_commands_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_multiple_commands_response_v12.xml")); - } - - @Test - void testFeeExtension_multipleCommands_tokenNotValidForSome_v12() throws Exception { - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(UNLIMITED_USE) - .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.TRANSFER)) - .setDiscountFraction(0.1) - .build()); - setEppInput("domain_check_fee_multiple_commands_allocationtoken_v12.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_multiple_commands_allocationtoken_response_v12.xml")); - } - - @Test - void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v06() throws Exception { - createTld("tld", START_DATE_SUNRISE); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v06.xml")); - } - - @Test - void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v06_withRestoreRenewals() - throws Exception { - createTld("tld", START_DATE_SUNRISE); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - persistPendingDeleteDomain("reserved.tld"); - persistPendingDeleteDomain("allowedinsunrise.tld"); - persistPendingDeleteDomain("collision.tld"); - persistPendingDeleteDomain("premiumcollision.tld"); - setEppInput("domain_check_fee_reserved_v06.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_reserved_sunrise_response_v06_with_renewals.xml")); - } - - @Test - void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_create() throws Exception { - createTld("tld", START_DATE_SUNRISE); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_create.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_create.xml")); - } - - @Test - void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_renew() throws Exception { - createTld("tld", START_DATE_SUNRISE); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_renew.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_renew.xml")); - } - - @Test - void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_transfer() throws Exception { - createTld("tld", START_DATE_SUNRISE); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_transfer.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_transfer.xml")); - } - - @Test - void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_restore() throws Exception { - createTld("tld", START_DATE_SUNRISE); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_restore.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_restore.xml")); - } - - @Test - void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v12() throws Exception { - createTld("tld", START_DATE_SUNRISE); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v12.xml")); - } - - @Test - void testFeeExtension_wrongCurrency_v06() { - setEppInput("domain_check_fee_euro_v06.xml"); - EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_wrongCurrency_v11() { - setEppInput("domain_check_fee_euro_v11.xml"); - EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_invalidCommand_v06() { - setEppInput("domain_check_fee_invalid_command_v06.xml"); - EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_invalidCommand_v11() { - setEppInput("domain_check_fee_invalid_command_v11.xml"); - EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_invalidCommand_v12() { - setEppInput("domain_check_fee_invalid_command_v12.xml"); - EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_reservedName_v06() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v06.xml")); - } - - @Test - void testFeeExtension_reservedName_restoreFeeWithDupes_v06() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - // The domain needs to exist in order for it to be loaded to check for restore fee. - persistBillingRecurrenceForDomain(persistActiveDomain("allowedinsunrise.tld"), DEFAULT, null); - setEppInput("domain_check_fee_reserved_dupes_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_dupes_v06.xml")); - } - - /** The tests must be split up for version 11, which allows only one command at a time. */ - @Test - void testFeeExtension_reservedName_v11_create() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_create.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_create.xml")); - } - - @Test - void testFeeExtension_reservedName_v11_renew() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_renew.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_renew.xml")); - } - - @Test - void testFeeExtension_reservedName_v11_transfer() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_transfer.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_transfer.xml")); - } - - @Test - void testFeeExtension_reservedName_v11_restore() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v11_restore.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_restore.xml")); - } - - @Test - void testFeeExtension_reservedName_v11_restore_withRenewals() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - persistPendingDeleteDomain("reserved.tld"); - persistPendingDeleteDomain("allowedinsunrise.tld"); - persistPendingDeleteDomain("collision.tld"); - persistPendingDeleteDomain("premiumcollision.tld"); - setEppInput("domain_check_fee_reserved_v11_restore.xml"); - runFlowAssertResponse( - loadFile("domain_check_fee_reserved_response_v11_restore_with_renewals.xml")); - } - - @Test - void testFeeExtension_reservedName_v12() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - setEppInput("domain_check_fee_reserved_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v12.xml")); - } - - @Test - void testFeeExtension_reservedName_restoreFeeWithDupes_v12() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists(createReservedList()) - .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) - .build()); - // The domain needs to exist in order for it to be loaded to check for restore fee. - setEppInput("domain_check_fee_reserved_dupes_v12.xml"); - persistBillingRecurrenceForDomain(persistActiveDomain("allowedinsunrise.tld"), DEFAULT, null); - runFlowAssertResponse(loadFile("domain_check_fee_reserved_dupes_response_v12.xml")); - } - - @Test - void testFeeExtension_periodNotInYears_v06() { - setEppInput("domain_check_fee_bad_period_v06.xml"); - EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_periodNotInYears_v11() { - setEppInput("domain_check_fee_bad_period_v11.xml"); - EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_unknownCommand_v06() { - setEppInput("domain_check_fee_unknown_command_v06.xml"); - EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_unknownCommand_v11() { - setEppInput("domain_check_fee_unknown_command_v11.xml"); - EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - /** Test multiyear periods and explicitly correct currency and that the avail extension is ok. */ - @Test - void testFeeExtension_v06() throws Exception { - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")); - runFlowAssertResponse(loadFile("domain_check_fee_response_v06.xml")); - } - - @Test - void testFeeExtension_defaultToken_v06() throws Exception { - setUpDefaultToken(); - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")); - runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v06.xml")); - } - - @Test - void testFeeExtension_multipleReservations() throws Exception { - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists( - persistReservedList("example-sunrise", "allowedinsunrise,ALLOWED_IN_SUNRISE")) - .build()); - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")); - runFlowAssertResponse(loadFile("domain_check_fee_response_v06.xml")); - } - - @Test - void testFeeExtension_v11() throws Exception { - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v11.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_response_v11.xml")); - } - - @Test - void testFeeExtension_defaultToken_v11() throws Exception { - setUpDefaultToken(); - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v11.xml", ImmutableMap.of("CURRENCY", "USD")); - runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v11.xml")); - } - - @Test - void testFeeExtension_v12() throws Exception { - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml")); - } - - @Test - void testFeeExtension_defaultToken_v12() throws Exception { - setUpDefaultToken(); - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v12.xml", ImmutableMap.of("CURRENCY", "USD")); - runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v12.xml")); - } - - @Test - void testFeeExtension_premiumLabels_v12_specifiedPriceRenewal_renewPriceOnly() throws Exception { - createTld("example"); - persistBillingRecurrenceForDomain( - persistActiveDomain("rich.example"), SPECIFIED, Money.of(USD, new BigDecimal("27.74"))); - setEppInput("domain_check_fee_premium_v12_renew_only.xml"); - runFlowAssertResponse( - loadFile( - "domain_check_fee_premium_response_v12_renew_only.xml", - ImmutableMap.of("RENEWPRICE", "27.74"))); - } - - @Test - void testFeeExtension_premiumLabels_doesNotApplyDefaultToken_v12() throws Exception { - createTld("example"); - AllocationToken defaultToken = - persistResource( - new AllocationToken.Builder() - .setToken("bbbbb") - .setTokenType(DEFAULT_PROMO) - .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) - .setAllowedTlds(ImmutableSet.of("example")) - .setDiscountPremiums(false) - .setDiscountFraction(0.5) - .build()); - persistResource( - Tld.get("example") - .asBuilder() - .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) - .build()); - setEppInput("domain_check_fee_premium_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12.xml")); - } - - @Test - void testFeeExtension_commandSubphase_v06() { - setEppInput("domain_check_fee_command_subphase_v06.xml"); - EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_commandSubphase_v11() { - setEppInput("domain_check_fee_command_subphase_v11.xml"); - EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - // This test is only relevant for v06, since domain names are not specified after. - @Test - void testFeeExtension_feeCheckNotInAvailabilityCheck() { - setEppInput("domain_check_fee_not_in_avail.xml"); - EppException thrown = - assertThrows(OnlyCheckedNamesCanBeFeeCheckedException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_multiyearRestore_v06() { - setEppInput("domain_check_fee_multiyear_restore_v06.xml"); - EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_multiyearRestore_v11() { - setEppInput("domain_check_fee_multiyear_restore_v11.xml"); - EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_multiyearTransfer_v06() { - setEppInput("domain_check_fee_multiyear_transfer_v06.xml"); - EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_multiyearTransfer_v11() { - setEppInput("domain_check_fee_multiyear_transfer_v11.xml"); - EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_premiumLabels_v12_withRenewalOnRestore() throws Exception { - createTld("example"); - setEppInput("domain_check_fee_premium_v12.xml"); - persistPendingDeleteDomain("rich.example"); - runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12_with_renewal.xml")); - } - - @Test - void testFeeExtension_commandWithPhase_v06() { - setEppInput("domain_check_fee_command_phase_v06.xml"); - EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_commandWithPhase_v11() { - setEppInput("domain_check_fee_command_phase_v11.xml"); - EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_multiyearRestore_v12() { - setEppInput("domain_check_fee_multiyear_restore_v12.xml"); - EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_multiyearTransfer_v12() { - setEppInput("domain_check_fee_multiyear_transfer_v12.xml"); - EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_unknownCommand_v12() { - setEppInput("domain_check_fee_unknown_command_v12.xml"); - EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_wrongCurrency_v12() { - setEppInput("domain_check_fee_euro_v12.xml"); - EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_periodNotInYears_v12() { - setEppInput("domain_check_fee_bad_period_v12.xml"); - EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_commandWithPhase_v12() { - setEppInput("domain_check_fee_command_phase_v12.xml"); - EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFeeExtension_commandSubphase_v12() { - setEppInput("domain_check_fee_command_subphase_v12.xml"); - EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testSuccess_multipleCurencies_v12() throws Exception { - persistResource( - createTld("example") - .asBuilder() - .setCurrency(JPY) - .setCreateBillingCostTransitions( - ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(JPY, 800))) - .setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(JPY, 800))) - .setRenewBillingCostTransitions( - ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(JPY, 800))) - .setRegistryLockOrUnlockBillingCost(Money.ofMajor(JPY, 800)) - .setServerStatusChangeBillingCost(Money.ofMajor(JPY, 800)) - .setRestoreBillingCost(Money.ofMajor(JPY, 800)) - .build()); - persistResource( - Registrar.loadByRegistrarId("TheRegistrar") - .get() - .asBuilder() - .setBillingAccountMap(ImmutableMap.of(USD, "foo", JPY, "bar")) - .build()); - setEppInput("domain_check_fee_multiple_currencies_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_multiple_currencies_response_v12.xml")); - } - - @Test - void testTieredPricingPromoResponse_v12() throws Exception { - sessionMetadata.setRegistrarId("NewRegistrar"); - setUpDefaultToken("NewRegistrar"); - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_tiered_promotion_fee_response_v12.xml")); - } - - @Test - void testTieredPricingPromo_registrarNotIncluded_standardResponse_v12() throws Exception { - setUpDefaultToken("NewRegistrar"); - persistActiveDomain("example1.tld"); - setEppInput("domain_check_fee_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml")); - } - - @Test - void testTieredPricingPromo_registrarIncluded_noTokenActive_v12() throws Exception { - sessionMetadata.setRegistrarId("NewRegistrar"); - persistActiveDomain("example1.tld"); - - persistResource( - setUpDefaultToken("NewRegistrar") - .asBuilder() - .setTokenStatusTransitions( - ImmutableSortedMap.of( - START_OF_TIME, - TokenStatus.NOT_STARTED, - clock.nowUtc().plusDays(1), - TokenStatus.VALID)) - .build()); - - setEppInput("domain_check_fee_v12.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml")); - } - - @Test - void testFeeExtension_fractionalCost_v06() throws Exception { - // Note that the response xml expects to see "11.10" with two digits after the decimal point. - // This works because Money.getAmount(), used in the flow, returns a BigDecimal that is set to - // display the number of digits that is conventional for the given currency. - persistResource( - Tld.get("tld") - .asBuilder() - .setCreateBillingCostTransitions( - ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 11.1))) - .build()); - setEppInput("domain_check_fee_fractional_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_fee_fractional_response_v06.xml")); - } - - @Test - void testSuccess_allocationTokenPromotion_doesNotUseValidDefaultToken_singleYear_v06() - throws Exception { - setUpDefaultToken(); - createTld("example"); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(UNLIMITED_USE) - .setDiscountFraction(0.5) - .setDiscountYears(2) - .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE)) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) - .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) - .build()) - .build()); - setEppInput("domain_check_allocationtoken_fee_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response_v06.xml")); - } - - @Test - void testSuccess_thirtyDomains_restoreFees_v06() throws Exception { - // Note that 30 is more than 25, which is the maximum # of entity groups you can enlist in a - // single database transaction (each Domain entity is in a separate entity group). - // It's also pretty common for registrars to send large domain checks. - setEppInput("domain_check_fee_thirty_domains_v06.xml"); - // example-00.tld won't exist and thus will not have a renew fee like the others. - for (int i = 1; i < 30; i++) { - persistPendingDeleteDomain(String.format("example-%02d.tld", i)); - } - runFlowAssertResponse(loadFile("domain_check_fee_response_thirty_domains_v06.xml")); - } - - @Test - void testSuccess_allocationTokenPromotion_multiYear_v06() throws Exception { - createTld("tld"); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setDomainName("single.tld") - .setDiscountFraction(0.444) - .setDiscountYears(2) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) - .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) - .build()) - .build()); - setEppInput( - "domain_check_allocationtoken_promotion_v06.xml", ImmutableMap.of("DOMAIN", "single.tld")); - // 1-yr: 13 * .556 - // 2-yr: (13 + 11) * .556 - // 5-yr: 2-yr-cost + 3 * 11 - runFlowAssertResponse( - loadFile( - "domain_check_allocationtoken_promotion_response_v06.xml", - new ImmutableMap.Builder() - .put("DOMAIN", "single.tld") - .put("COST_1YR", "7.23") - .put("COST_2YR", "13.34") - .put("COST_5YR", "46.34") - .put("FEE_CLASS", "") - .build())); - } - - @Test - void testSuccess_allocationTokenPromotion_multiYearAndPremiums() throws Exception { - createTld("example"); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setDomainName("rich.example") - .setDiscountFraction(0.9) - .setDiscountYears(3) - .setDiscountPremiums(true) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) - .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) - .build()) - .build()); - setEppInput( - "domain_check_allocationtoken_promotion_v06.xml", - ImmutableMap.of("DOMAIN", "rich.example")); - runFlowAssertResponse( - loadFile( - "domain_check_allocationtoken_promotion_response_v06.xml", - new ImmutableMap.Builder() - .put("DOMAIN", "rich.example") - .put("COST_1YR", "10.00") - .put("COST_2YR", "20.00") - .put("COST_5YR", "230.00") - .put("FEE_CLASS", "premium") - .build())); - } - - @Test - void testSuccess_allocationToken_premiumAnchorTenant_noFee_v06() throws Exception { - createTld("example"); - persistResource( - Tld.get("tld") - .asBuilder() - .setPremiumList(persistPremiumList("example1", USD, "example1,USD 100")) - .build()); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setRegistrationBehavior(AllocationToken.RegistrationBehavior.ANCHOR_TENANT) - .setDomainName("example1.tld") - .build()); - setEppInput("domain_check_allocationtoken_fee_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_anchor_response_v06.xml")); - } - - @Test - void testSuccess_promotionNotActive_v06() throws Exception { - createTld("example"); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(UNLIMITED_USE) - .setDiscountFraction(0.5) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().plusDays(1), TokenStatus.VALID) - .put(clock.nowUtc().plusDays(60), TokenStatus.ENDED) - .build()) - .build()); - setEppInput("domain_check_allocationtoken_fee_v06.xml"); - assertMutatingFlow(false); - assertThat(((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks()) - .containsExactly( - create(false, "example1.tld", "Alloc token not in promo period"), - create(false, "example2.example", "Alloc token not in promo period"), - create(false, "reserved.tld", "Alloc token not in promo period"), - create(false, "rich.example", "Alloc token not in promo period")); - assertNoBillingEvents(); // Checks are always free. - assertNoHistory(); // Checks don't create a history event. - } - - @Test - void testSuccess_allocationTokenPromotion_singleYear_v06() throws Exception { - createTld("example"); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(UNLIMITED_USE) - .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE)) - .setDiscountFraction(0.5) - .setDiscountYears(2) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) - .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) - .build()) - .build()); - setEppInput("domain_check_allocationtoken_fee_v06.xml"); - runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response_v06.xml")); - } - - @Test - void testSuccess_promoTokenNotValidForTld_v06() throws Exception { - createTld("example"); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(UNLIMITED_USE) - .setDiscountFraction(0.5) - .setAllowedTlds(ImmutableSet.of("example")) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) - .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) - .build()) - .build()); - setEppInput("domain_check_allocationtoken_fee_v06.xml"); - assertMutatingFlow(false); - assertThat(((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks()) - .containsExactly( - create(true, "example1.tld", null), - create(true, "example2.example", null), - create(false, "reserved.tld", "Reserved"), - create(true, "rich.example", null)); - assertNoBillingEvents(); // Checks are always free. - assertNoHistory(); // Checks don't create a history event. - } - - @Test - void testSuccess_promoTokenNotValidForRegistrar_v06() throws Exception { - createTld("example"); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(UNLIMITED_USE) - .setDiscountFraction(0.5) - .setAllowedRegistrarIds(ImmutableSet.of("someOtherClient")) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) - .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) - .build()) - .build()); - setEppInput("domain_check_allocationtoken_fee_v06.xml"); - assertMutatingFlow(false); - assertThat(((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks()) - .containsExactly( - create(false, "example1.tld", "Alloc token invalid for client"), - create(false, "example2.example", "Alloc token invalid for client"), - create(false, "reserved.tld", "Alloc token invalid for client"), - create(false, "rich.example", "Alloc token invalid for client")); - assertNoBillingEvents(); // Checks are always free. - assertNoHistory(); // Checks don't create a history event. - } - - @Test - void testSuccess_allocationTokenForReservedDomain_showsFee_v06() throws Exception { - setEppInput("domain_check_allocationtoken_fee_specificuse_v06.xml"); - createTld("example"); - persistResource( - new AllocationToken.Builder() - .setDomainName("specificuse.tld") - .setToken("abc123") - .setTokenType(SINGLE_USE) - .build()); - // Fees are shown for all non-reserved domains and the reserved domain matching this - // allocation token. - runFlowAssertResponse( - loadFile("domain_check_allocationtoken_fee_specificuse_response_v06.xml")); - } - - @Test - void testSuccess_eapFeeCheck_date_v12() throws Exception { - runEapFeeCheckTestWithXmlInputOutput( - loadFile("domain_check_fee_date_v12.xml", ImmutableMap.of("CURRENCY", "USD")), - loadFile("domain_check_eap_fee_response_date_v12.xml")); - } - - @Test - void testSuccess_eapFeeCheck_v06() throws Exception { - runEapFeeCheckTestWithXmlInputOutput( - loadFile("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")), - loadFile("domain_check_eap_fee_response_v06.xml")); - } - - @Test - void testSuccess_eapFeeCheck_v11() throws Exception { - runEapFeeCheckTestWithXmlInputOutput( - loadFile("domain_check_fee_v11.xml", ImmutableMap.of("CURRENCY", "USD")), - loadFile("domain_check_eap_fee_response_v11.xml")); - } - - @Test - void testSuccess_eapFeeCheck_v12() throws Exception { - runEapFeeCheckTestWithXmlInputOutput( - loadFile("domain_check_fee_v12.xml", ImmutableMap.of("CURRENCY", "USD")), - loadFile("domain_check_eap_fee_response_v12.xml")); - } - - private Domain persistPendingDeleteDomain(String domainName) { - Domain existingDomain = - persistResource( - DatabaseHelper.newDomain(domainName) - .asBuilder() - .setDeletionTime(clock.nowUtc().plusDays(25)) - .setRegistrationExpirationTime(clock.nowUtc().minusDays(1)) - .setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE)) - .build()); - DomainHistory historyEntry = - persistResource( - new DomainHistory.Builder() - .setDomain(existingDomain) - .setType(HistoryEntry.Type.DOMAIN_DELETE) - .setModificationTime(existingDomain.getCreationTime()) - .setRegistrarId(existingDomain.getCreationRegistrarId()) - .build()); - BillingRecurrence renewEvent = - persistResource( - new BillingRecurrence.Builder() - .setReason(Reason.RENEW) - .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) - .setTargetId(existingDomain.getDomainName()) - .setRegistrarId("TheRegistrar") - .setEventTime(existingDomain.getCreationTime()) - .setRecurrenceEndTime(clock.nowUtc()) - .setDomainHistory(historyEntry) - .build()); - return persistResource( - existingDomain.asBuilder().setAutorenewBillingEvent(renewEvent.createVKey()).build()); - } - - private void runEapFeeCheckTestWithXmlInputOutput(String inputXml, String outputXml) - throws Exception { - clock.setTo(DateTime.parse("2010-01-01T10:00:00Z")); - persistActiveDomain("example1.tld"); - persistResource( - Tld.get("tld") - .asBuilder() - .setEapFeeSchedule( - new ImmutableSortedMap.Builder(Ordering.natural()) - .put(START_OF_TIME, Money.of(USD, 0)) - .put(clock.nowUtc().minusDays(1), Money.of(USD, 100)) - .put(clock.nowUtc().plusDays(1), Money.of(USD, 50)) - .put(clock.nowUtc().plusDays(2), Money.of(USD, 0)) - .build()) - .build()); - setEppInputXml(inputXml); - runFlowAssertResponse(outputXml); - } -} diff --git a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java index 19521efd2..295903ff3 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java @@ -14,8 +14,10 @@ package google.registry.flows.domain; +import static com.google.common.truth.Truth.assertThat; import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel; 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.model.domain.token.AllocationToken.TokenType.REGISTER_BSA; @@ -24,6 +26,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIM import static google.registry.model.eppoutput.CheckData.DomainCheck.create; import static google.registry.model.tld.Tld.TldState.PREDELEGATION; import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE; +import static google.registry.testing.DatabaseHelper.assertNoBillingEvents; import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.createTlds; import static google.registry.testing.DatabaseHelper.loadRegistrar; @@ -49,6 +52,7 @@ import google.registry.flows.FlowUtils.GenericXmlSyntaxErrorException; import google.registry.flows.FlowUtils.NotLoggedInException; import google.registry.flows.FlowUtils.UnknownCurrencyEppException; import google.registry.flows.ResourceCheckFlowTestCase; +import google.registry.flows.domain.DomainCheckFlow.OnlyCheckedNamesCanBeFeeCheckedException; import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException; import google.registry.flows.domain.DomainFlowUtils.BadDomainNameCharacterException; import google.registry.flows.domain.DomainFlowUtils.BadDomainNamePartsCountException; @@ -80,6 +84,7 @@ import google.registry.model.domain.feestdv1.FeeCheckCommandExtensionStdV1.Multi import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppoutput.CheckData; import google.registry.model.registrar.Registrar; import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId; @@ -92,6 +97,7 @@ 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.Disabled; import org.junit.jupiter.api.Test; /** Unit tests for {@link DomainCheckFlow}. */ @@ -1205,6 +1211,1190 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase(Ordering.natural()) + .put(START_OF_TIME, Money.of(USD, 0)) + .put(clock.nowUtc().minusDays(1), Money.of(USD, 100)) + .put(clock.nowUtc().plusDays(1), Money.of(USD, 50)) + .put(clock.nowUtc().plusDays(2), Money.of(USD, 0)) + .build()) + .build()); + + runFlowAssertResponse(loadFile("domain_check_fee_premium_eap_response_v06.xml")); + } + + @Test + void testFeeExtension_premium_eap_v06_withRenewalOnRestore() throws Exception { + createTld("example"); + DateTime startTime = DateTime.parse("2010-01-01T10:00:00Z"); + clock.setTo(startTime); + persistResource( + persistActiveDomain("rich.example") + .asBuilder() + .setDeletionTime(clock.nowUtc().plusDays(25)) + .setRegistrationExpirationTime(clock.nowUtc().minusDays(1)) + .setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE)) + .build()); + persistPendingDeleteDomain("rich.example"); + setEppInput("domain_check_fee_premium_v06.xml"); + persistResource( + Tld.get("example") + .asBuilder() + .setEapFeeSchedule( + new ImmutableSortedMap.Builder(Ordering.natural()) + .put(START_OF_TIME, Money.of(USD, 0)) + .put(startTime.minusDays(1), Money.of(USD, 100)) + .put(startTime.plusDays(1), Money.of(USD, 50)) + .put(startTime.plusDays(2), Money.of(USD, 0)) + .build()) + .build()); + runFlowAssertResponse(loadFile("domain_check_fee_premium_eap_response_v06_with_renewal.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v11_create() throws Exception { + createTld("example"); + setEppInput("domain_check_fee_premium_v11_create.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_create.xml")); + } + + @Test + void testFeeExtension_premiumLabels_doesNotApplyDefaultToken_v11() throws Exception { + createTld("example"); + AllocationToken defaultToken = + persistResource( + new AllocationToken.Builder() + .setToken("bbbbb") + .setTokenType(DEFAULT_PROMO) + .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) + .setAllowedTlds(ImmutableSet.of("example")) + .setDiscountPremiums(false) + .setDiscountFraction(0.5) + .build()); + persistResource( + Tld.get("example") + .asBuilder() + .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) + .build()); + setEppInput("domain_check_fee_premium_v11_create.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_create.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v11_renew() throws Exception { + createTld("example"); + setEppInput("domain_check_fee_premium_v11_renew.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_renew.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v11_transfer() throws Exception { + createTld("example"); + setEppInput("domain_check_fee_premium_v11_transfer.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_transfer.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v11_restore() throws Exception { + createTld("example"); + setEppInput("domain_check_fee_premium_v11_restore.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_restore.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v11_restore_withRenewal() throws Exception { + setEppInput("domain_check_fee_premium_v11_restore.xml"); + createTld("example"); + persistPendingDeleteDomain("rich.example"); + runFlowAssertResponse( + loadFile("domain_check_fee_premium_response_v11_restore_with_renewal.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v11_update() throws Exception { + createTld("example"); + setEppInput("domain_check_fee_premium_v11_update.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v11_update.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v12() throws Exception { + createTld("example"); + setEppInput("domain_check_fee_premium_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12.xml")); + } + + @Test + void testFeeExtension_multipleCommands_defaultTokenOnlyOnCreate_v12() throws Exception { + setUpDefaultToken(); + setEppInput("domain_check_fee_multiple_commands_v12.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_multiple_commands_default_token_response_v12.xml")); + } + + @Disabled("TODO(b/454680236): broken test") + @Test + void testFeeExtension_defaultToken_notValidForAllLabels_v06() throws Exception { + createTld("example"); + AllocationToken defaultToken = + persistResource( + new AllocationToken.Builder() + .setToken("bbbbb") + .setTokenType(DEFAULT_PROMO) + .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) + .setAllowedTlds(ImmutableSet.of("example")) + .setDiscountPremiums(false) + .setDiscountFraction(0.5) + .build()); + persistResource( + Tld.get("example") + .asBuilder() + .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) + .build()); + setEppInput("domain_check_fee_default_token_multiple_names_v06.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_default_token_multiple_names_response_v06.xml")); + } + + @Disabled("TODO(b/454680236): broken") + @Test + void testFeeExtension_defaultToken_notValidForAllLabels_v11() throws Exception { + createTld("example"); + AllocationToken defaultToken = + persistResource( + new AllocationToken.Builder() + .setToken("bbbbb") + .setTokenType(DEFAULT_PROMO) + .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) + .setAllowedTlds(ImmutableSet.of("example")) + .setDiscountPremiums(false) + .setDiscountFraction(0.5) + .build()); + persistResource( + Tld.get("example") + .asBuilder() + .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) + .build()); + setEppInput("domain_check_fee_default_token_multiple_names_v11.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_default_token_multiple_names_response_v11.xml")); + } + + @Disabled("TODO(b/454680236): broken test") + @Test + void testFeeExtension_defaultToken_notValidForAllLabels_v12() throws Exception { + createTld("example"); + AllocationToken defaultToken = + persistResource( + new AllocationToken.Builder() + .setToken("bbbbb") + .setTokenType(DEFAULT_PROMO) + .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) + .setAllowedTlds(ImmutableSet.of("example")) + .setDiscountPremiums(false) + .setDiscountFraction(0.5) + .build()); + persistResource( + Tld.get("example") + .asBuilder() + .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) + .build()); + setEppInput("domain_check_fee_default_token_multiple_names_v12.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_default_token_multiple_names_response_v12.xml")); + } + + /** + * Test commands for create, renew, transfer, restore and update with implicit period and + * currency. + */ + @Test + void testFeeExtension_multipleCommands_v06() throws Exception { + setEppInput("domain_check_fee_multiple_commands_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_multiple_commands_response_v06.xml")); + } + + @Test + void testFeeExtension_multipleCommands_tokenNotValidForSome_v06() throws Exception { + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(UNLIMITED_USE) + .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.TRANSFER)) + .setDiscountFraction(0.1) + .build()); + setEppInput("domain_check_fee_multiple_commands_allocationtoken_v06.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_multiple_commands_allocationtoken_response_v06.xml")); + } + + @Test + void testFeeExtension_multipleCommands_defaultTokenOnlyOnCreate_v06() throws Exception { + setUpDefaultToken(); + setEppInput("domain_check_fee_multiple_commands_v06.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_multiple_commands_default_token_response_v06.xml")); + } + + // Version 11 cannot have multiple commands. + + @Test + void testFeeExtension_multipleCommands_v12() throws Exception { + setEppInput("domain_check_fee_multiple_commands_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_multiple_commands_response_v12.xml")); + } + + @Test + void testFeeExtension_multipleCommands_tokenNotValidForSome_v12() throws Exception { + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(UNLIMITED_USE) + .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE, CommandName.TRANSFER)) + .setDiscountFraction(0.1) + .build()); + setEppInput("domain_check_fee_multiple_commands_allocationtoken_v12.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_multiple_commands_allocationtoken_response_v12.xml")); + } + + @Test + void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v06() throws Exception { + createTld("tld", START_DATE_SUNRISE); + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v06.xml")); + } + + @Test + void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v06_withRestoreRenewals() + throws Exception { + createTld("tld", START_DATE_SUNRISE); + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + persistPendingDeleteDomain("reserved.tld"); + persistPendingDeleteDomain("allowedinsunrise.tld"); + persistPendingDeleteDomain("collision.tld"); + persistPendingDeleteDomain("premiumcollision.tld"); + setEppInput("domain_check_fee_reserved_v06.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_reserved_sunrise_response_v06_with_renewals.xml")); + } + + @Test + void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_create() throws Exception { + createTld("tld", START_DATE_SUNRISE); + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_create.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_create.xml")); + } + + @Test + void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_renew() throws Exception { + createTld("tld", START_DATE_SUNRISE); + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_renew.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_renew.xml")); + } + + @Test + void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_transfer() throws Exception { + createTld("tld", START_DATE_SUNRISE); + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_transfer.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_transfer.xml")); + } + + @Test + void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v11_restore() throws Exception { + createTld("tld", START_DATE_SUNRISE); + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_restore.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v11_restore.xml")); + } + + @Test + void testFeeExtension_feesNotOmittedOnReservedNamesInSunrise_v12() throws Exception { + createTld("tld", START_DATE_SUNRISE); + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_sunrise_response_v12.xml")); + } + + @Test + void testFeeExtension_wrongCurrency_v06() { + setEppInput("domain_check_fee_euro_v06.xml"); + EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_wrongCurrency_v11() { + setEppInput("domain_check_fee_euro_v11.xml"); + EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_invalidCommand_v06() { + setEppInput("domain_check_fee_invalid_command_v06.xml"); + EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_invalidCommand_v11() { + setEppInput("domain_check_fee_invalid_command_v11.xml"); + EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_invalidCommand_v12() { + setEppInput("domain_check_fee_invalid_command_v12.xml"); + EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_reservedName_v06() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v06.xml")); + } + + @Test + void testFeeExtension_reservedName_restoreFeeWithDupes_v06() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + // The domain needs to exist in order for it to be loaded to check for restore fee. + persistBillingRecurrenceForDomain(persistActiveDomain("allowedinsunrise.tld"), DEFAULT, null); + setEppInput("domain_check_fee_reserved_dupes_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_dupes_v06.xml")); + } + + /** The tests must be split up for version 11, which allows only one command at a time. */ + @Test + void testFeeExtension_reservedName_v11_create() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_create.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_create.xml")); + } + + @Test + void testFeeExtension_reservedName_v11_renew() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_renew.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_renew.xml")); + } + + @Test + void testFeeExtension_reservedName_v11_transfer() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_transfer.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_transfer.xml")); + } + + @Test + void testFeeExtension_reservedName_v11_restore() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v11_restore.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v11_restore.xml")); + } + + @Test + void testFeeExtension_reservedName_v11_restore_withRenewals() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + persistPendingDeleteDomain("reserved.tld"); + persistPendingDeleteDomain("allowedinsunrise.tld"); + persistPendingDeleteDomain("collision.tld"); + persistPendingDeleteDomain("premiumcollision.tld"); + setEppInput("domain_check_fee_reserved_v11_restore.xml"); + runFlowAssertResponse( + loadFile("domain_check_fee_reserved_response_v11_restore_with_renewals.xml")); + } + + @Test + void testFeeExtension_reservedName_v12() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + setEppInput("domain_check_fee_reserved_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_response_v12.xml")); + } + + @Test + void testFeeExtension_reservedName_restoreFeeWithDupes_v12() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists(createReservedList()) + .setPremiumList(persistPremiumList("tld", USD, "premiumcollision,USD 70")) + .build()); + // The domain needs to exist in order for it to be loaded to check for restore fee. + setEppInput("domain_check_fee_reserved_dupes_v12.xml"); + persistBillingRecurrenceForDomain(persistActiveDomain("allowedinsunrise.tld"), DEFAULT, null); + runFlowAssertResponse(loadFile("domain_check_fee_reserved_dupes_response_v12.xml")); + } + + @Test + void testFeeExtension_periodNotInYears_v06() { + setEppInput("domain_check_fee_bad_period_v06.xml"); + EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_periodNotInYears_v11() { + setEppInput("domain_check_fee_bad_period_v11.xml"); + EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_unknownCommand_v06() { + setEppInput("domain_check_fee_unknown_command_v06.xml"); + EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_unknownCommand_v11() { + setEppInput("domain_check_fee_unknown_command_v11.xml"); + EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + /** Test multiyear periods and explicitly correct currency and that the avail extension is ok. */ + @Test + void testFeeExtension_v06() throws Exception { + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")); + runFlowAssertResponse(loadFile("domain_check_fee_response_v06.xml")); + } + + @Test + void testFeeExtension_defaultToken_v06() throws Exception { + setUpDefaultToken(); + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")); + runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v06.xml")); + } + + @Test + void testFeeExtension_multipleReservations() throws Exception { + persistResource( + Tld.get("tld") + .asBuilder() + .setReservedLists( + persistReservedList("example-sunrise", "allowedinsunrise,ALLOWED_IN_SUNRISE")) + .build()); + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")); + runFlowAssertResponse(loadFile("domain_check_fee_response_v06.xml")); + } + + @Test + void testFeeExtension_v11() throws Exception { + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v11.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_response_v11.xml")); + } + + @Test + void testFeeExtension_defaultToken_v11() throws Exception { + setUpDefaultToken(); + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v11.xml", ImmutableMap.of("CURRENCY", "USD")); + runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v11.xml")); + } + + @Test + void testFeeExtension_v12() throws Exception { + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml")); + } + + @Test + void testFeeExtension_defaultToken_v12() throws Exception { + setUpDefaultToken(); + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v12.xml", ImmutableMap.of("CURRENCY", "USD")); + runFlowAssertResponse(loadFile("domain_check_fee_response_default_token_v12.xml")); + } + + @Test + void testFeeExtension_premiumLabels_v12_specifiedPriceRenewal_renewPriceOnly() throws Exception { + createTld("example"); + persistBillingRecurrenceForDomain( + persistActiveDomain("rich.example"), SPECIFIED, Money.of(USD, new BigDecimal("27.74"))); + setEppInput("domain_check_fee_premium_v12_renew_only.xml"); + runFlowAssertResponse( + loadFile( + "domain_check_fee_premium_response_v12_renew_only.xml", + ImmutableMap.of("RENEWPRICE", "27.74"))); + } + + @Test + void testFeeExtension_premiumLabels_doesNotApplyDefaultToken_v12() throws Exception { + createTld("example"); + AllocationToken defaultToken = + persistResource( + new AllocationToken.Builder() + .setToken("bbbbb") + .setTokenType(DEFAULT_PROMO) + .setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar")) + .setAllowedTlds(ImmutableSet.of("example")) + .setDiscountPremiums(false) + .setDiscountFraction(0.5) + .build()); + persistResource( + Tld.get("example") + .asBuilder() + .setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey())) + .build()); + setEppInput("domain_check_fee_premium_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12.xml")); + } + + @Test + void testFeeExtension_commandSubphase_v06() { + setEppInput("domain_check_fee_command_subphase_v06.xml"); + EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_commandSubphase_v11() { + setEppInput("domain_check_fee_command_subphase_v11.xml"); + EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + // This test is only relevant for v06, since domain names are not specified after. + @Test + void testFeeExtension_feeCheckNotInAvailabilityCheck() { + setEppInput("domain_check_fee_not_in_avail.xml"); + EppException thrown = + assertThrows(OnlyCheckedNamesCanBeFeeCheckedException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_multiyearRestore_v06() { + setEppInput("domain_check_fee_multiyear_restore_v06.xml"); + EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_multiyearRestore_v11() { + setEppInput("domain_check_fee_multiyear_restore_v11.xml"); + EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_multiyearTransfer_v06() { + setEppInput("domain_check_fee_multiyear_transfer_v06.xml"); + EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_multiyearTransfer_v11() { + setEppInput("domain_check_fee_multiyear_transfer_v11.xml"); + EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_premiumLabels_v12_withRenewalOnRestore() throws Exception { + createTld("example"); + setEppInput("domain_check_fee_premium_v12.xml"); + persistPendingDeleteDomain("rich.example"); + runFlowAssertResponse(loadFile("domain_check_fee_premium_response_v12_with_renewal.xml")); + } + + @Test + void testFeeExtension_commandWithPhase_v06() { + setEppInput("domain_check_fee_command_phase_v06.xml"); + EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_commandWithPhase_v11() { + setEppInput("domain_check_fee_command_phase_v11.xml"); + EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_multiyearRestore_v12() { + setEppInput("domain_check_fee_multiyear_restore_v12.xml"); + EppException thrown = assertThrows(RestoresAreAlwaysForOneYearException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_multiyearTransfer_v12() { + setEppInput("domain_check_fee_multiyear_transfer_v12.xml"); + EppException thrown = assertThrows(TransfersAreAlwaysForOneYearException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_unknownCommand_v12() { + setEppInput("domain_check_fee_unknown_command_v12.xml"); + EppException thrown = assertThrows(UnknownFeeCommandException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_wrongCurrency_v12() { + setEppInput("domain_check_fee_euro_v12.xml"); + EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_periodNotInYears_v12() { + setEppInput("domain_check_fee_bad_period_v12.xml"); + EppException thrown = assertThrows(BadPeriodUnitException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_commandWithPhase_v12() { + setEppInput("domain_check_fee_command_phase_v12.xml"); + EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFeeExtension_commandSubphase_v12() { + setEppInput("domain_check_fee_command_subphase_v12.xml"); + EppException thrown = assertThrows(FeeChecksDontSupportPhasesException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testSuccess_multipleCurencies_v12() throws Exception { + persistResource( + createTld("example") + .asBuilder() + .setCurrency(JPY) + .setCreateBillingCostTransitions( + ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(JPY, 800))) + .setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(JPY, 800))) + .setRenewBillingCostTransitions( + ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(JPY, 800))) + .setRegistryLockOrUnlockBillingCost(Money.ofMajor(JPY, 800)) + .setServerStatusChangeBillingCost(Money.ofMajor(JPY, 800)) + .setRestoreBillingCost(Money.ofMajor(JPY, 800)) + .build()); + persistResource( + Registrar.loadByRegistrarId("TheRegistrar") + .get() + .asBuilder() + .setBillingAccountMap(ImmutableMap.of(USD, "foo", JPY, "bar")) + .build()); + setEppInput("domain_check_fee_multiple_currencies_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_multiple_currencies_response_v12.xml")); + } + + @Test + void testTieredPricingPromoResponse_v12() throws Exception { + sessionMetadata.setRegistrarId("NewRegistrar"); + setUpDefaultToken("NewRegistrar"); + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_tiered_promotion_fee_response_v12.xml")); + } + + @Test + void testTieredPricingPromo_registrarNotIncluded_standardResponse_v12() throws Exception { + setUpDefaultToken("NewRegistrar"); + persistActiveDomain("example1.tld"); + setEppInput("domain_check_fee_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml")); + } + + @Test + void testTieredPricingPromo_registrarIncluded_noTokenActive_v12() throws Exception { + sessionMetadata.setRegistrarId("NewRegistrar"); + persistActiveDomain("example1.tld"); + + persistResource( + setUpDefaultToken("NewRegistrar") + .asBuilder() + .setTokenStatusTransitions( + ImmutableSortedMap.of( + START_OF_TIME, + TokenStatus.NOT_STARTED, + clock.nowUtc().plusDays(1), + TokenStatus.VALID)) + .build()); + + setEppInput("domain_check_fee_v12.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_response_v12.xml")); + } + + @Test + void testFeeExtension_fractionalCost_v06() throws Exception { + // Note that the response xml expects to see "11.10" with two digits after the decimal point. + // This works because Money.getAmount(), used in the flow, returns a BigDecimal that is set to + // display the number of digits that is conventional for the given currency. + persistResource( + Tld.get("tld") + .asBuilder() + .setCreateBillingCostTransitions( + ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 11.1))) + .build()); + setEppInput("domain_check_fee_fractional_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_fee_fractional_response_v06.xml")); + } + + @Test + void testSuccess_allocationTokenPromotion_doesNotUseValidDefaultToken_singleYear_v06() + throws Exception { + setUpDefaultToken(); + createTld("example"); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(UNLIMITED_USE) + .setDiscountFraction(0.5) + .setDiscountYears(2) + .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE)) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) + .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) + .build()) + .build()); + setEppInput("domain_check_allocationtoken_fee_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response_v06.xml")); + } + + @Test + void testSuccess_thirtyDomains_restoreFees_v06() throws Exception { + // Note that 30 is more than 25, which is the maximum # of entity groups you can enlist in a + // single database transaction (each Domain entity is in a separate entity group). + // It's also pretty common for registrars to send large domain checks. + setEppInput("domain_check_fee_thirty_domains_v06.xml"); + // example-00.tld won't exist and thus will not have a renew fee like the others. + for (int i = 1; i < 30; i++) { + persistPendingDeleteDomain(String.format("example-%02d.tld", i)); + } + runFlowAssertResponse(loadFile("domain_check_fee_response_thirty_domains_v06.xml")); + } + + @Test + void testSuccess_allocationTokenPromotion_multiYear_v06() throws Exception { + createTld("tld"); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setDomainName("single.tld") + .setDiscountFraction(0.444) + .setDiscountYears(2) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) + .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) + .build()) + .build()); + setEppInput( + "domain_check_allocationtoken_promotion_v06.xml", ImmutableMap.of("DOMAIN", "single.tld")); + // 1-yr: 13 * .556 + // 2-yr: (13 + 11) * .556 + // 5-yr: 2-yr-cost + 3 * 11 + runFlowAssertResponse( + loadFile( + "domain_check_allocationtoken_promotion_response_v06.xml", + new ImmutableMap.Builder() + .put("DOMAIN", "single.tld") + .put("COST_1YR", "7.23") + .put("COST_2YR", "13.34") + .put("COST_5YR", "46.34") + .put("FEE_CLASS", "") + .build())); + } + + @Test + void testSuccess_allocationTokenPromotion_multiYearAndPremiums_v06() throws Exception { + createTld("example"); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setDomainName("rich.example") + .setDiscountFraction(0.9) + .setDiscountYears(3) + .setDiscountPremiums(true) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) + .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) + .build()) + .build()); + setEppInput( + "domain_check_allocationtoken_promotion_v06.xml", + ImmutableMap.of("DOMAIN", "rich.example")); + runFlowAssertResponse( + loadFile( + "domain_check_allocationtoken_promotion_response_v06.xml", + new ImmutableMap.Builder() + .put("DOMAIN", "rich.example") + .put("COST_1YR", "10.00") + .put("COST_2YR", "20.00") + .put("COST_5YR", "230.00") + .put("FEE_CLASS", "premium") + .build())); + } + + @Test + void testSuccess_allocationToken_premiumAnchorTenant_noFee_v06() throws Exception { + createTld("example"); + persistResource( + Tld.get("tld") + .asBuilder() + .setPremiumList(persistPremiumList("example1", USD, "example1,USD 100")) + .build()); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setRegistrationBehavior(AllocationToken.RegistrationBehavior.ANCHOR_TENANT) + .setDomainName("example1.tld") + .build()); + setEppInput("domain_check_allocationtoken_fee_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_anchor_response_v06.xml")); + } + + @Test + void testSuccess_promotionNotActive_v06() throws Exception { + createTld("example"); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(UNLIMITED_USE) + .setDiscountFraction(0.5) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().plusDays(1), TokenStatus.VALID) + .put(clock.nowUtc().plusDays(60), TokenStatus.ENDED) + .build()) + .build()); + setEppInput("domain_check_allocationtoken_fee_v06.xml"); + assertMutatingFlow(false); + assertThat(((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks()) + .containsExactly( + create(false, "example1.tld", "Alloc token not in promo period"), + create(false, "example2.example", "Alloc token not in promo period"), + create(false, "reserved.tld", "Alloc token not in promo period"), + create(false, "rich.example", "Alloc token not in promo period")); + assertNoBillingEvents(); // Checks are always free. + assertNoHistory(); // Checks don't create a history event. + } + + @Test + void testSuccess_allocationTokenPromotion_singleYear_v06() throws Exception { + createTld("example"); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(UNLIMITED_USE) + .setAllowedEppActions(ImmutableSet.of(CommandName.CREATE)) + .setDiscountFraction(0.5) + .setDiscountYears(2) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) + .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) + .build()) + .build()); + setEppInput("domain_check_allocationtoken_fee_v06.xml"); + runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response_v06.xml")); + } + + @Test + void testSuccess_promoTokenNotValidForTld_v06() throws Exception { + createTld("example"); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(UNLIMITED_USE) + .setDiscountFraction(0.5) + .setAllowedTlds(ImmutableSet.of("example")) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) + .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) + .build()) + .build()); + setEppInput("domain_check_allocationtoken_fee_v06.xml"); + assertMutatingFlow(false); + assertThat(((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks()) + .containsExactly( + create(true, "example1.tld", null), + create(true, "example2.example", null), + create(false, "reserved.tld", "Reserved"), + create(true, "rich.example", null)); + assertNoBillingEvents(); // Checks are always free. + assertNoHistory(); // Checks don't create a history event. + } + + @Test + void testSuccess_promoTokenNotValidForRegistrar_v06() throws Exception { + createTld("example"); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(UNLIMITED_USE) + .setDiscountFraction(0.5) + .setAllowedRegistrarIds(ImmutableSet.of("someOtherClient")) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().minusDays(1), TokenStatus.VALID) + .put(clock.nowUtc().plusDays(1), TokenStatus.ENDED) + .build()) + .build()); + setEppInput("domain_check_allocationtoken_fee_v06.xml"); + assertMutatingFlow(false); + assertThat(((CheckData) runFlow().getResponse().getResponseData().get(0)).getChecks()) + .containsExactly( + create(false, "example1.tld", "Alloc token invalid for client"), + create(false, "example2.example", "Alloc token invalid for client"), + create(false, "reserved.tld", "Alloc token invalid for client"), + create(false, "rich.example", "Alloc token invalid for client")); + assertNoBillingEvents(); // Checks are always free. + assertNoHistory(); // Checks don't create a history event. + } + + @Test + void testSuccess_allocationTokenForReservedDomain_showsFee_v06() throws Exception { + setEppInput("domain_check_allocationtoken_fee_specificuse_v06.xml"); + createTld("example"); + persistResource( + new AllocationToken.Builder() + .setDomainName("specificuse.tld") + .setToken("abc123") + .setTokenType(SINGLE_USE) + .build()); + // Fees are shown for all non-reserved domains and the reserved domain matching this + // allocation token. + runFlowAssertResponse( + loadFile("domain_check_allocationtoken_fee_specificuse_response_v06.xml")); + } + + @Test + void testSuccess_eapFeeCheck_date_v12() throws Exception { + runEapFeeCheckTestWithXmlInputOutput( + loadFile("domain_check_fee_date_v12.xml", ImmutableMap.of("CURRENCY", "USD")), + loadFile("domain_check_eap_fee_response_date_v12.xml")); + } + + @Test + void testSuccess_eapFeeCheck_v06() throws Exception { + runEapFeeCheckTestWithXmlInputOutput( + loadFile("domain_check_fee_v06.xml", ImmutableMap.of("CURRENCY", "USD")), + loadFile("domain_check_eap_fee_response_v06.xml")); + } + + @Test + void testSuccess_eapFeeCheck_v11() throws Exception { + runEapFeeCheckTestWithXmlInputOutput( + loadFile("domain_check_fee_v11.xml", ImmutableMap.of("CURRENCY", "USD")), + loadFile("domain_check_eap_fee_response_v11.xml")); + } + + @Test + void testSuccess_eapFeeCheck_v12() throws Exception { + runEapFeeCheckTestWithXmlInputOutput( + loadFile("domain_check_fee_v12.xml", ImmutableMap.of("CURRENCY", "USD")), + loadFile("domain_check_eap_fee_response_v12.xml")); + } + private Domain persistPendingDeleteDomain(String domainName) { Domain existingDomain = persistResource( diff --git a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowOldFeeExtensionsTest.java b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowOldFeeExtensionsTest.java deleted file mode 100644 index 4c91ea6ca..000000000 --- a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowOldFeeExtensionsTest.java +++ /dev/null @@ -1,1153 +0,0 @@ -// 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.flows.FlowTestCase.UserPrivileges.SUPERUSER; -import static google.registry.model.billing.BillingBase.Flag.ANCHOR_TENANT; -import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM; -import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED; -import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS; -import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO; -import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; -import static google.registry.testing.DatabaseHelper.assertBillingEvents; -import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests; -import static google.registry.testing.DatabaseHelper.assertPollMessagesForResource; -import static google.registry.testing.DatabaseHelper.createTld; -import static google.registry.testing.DatabaseHelper.getHistoryEntries; -import static google.registry.testing.DatabaseHelper.loadAllOf; -import static google.registry.testing.DatabaseHelper.loadRegistrar; -import static google.registry.testing.DatabaseHelper.persistActiveContact; -import static google.registry.testing.DatabaseHelper.persistActiveDomain; -import static google.registry.testing.DatabaseHelper.persistActiveHost; -import static google.registry.testing.DatabaseHelper.persistReservedList; -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.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.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.ExtensionManager.UndeclaredServiceExtensionException; -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.FeeDescriptionMultipleMatchesException; -import google.registry.flows.domain.DomainFlowUtils.FeeDescriptionParseException; -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.billing.BillingBase; -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.domain.token.AllocationToken.RegistrationBehavior; -import google.registry.model.domain.token.AllocationToken.TokenStatus; -import google.registry.model.poll.PollMessage; -import google.registry.model.reporting.HistoryEntry; -import google.registry.model.tld.Tld; -import google.registry.persistence.VKey; -import google.registry.testing.DatabaseHelper; -import google.registry.tmch.LordnTaskUtils.LordnPhase; -import java.math.BigDecimal; -import java.util.Map; -import java.util.Optional; -import javax.annotation.Nullable; -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 DomainCreateFlow} that use the old fee extensions (0.6, 0.11, 0.12). */ -public class DomainCreateFlowOldFeeExtensionsTest - extends ProductionSimulatingFeeExtensionsTest { - - private static final ImmutableMap FEE_06_MAP = - ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee", "CURRENCY", "USD", "FEE", "15.00"); - private static final ImmutableMap FEE_11_MAP = - ImmutableMap.of( - "FEE_VERSION", "fee-0.11", "FEE_NS", "fee", "CURRENCY", "USD", "FEE", "15.00"); - private static final ImmutableMap FEE_12_MAP = - ImmutableMap.of( - "FEE_VERSION", "fee-0.12", "FEE_NS", "fee", "CURRENCY", "USD", "FEE", "15.00"); - - private static final String CLAIMS_KEY = "2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001"; - - private AllocationToken allocationToken; - - @BeforeEach - void beforeEachDomainCreateFlowOldFeeExtensionsTest() { - setEppInput("domain_create.xml", ImmutableMap.of("DOMAIN", "example.tld")); - clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z").minus(Duration.millis(2))); - createTld("tld"); - allocationToken = - persistResource( - new AllocationToken.Builder() - .setToken("abcDEF23456") - .setTokenType(SINGLE_USE) - .setDomainName("anchor.tld") - .build()); - persistResource( - Tld.get("tld") - .asBuilder() - .setReservedLists( - persistReservedList( - "tld-reserved", - "reserved,FULLY_BLOCKED", - "resdom,RESERVED_FOR_SPECIFIC_USE", - "anchor,RESERVED_FOR_ANCHOR_TENANT", - "test-and-validate,NAME_COLLISION", - "badcrash,NAME_COLLISION"), - persistReservedList("global-list", "resdom,FULLY_BLOCKED")) - .build()); - persistClaimsList(ImmutableMap.of("example-one", CLAIMS_KEY, "test-validate", CLAIMS_KEY)); - } - - private void persistContactsAndHosts() { - persistContactsAndHosts("net"); // domain_create.xml uses hosts on "net". - } - - /** - * Create host and contact entries for testing. - * - * @param hostTld the TLD of the host (which might be an external TLD) - */ - private void persistContactsAndHosts(String hostTld) { - for (int i = 1; i <= 14; ++i) { - persistActiveHost(String.format("ns%d.example.%s", i, hostTld)); - } - persistActiveContact("jd1234"); - persistActiveContact("sh8013"); - clock.advanceOneMilli(); - } - - private AllocationToken setupDefaultTokenWithDiscount() { - return setupDefaultTokenWithDiscount("TheRegistrar"); - } - - private AllocationToken setupDefaultTokenWithDiscount(String registrarId) { - return setupDefaultToken("bbbbb", 0.5, registrarId); - } - - private AllocationToken setupDefaultToken( - String token, double discountFraction, String registrarId) { - AllocationToken allocationToken = - persistResource( - new AllocationToken.Builder() - .setToken(token) - .setTokenType(DEFAULT_PROMO) - .setAllowedRegistrarIds(ImmutableSet.of(registrarId)) - .setAllowedTlds(ImmutableSet.of("tld")) - .setDiscountFraction(discountFraction) - .build()); - Tld tld = Tld.get("tld"); - persistResource( - tld.asBuilder() - .setDefaultPromoTokens( - ImmutableList.>builder() - .addAll(tld.getDefaultPromoTokens()) - .add(allocationToken.createVKey()) - .build()) - .build()); - return allocationToken; - } - - private void setEapForTld(String tld) { - persistResource( - Tld.get(tld) - .asBuilder() - .setEapFeeSchedule( - ImmutableSortedMap.of( - START_OF_TIME, - Money.of(USD, 0), - clock.nowUtc().minusDays(1), - Money.of(USD, 100), - clock.nowUtc().plusDays(1), - Money.of(USD, 0))) - .build()); - } - - private void doSuccessfulTest( - String domainTld, - String responseXmlFile, - UserPrivileges userPrivileges, - Map substitutions) - throws Exception { - assertMutatingFlow(true); - runFlowAssertResponse( - CommitMode.LIVE, userPrivileges, loadFile(responseXmlFile, substitutions)); - assertSuccessfulCreate(domainTld, ImmutableSet.of(), 24); - assertNoLordn(); - } - - private void doSuccessfulTest( - String domainTld, String responseXmlFile, Map substitutions) - throws Exception { - doSuccessfulTest(domainTld, responseXmlFile, UserPrivileges.NORMAL, substitutions); - } - - private void doSuccessfulTest(String domainTld) throws Exception { - doSuccessfulTest( - domainTld, "domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")); - } - - private void doSuccessfulTest() throws Exception { - doSuccessfulTest("tld"); - } - - private void assertSuccessfulCreate(String domainTld, ImmutableSet expectedBillingFlags) - throws Exception { - assertSuccessfulCreate(domainTld, expectedBillingFlags, null, 24, null); - } - - private void assertSuccessfulCreate( - String domainTld, ImmutableSet expectedBillingFlags, double createCost) - throws Exception { - assertSuccessfulCreate(domainTld, expectedBillingFlags, null, createCost, null); - } - - private void assertSuccessfulCreate( - String domainTld, ImmutableSet expectedBillingFlags, AllocationToken token) - throws Exception { - assertSuccessfulCreate(domainTld, expectedBillingFlags, token, 24, null); - } - - private void assertSuccessfulCreate( - String domainTld, - ImmutableSet expectedBillingFlags, - AllocationToken token, - double createCost) - throws Exception { - assertSuccessfulCreate(domainTld, expectedBillingFlags, token, createCost, null); - } - - private void assertSuccessfulCreate( - String domainTld, - ImmutableSet expectedBillingFlags, - @Nullable AllocationToken token, - double createCost, - @Nullable Integer specifiedRenewCost) - throws Exception { - Domain domain = reloadResourceByForeignKey(); - - boolean isAnchorTenant = expectedBillingFlags.contains(ANCHOR_TENANT); - // Set up the creation cost. - Money eapFee = - Money.of( - Tld.get(domainTld).getCurrency(), - Tld.get(domainTld).getEapFeeFor(clock.nowUtc()).getCost()); - DateTime billingTime = - isAnchorTenant - ? clock.nowUtc().plus(Tld.get(domainTld).getAnchorTenantAddGracePeriodLength()) - : clock.nowUtc().plus(Tld.get(domainTld).getAddGracePeriodLength()); - assertLastHistoryContainsResource(domain); - DomainHistory historyEntry = getHistoryEntries(domain, DomainHistory.class).get(0); - assertAboutDomains() - .that(domain) - .hasRegistrationExpirationTime( - tm().transact(() -> tm().loadByKey(domain.getAutorenewBillingEvent()).getEventTime())) - .and() - .hasOnlyOneHistoryEntryWhich() - .hasType(HistoryEntry.Type.DOMAIN_CREATE) - .and() - .hasPeriodYears(2); - RenewalPriceBehavior expectedRenewalPriceBehavior = - isAnchorTenant - ? RenewalPriceBehavior.NONPREMIUM - : Optional.ofNullable(token) - .map(AllocationToken::getRenewalPriceBehavior) - .orElse(RenewalPriceBehavior.DEFAULT); - // There should be one bill for the create and one for the recurrence autorenew event. - BillingEvent createBillingEvent = - new BillingEvent.Builder() - .setReason(Reason.CREATE) - .setTargetId(getUniqueIdFromCommand()) - .setRegistrarId("TheRegistrar") - .setCost(Money.of(USD, BigDecimal.valueOf(createCost))) - .setPeriodYears(2) - .setEventTime(clock.nowUtc()) - .setBillingTime(billingTime) - .setFlags(expectedBillingFlags) - .setDomainHistory(historyEntry) - .setAllocationToken(Optional.ofNullable(token).map(t -> t.createVKey()).orElse(null)) - .build(); - - BillingRecurrence renewBillingEvent = - new BillingRecurrence.Builder() - .setReason(Reason.RENEW) - .setFlags(ImmutableSet.of(Flag.AUTO_RENEW)) - .setTargetId(getUniqueIdFromCommand()) - .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) - .setRecurrenceEndTime(END_OF_TIME) - .setDomainHistory(historyEntry) - .setRenewalPriceBehavior(expectedRenewalPriceBehavior) - .setRenewalPrice( - Optional.ofNullable(specifiedRenewCost) - .map(r -> Money.of(USD, BigDecimal.valueOf(r))) - .orElse(null)) - .build(); - - ImmutableSet.Builder expectedBillingEvents = - new ImmutableSet.Builder().add(createBillingEvent).add(renewBillingEvent); - - // If EAP is applied, a billing event for EAP should be present. - // EAP fees are bypassed for anchor tenant domains. - if (!isAnchorTenant && !eapFee.isZero()) { - BillingEvent eapBillingEvent = - new BillingEvent.Builder() - .setReason(Reason.FEE_EARLY_ACCESS) - .setTargetId(getUniqueIdFromCommand()) - .setRegistrarId("TheRegistrar") - .setPeriodYears(1) - .setCost(eapFee) - .setEventTime(clock.nowUtc()) - .setBillingTime(billingTime) - .setFlags(expectedBillingFlags) - .setDomainHistory(historyEntry) - .build(); - expectedBillingEvents.add(eapBillingEvent); - } - assertBillingEvents(expectedBillingEvents.build()); - assertPollMessagesForResource( - domain, - new PollMessage.Autorenew.Builder() - .setTargetId(domain.getDomainName()) - .setRegistrarId("TheRegistrar") - .setEventTime(domain.getRegistrationExpirationTime()) - .setMsg("Domain was auto-renewed.") - .setHistoryEntry(historyEntry) - .build()); - - assertGracePeriods( - domain.getGracePeriods(), - ImmutableMap.of( - GracePeriod.create( - GracePeriodStatus.ADD, domain.getRepoId(), billingTime, "TheRegistrar", null), - createBillingEvent)); - assertDomainDnsRequests(getUniqueIdFromCommand()); - } - - private void assertNoLordn() throws Exception { - assertAboutDomains() - .that(reloadResourceByForeignKey()) - .hasSmdId(null) - .and() - .hasLaunchNotice(null) - .and() - .hasLordnPhase(LordnPhase.NONE); - } - - @Test - void testFailure_wrongFeeAmount_v06() { - setEppInput("domain_create_fee.xml", FEE_06_MAP); - 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 testFailure_wrongFeeAmount_v11() { - setEppInput("domain_create_fee.xml", FEE_11_MAP); - 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 testFailure_wrongFeeAmount_v12() { - setEppInput("domain_create_fee.xml", FEE_12_MAP); - 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_v06() 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", FEE_06_MAP); - 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", FEE_06_MAP)); - } - - @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", FEE_11_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", FEE_11_MAP)); - } - - @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", FEE_12_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", FEE_12_MAP)); - } - - @Test - void testFailure_omitFeeExtensionOnLogin_v06() { - for (String uri : FEE_EXTENSION_URIS) { - removeServiceExtensionUri(uri); - } - createTld("net"); - setEppInput("domain_create_fee.xml", FEE_06_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", FEE_11_MAP); - 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", FEE_12_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UndeclaredServiceExtensionException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testSuccess_eapFeeApplied_v06() throws Exception { - setEppInput( - "domain_create_eap_fee.xml", - new ImmutableMap.Builder() - .putAll(FEE_06_MAP) - .put("DESCRIPTION_1", "create") - .put("DESCRIPTION_2", "Early Access Period") - .build()); - persistContactsAndHosts(); - setEapForTld("tld"); - doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_06_MAP); - } - - @Test - void testSuccess_eapFeeApplied_v11() throws Exception { - setEppInput( - "domain_create_eap_fee.xml", - new ImmutableMap.Builder() - .putAll(FEE_11_MAP) - .put("DESCRIPTION_1", "create") - .put("DESCRIPTION_2", "Early Access Period") - .build()); - persistContactsAndHosts(); - setEapForTld("tld"); - doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_11_MAP); - } - - @Test - void testSuccess_eapFeeApplied_v12() throws Exception { - setEppInput( - "domain_create_eap_fee.xml", - new ImmutableMap.Builder() - .putAll(FEE_12_MAP) - .put("DESCRIPTION_1", "create") - .put("DESCRIPTION_2", "Early Access Period") - .build()); - persistContactsAndHosts(); - setEapForTld("tld"); - doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_12_MAP); - } - - @Test - void testFailure_feeGivenInWrongScale_v06() { - setEppInput("domain_create_fee_bad_scale.xml", FEE_06_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_feeGivenInWrongScale_v11() { - setEppInput("domain_create_fee_bad_scale.xml", FEE_11_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_feeGivenInWrongScale_v12() { - setEppInput("domain_create_fee_bad_scale.xml", FEE_12_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_appliedFee_v06() { - setEppInput("domain_create_fee_applied.xml", FEE_06_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_appliedFee_v11() { - setEppInput("domain_create_fee_applied.xml", FEE_11_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_appliedFee_v12() { - setEppInput("domain_create_fee_applied.xml", FEE_12_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @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", FEE_06_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_wrongFeeAmountTooLow_defaultToken_v11() 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", FEE_11_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @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", FEE_12_MAP); - 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", "fee-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", "fee-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", "fee-0.12", "CURRENCY", "EUR")); - persistContactsAndHosts(); - EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_gracePeriodFee_v06() { - setEppInput("domain_create_fee_grace_period.xml", FEE_06_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_gracePeriodFee_v11() { - setEppInput("domain_create_fee_grace_period.xml", FEE_11_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_gracePeriodFee_v12() { - setEppInput("domain_create_fee_grace_period.xml", FEE_12_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testSuccess_fee_withDefaultAttributes_v06() throws Exception { - setEppInput("domain_create_fee_defaults.xml", FEE_06_MAP); - persistContactsAndHosts(); - doSuccessfulTest( - "tld", - "domain_create_response_fee.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE", "24.00")); - } - - @Test - void testSuccess_fee_withDefaultAttributes_v11() throws Exception { - setEppInput("domain_create_fee_defaults.xml", FEE_11_MAP); - persistContactsAndHosts(); - doSuccessfulTest( - "tld", - "domain_create_response_fee.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE", "24.00")); - } - - @Test - void testSuccess_fee_withDefaultAttributes_v12() throws Exception { - setEppInput("domain_create_fee_defaults.xml", FEE_12_MAP); - persistContactsAndHosts(); - doSuccessfulTest( - "tld", - "domain_create_response_fee.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE", "24.00")); - } - - @Test - void testFailure_refundableFee_v06() { - setEppInput("domain_create_fee_refundable.xml", FEE_06_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_refundableFee_v11() { - setEppInput("domain_create_fee_refundable.xml", FEE_11_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_refundableFee_v12() { - setEppInput("domain_create_fee_refundable.xml", FEE_12_MAP); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testSuccess_fee_v06() throws Exception { - setEppInput("domain_create_fee.xml", FEE_06_MAP); - persistContactsAndHosts(); - doSuccessfulTest( - "tld", - "domain_create_response_fee.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE", "24.00")); - } - - @Test - void testSuccess_fee_v11() throws Exception { - setEppInput("domain_create_fee.xml", FEE_11_MAP); - persistContactsAndHosts(); - doSuccessfulTest( - "tld", - "domain_create_response_fee.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE", "24.00")); - } - - @Test - void testSuccess_fee_v12() throws Exception { - setEppInput("domain_create_fee.xml", FEE_12_MAP); - persistContactsAndHosts(); - doSuccessfulTest( - "tld", - "domain_create_response_fee.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE", "24.00")); - } - - @Test - void testFailure_eapFee_description_multipleMatch_v06() { - setEppInput( - "domain_create_eap_fee.xml", - ImmutableMap.of( - "FEE_VERSION", - "fee-0.6", - "DESCRIPTION_1", - "create", - "DESCRIPTION_2", - "renew transfer")); - persistContactsAndHosts(); - setEapForTld("tld"); - EppException thrown = assertThrows(FeeDescriptionMultipleMatchesException.class, this::runFlow); - assertThat(thrown).hasMessageThat().contains("RENEW, TRANSFER"); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_unknownCurrency_v12() { - setEppInput( - "domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "fee-0.12", "CURRENCY", "BAD")); - persistContactsAndHosts(); - EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::runFlow); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testTieredPricingPromoResponse_v12() throws Exception { - sessionMetadata.setRegistrarId("NewRegistrar"); - setupDefaultTokenWithDiscount("NewRegistrar"); - setEppInput("domain_create_fee.xml", FEE_12_MAP); - persistContactsAndHosts(); - - // Fee in the result should be 24 (create cost of 13 plus renew cost of 11) even though the - // actual cost is lower (due to the tiered pricing promo) - runFlowAssertResponse( - loadFile( - "domain_create_response_fee.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.12", "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 testSuccess_eapFee_multipleEAPfees_doNotAddToExpectedValue_v06() { - setEppInput( - "domain_create_extra_fees.xml", - new ImmutableMap.Builder() - .put("FEE_VERSION", "fee-0.6") - .put("DESCRIPTION_1", "create") - .put("FEE_1", "24") - .put("DESCRIPTION_2", "Early Access Period") - .put("FEE_2", "55") - .put("DESCRIPTION_3", "Early Access Period") - .put("FEE_3", "55") - .build()); - persistContactsAndHosts(); - setEapForTld("tld"); - EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); - assertThat(thrown).hasMessageThat().contains("expected fee of USD 100.00"); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testFailure_eapFee_description_swapped_v06() { - setEppInput( - "domain_create_eap_fee.xml", - ImmutableMap.of( - "FEE_VERSION", - "fee-0.6", - "DESCRIPTION_1", - "Early Access Period", - "DESCRIPTION_2", - "create")); - persistContactsAndHosts(); - setEapForTld("tld"); - EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); - assertThat(thrown).hasMessageThat().contains("CREATE"); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName_v12() throws Exception { - persistContactsAndHosts(); - createTld("example"); - persistResource( - setupDefaultTokenWithDiscount() - .asBuilder() - .setAllowedTlds(ImmutableSet.of("example")) - .build()); - setEppInput("domain_create_premium.xml", FEE_12_MAP); - runFlowAssertResponse( - loadFile( - "domain_create_response_premium.xml", - ImmutableMap.of( - "FEE_VERSION", "fee-0.12", "EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00"))); - assertSuccessfulCreate("example", ImmutableSet.of(), 200); - } - - @Test - void testSuccess_superuserOverridesPremiumNameBlock_v12() throws Exception { - createTld("example"); - setEppInput("domain_create_premium.xml", FEE_12_MAP); - persistContactsAndHosts("net"); - // Modify the Registrar to block premium names. - persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build()); - runFlowAssertResponse( - CommitMode.LIVE, - SUPERUSER, - loadFile( - "domain_create_response_premium.xml", - ImmutableMap.of( - "FEE_VERSION", "fee-0.12", "EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00"))); - assertSuccessfulCreate("example", ImmutableSet.of(), 200); - } - - @Test - void testFailure_eapFee_combined_v06() { - setEppInput("domain_create_eap_combined_fee.xml", FEE_06_MAP); - persistContactsAndHosts(); - setEapForTld("tld"); - EppException thrown = assertThrows(FeeDescriptionParseException.class, this::runFlow); - assertThat(thrown).hasMessageThat().contains("No fee description"); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testSuccess_nonpremiumCreateToken_v06() throws Exception { - createTld("example"); - persistContactsAndHosts(); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setRegistrationBehavior(RegistrationBehavior.NONPREMIUM_CREATE) - .setDomainName("rich.example") - .build()); - setEppInput( - "domain_create_premium_allocationtoken.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "1", "FEE", "13.00")); - runFlowAssertResponse(loadFile("domain_create_nonpremium_token_response.xml", FEE_06_MAP)); - } - - @Test - void testSuccess_eapFee_fullDescription_includingArbitraryExpiryTime_v06() throws Exception { - setEppInput( - "domain_create_eap_fee.xml", - ImmutableMap.of( - "FEE_VERSION", - "fee-0.6", - "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", FEE_06_MAP); - } - - @Test - void testSuccess_allocationToken_multiYearDiscount_worksForPremiums_v06() throws Exception { - createTld("example"); - persistContactsAndHosts(); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setDomainName("rich.example") - .setDiscountFraction(0.98) - .setDiscountYears(2) - .setDiscountPremiums(true) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().plusMillis(1), TokenStatus.VALID) - .put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED) - .build()) - .build()); - clock.advanceOneMilli(); - setEppInput( - "domain_create_premium_allocationtoken.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "3", "FEE", "104.00")); - runFlowAssertResponse( - loadFile( - "domain_create_response_premium.xml", - ImmutableMap.of( - "FEE_VERSION", "fee-0.6", "EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "104.00"))); - BillingEvent billingEvent = - Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class)); - assertThat(billingEvent.getTargetId()).isEqualTo("rich.example"); - // 1yr @ $100 + 2yrs @ $100 * (1 - 0.98) = $104 - assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 104.00)); - } - - @Test - void testSuccess_eapFee_multipleEAPfees_addToExpectedValue_v06() throws Exception { - setEppInput( - "domain_create_extra_fees.xml", - new ImmutableMap.Builder() - .put("FEE_VERSION", "fee-0.6") - .put("DESCRIPTION_1", "create") - .put("FEE_1", "24") - .put("DESCRIPTION_2", "Early Access Period") - .put("FEE_2", "55") - .put("DESCRIPTION_3", "Early Access Period") - .put("FEE_3", "45") - .build()); - persistContactsAndHosts(); - setEapForTld("tld"); - doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_06_MAP); - } - - @Test - void testFailure_eapFee_totalAmountNotMatched_v06() { - setEppInput( - "domain_create_extra_fees.xml", - new ImmutableMap.Builder() - .put("FEE_VERSION", "fee-0.6") - .put("DESCRIPTION_1", "create") - .put("FEE_1", "24") - .put("DESCRIPTION_2", "Early Access Period") - .put("FEE_2", "100") - .put("DESCRIPTION_3", "renew") - .put("FEE_3", "55") - .build()); - persistContactsAndHosts(); - setEapForTld("tld"); - EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); - assertThat(thrown).hasMessageThat().contains("expected total of USD 124.00"); - assertAboutEppExceptions().that(thrown).marshalsToXml(); - } - - @Test - void testSuccess_premiumAndEap_v06() throws Exception { - createTld("example"); - setEppInput("domain_create_premium_eap.xml", FEE_06_MAP); - persistContactsAndHosts("net"); - persistResource( - Tld.get("example") - .asBuilder() - .setEapFeeSchedule( - ImmutableSortedMap.of( - START_OF_TIME, - Money.of(USD, 0), - clock.nowUtc().minusDays(1), - Money.of(USD, 100), - clock.nowUtc().plusDays(1), - Money.of(USD, 0))) - .build()); - assertMutatingFlow(true); - runFlowAssertResponse( - CommitMode.LIVE, - UserPrivileges.NORMAL, - loadFile("domain_create_response_premium_eap.xml", FEE_06_MAP)); - assertSuccessfulCreate("example", ImmutableSet.of(), 200); - assertNoLordn(); - } - - @Test - void testFailure_premiumBlocked_v06() { - createTld("example"); - setEppInput("domain_create_premium.xml", FEE_06_MAP); - persistContactsAndHosts("net"); - // 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_allocationToken_singleYearDiscount_worksForPremiums_v06() throws Exception { - createTld("example"); - persistContactsAndHosts(); - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setDomainName("rich.example") - .setDiscountFraction(0.95555) - .setDiscountPremiums(true) - .setTokenStatusTransitions( - ImmutableSortedMap.naturalOrder() - .put(START_OF_TIME, TokenStatus.NOT_STARTED) - .put(clock.nowUtc().plusMillis(1), TokenStatus.VALID) - .put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED) - .build()) - .build()); - clock.advanceOneMilli(); - setEppInput( - "domain_create_premium_allocationtoken.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "3", "FEE", "204.44")); - runFlowAssertResponse( - loadFile( - "domain_create_response_premium.xml", - ImmutableMap.of( - "FEE_VERSION", "fee-0.6", "EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "204.44"))); - BillingEvent billingEvent = - Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class)); - assertThat(billingEvent.getTargetId()).isEqualTo("rich.example"); - // 2yrs @ $100 + 1yr @ $100 * (1 - 0.95555) = $204.44 - assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 204.44)); - } - - @Test - void testTieredPricingPromo_registrarIncluded_noTokenActive_v12() throws Exception { - sessionMetadata.setRegistrarId("NewRegistrar"); - persistActiveDomain("example1.tld"); - - persistResource( - setupDefaultTokenWithDiscount("NewRegistrar") - .asBuilder() - .setTokenStatusTransitions( - ImmutableSortedMap.of( - START_OF_TIME, - TokenStatus.NOT_STARTED, - clock.nowUtc().plusDays(1), - TokenStatus.VALID)) - .build()); - - setEppInput("domain_create_fee.xml", FEE_12_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", "fee-0.12", "FEE", "24.00"))); - assertThat(Iterables.getOnlyElement(loadAllOf(BillingEvent.class)).getCost()) - .isEqualTo(Money.of(USD, 24)); - } - - @Test - void testTieredPricingPromo_registrarNotIncluded_standardResponse_v12() throws Exception { - setupDefaultTokenWithDiscount("NewRegistrar"); - setEppInput("domain_create_fee.xml", FEE_12_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", "fee-0.12", "FEE", "24.00"))); - assertThat(Iterables.getOnlyElement(loadAllOf(BillingEvent.class)).getCost()) - .isEqualTo(Money.of(USD, 24)); - } - - @Test - void testSuccess_nonAnchorTenant_nonPremiumRenewal_v06() throws Exception { - createTld("example"); - AllocationToken token = - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setDomainName("rich.example") - .setRenewalPriceBehavior(NONPREMIUM) - .build()); - persistContactsAndHosts(); - // Creation is still $100 but it'll create a NONPREMIUM renewal - setEppInput( - "domain_create_premium_allocationtoken.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "2", "FEE", "111.00")); - runFlow(); - assertSuccessfulCreate("example", ImmutableSet.of(), token, 111); - } - - @Test - void testSuccess_specifiedRenewalPriceToken_specifiedRecurrencePrice_v06() throws Exception { - createTld("example"); - AllocationToken token = - persistResource( - new AllocationToken.Builder() - .setToken("abc123") - .setTokenType(SINGLE_USE) - .setDomainName("rich.example") - .setRenewalPriceBehavior(SPECIFIED) - .setRenewalPrice(Money.of(USD, 1)) - .build()); - persistContactsAndHosts(); - // Creation is still $100 but it'll create a $1 renewal - setEppInput( - "domain_create_premium_allocationtoken.xml", - ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "2", "FEE", "101.00")); - runFlow(); - assertSuccessfulCreate("example", ImmutableSet.of(), token, 101, 1); - } -} diff --git a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java index 2f0c50c34..77ba1c404 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java @@ -209,6 +209,14 @@ class DomainCreateFlowTest extends ResourceFlowTestCase FEE_06_MAP = + ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee", "CURRENCY", "USD", "FEE", "15.00"); + private static final ImmutableMap FEE_11_MAP = + ImmutableMap.of( + "FEE_VERSION", "fee-0.11", "FEE_NS", "fee", "CURRENCY", "USD", "FEE", "15.00"); + private static final ImmutableMap FEE_12_MAP = + ImmutableMap.of( + "FEE_VERSION", "fee-0.12", "FEE_NS", "fee", "CURRENCY", "USD", "FEE", "15.00"); private static final ImmutableMap FEE_STD_1_0_MAP = ImmutableMap.of( "FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00", "CURRENCY", "USD", "FEE", "15.00"); @@ -3305,6 +3313,791 @@ class DomainCreateFlowTest extends ResourceFlowTestCase() + .putAll(FEE_06_MAP) + .put("DESCRIPTION_1", "create") + .put("DESCRIPTION_2", "Early Access Period") + .build()); + persistContactsAndHosts(); + setEapForTld("tld"); + doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_06_MAP); + } + + @Test + void testSuccess_eapFeeApplied_v11() throws Exception { + setEppInput( + "domain_create_eap_fee.xml", + new ImmutableMap.Builder() + .putAll(FEE_11_MAP) + .put("DESCRIPTION_1", "create") + .put("DESCRIPTION_2", "Early Access Period") + .build()); + persistContactsAndHosts(); + setEapForTld("tld"); + doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_11_MAP); + } + + @Test + void testSuccess_eapFeeApplied_v12() throws Exception { + setEppInput( + "domain_create_eap_fee.xml", + new ImmutableMap.Builder() + .putAll(FEE_12_MAP) + .put("DESCRIPTION_1", "create") + .put("DESCRIPTION_2", "Early Access Period") + .build()); + persistContactsAndHosts(); + setEapForTld("tld"); + doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_12_MAP); + } + + @Test + void testFailure_feeGivenInWrongScale_v06() { + setEppInput("domain_create_fee_bad_scale.xml", FEE_06_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_feeGivenInWrongScale_v11() { + setEppInput("domain_create_fee_bad_scale.xml", FEE_11_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_feeGivenInWrongScale_v12() { + setEppInput("domain_create_fee_bad_scale.xml", FEE_12_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(CurrencyValueScaleException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_appliedFee_v06() { + setEppInput("domain_create_fee_applied.xml", FEE_06_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_appliedFee_v11() { + setEppInput("domain_create_fee_applied.xml", FEE_11_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_appliedFee_v12() { + setEppInput("domain_create_fee_applied.xml", FEE_12_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @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", FEE_06_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_wrongFeeAmountTooLow_defaultToken_v11() 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", FEE_11_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @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", FEE_12_MAP); + 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", "fee-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", "fee-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", "fee-0.12", "CURRENCY", "EUR")); + persistContactsAndHosts(); + EppException thrown = assertThrows(CurrencyUnitMismatchException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_gracePeriodFee_v06() { + setEppInput("domain_create_fee_grace_period.xml", FEE_06_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_gracePeriodFee_v11() { + setEppInput("domain_create_fee_grace_period.xml", FEE_11_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_gracePeriodFee_v12() { + setEppInput("domain_create_fee_grace_period.xml", FEE_12_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testSuccess_fee_withDefaultAttributes_v06() throws Exception { + setEppInput("domain_create_fee_defaults.xml", FEE_06_MAP); + persistContactsAndHosts(); + doSuccessfulTest( + "tld", + "domain_create_response_fee.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE", "24.00")); + } + + @Test + void testSuccess_fee_withDefaultAttributes_v11() throws Exception { + setEppInput("domain_create_fee_defaults.xml", FEE_11_MAP); + persistContactsAndHosts(); + doSuccessfulTest( + "tld", + "domain_create_response_fee.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE", "24.00")); + } + + @Test + void testSuccess_fee_withDefaultAttributes_v12() throws Exception { + setEppInput("domain_create_fee_defaults.xml", FEE_12_MAP); + persistContactsAndHosts(); + doSuccessfulTest( + "tld", + "domain_create_response_fee.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE", "24.00")); + } + + @Test + void testFailure_refundableFee_v06() { + setEppInput("domain_create_fee_refundable.xml", FEE_06_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_refundableFee_v11() { + setEppInput("domain_create_fee_refundable.xml", FEE_11_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_refundableFee_v12() { + setEppInput("domain_create_fee_refundable.xml", FEE_12_MAP); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnsupportedFeeAttributeException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testSuccess_fee_v06() throws Exception { + setEppInput("domain_create_fee.xml", FEE_06_MAP); + persistContactsAndHosts(); + doSuccessfulTest( + "tld", + "domain_create_response_fee.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE", "24.00")); + } + + @Test + void testSuccess_fee_v11() throws Exception { + setEppInput("domain_create_fee.xml", FEE_11_MAP); + persistContactsAndHosts(); + doSuccessfulTest( + "tld", + "domain_create_response_fee.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE", "24.00")); + } + + @Test + void testSuccess_fee_v12() throws Exception { + setEppInput("domain_create_fee.xml", FEE_12_MAP); + persistContactsAndHosts(); + doSuccessfulTest( + "tld", + "domain_create_response_fee.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE", "24.00")); + } + + @Test + void testFailure_eapFee_description_multipleMatch_v06() { + setEppInput( + "domain_create_eap_fee.xml", + ImmutableMap.of( + "FEE_VERSION", + "fee-0.6", + "DESCRIPTION_1", + "create", + "DESCRIPTION_2", + "renew transfer")); + persistContactsAndHosts(); + setEapForTld("tld"); + EppException thrown = assertThrows(FeeDescriptionMultipleMatchesException.class, this::runFlow); + assertThat(thrown).hasMessageThat().contains("RENEW, TRANSFER"); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_unknownCurrency_v12() { + setEppInput( + "domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "fee-0.12", "CURRENCY", "BAD")); + persistContactsAndHosts(); + EppException thrown = assertThrows(UnknownCurrencyEppException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testTieredPricingPromoResponse_v12() throws Exception { + sessionMetadata.setRegistrarId("NewRegistrar"); + setupDefaultTokenWithDiscount("NewRegistrar"); + setEppInput("domain_create_fee.xml", FEE_12_MAP); + persistContactsAndHosts(); + + // Fee in the result should be 24 (create cost of 13 plus renew cost of 11) even though the + // actual cost is lower (due to the tiered pricing promo) + runFlowAssertResponse( + loadFile( + "domain_create_response_fee.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.12", "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 testSuccess_eapFee_multipleEAPfees_doNotAddToExpectedValue_v06() { + setEppInput( + "domain_create_extra_fees.xml", + new ImmutableMap.Builder() + .put("FEE_VERSION", "fee-0.6") + .put("DESCRIPTION_1", "create") + .put("FEE_1", "24") + .put("DESCRIPTION_2", "Early Access Period") + .put("FEE_2", "55") + .put("DESCRIPTION_3", "Early Access Period") + .put("FEE_3", "55") + .build()); + persistContactsAndHosts(); + setEapForTld("tld"); + EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); + assertThat(thrown).hasMessageThat().contains("expected fee of USD 100.00"); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testFailure_eapFee_description_swapped_v06() { + setEppInput( + "domain_create_eap_fee.xml", + ImmutableMap.of( + "FEE_VERSION", + "fee-0.6", + "DESCRIPTION_1", + "Early Access Period", + "DESCRIPTION_2", + "create")); + persistContactsAndHosts(); + setEapForTld("tld"); + EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); + assertThat(thrown).hasMessageThat().contains("CREATE"); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testSuccess_doesNotApplyNonPremiumDefaultTokenToPremiumName_v12() throws Exception { + persistContactsAndHosts(); + createTld("example"); + persistResource( + setupDefaultTokenWithDiscount() + .asBuilder() + .setAllowedTlds(ImmutableSet.of("example")) + .build()); + setEppInput("domain_create_premium.xml", FEE_12_MAP); + runFlowAssertResponse( + loadFile( + "domain_create_response_premium.xml", + ImmutableMap.of( + "FEE_VERSION", "fee-0.12", "EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00"))); + assertSuccessfulCreate("example", ImmutableSet.of(), 200); + } + + @Test + void testSuccess_superuserOverridesPremiumNameBlock_v12() throws Exception { + createTld("example"); + setEppInput("domain_create_premium.xml", FEE_12_MAP); + persistContactsAndHosts("net"); + // Modify the Registrar to block premium names. + persistResource(loadRegistrar("TheRegistrar").asBuilder().setBlockPremiumNames(true).build()); + runFlowAssertResponse( + CommitMode.LIVE, + SUPERUSER, + loadFile( + "domain_create_response_premium.xml", + ImmutableMap.of( + "FEE_VERSION", "fee-0.12", "EXDATE", "2001-04-03T22:00:00.0Z", "FEE", "200.00"))); + assertSuccessfulCreate("example", ImmutableSet.of(), 200); + } + + @Test + void testFailure_eapFee_combined_v06() { + setEppInput("domain_create_eap_combined_fee.xml", FEE_06_MAP); + persistContactsAndHosts(); + setEapForTld("tld"); + EppException thrown = assertThrows(FeeDescriptionParseException.class, this::runFlow); + assertThat(thrown).hasMessageThat().contains("No fee description"); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testSuccess_nonpremiumCreateToken_v06() throws Exception { + createTld("example"); + persistContactsAndHosts(); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setRegistrationBehavior(RegistrationBehavior.NONPREMIUM_CREATE) + .setDomainName("rich.example") + .build()); + setEppInput( + "domain_create_premium_allocationtoken.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "1", "FEE", "13.00")); + runFlowAssertResponse(loadFile("domain_create_nonpremium_token_response.xml", FEE_06_MAP)); + } + + @Test + void testSuccess_eapFee_fullDescription_includingArbitraryExpiryTime_v06() throws Exception { + setEppInput( + "domain_create_eap_fee.xml", + ImmutableMap.of( + "FEE_VERSION", + "fee-0.6", + "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", FEE_06_MAP); + } + + @Test + void testSuccess_allocationToken_multiYearDiscount_worksForPremiums_v06() throws Exception { + createTld("example"); + persistContactsAndHosts(); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setDomainName("rich.example") + .setDiscountFraction(0.98) + .setDiscountYears(2) + .setDiscountPremiums(true) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().plusMillis(1), TokenStatus.VALID) + .put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED) + .build()) + .build()); + clock.advanceOneMilli(); + setEppInput( + "domain_create_premium_allocationtoken.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "3", "FEE", "104.00")); + runFlowAssertResponse( + loadFile( + "domain_create_response_premium.xml", + ImmutableMap.of( + "FEE_VERSION", "fee-0.6", "EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "104.00"))); + BillingEvent billingEvent = + Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class)); + assertThat(billingEvent.getTargetId()).isEqualTo("rich.example"); + // 1yr @ $100 + 2yrs @ $100 * (1 - 0.98) = $104 + assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 104.00)); + } + + @Test + void testSuccess_eapFee_multipleEAPfees_addToExpectedValue_v06() throws Exception { + setEppInput( + "domain_create_extra_fees.xml", + new ImmutableMap.Builder() + .put("FEE_VERSION", "fee-0.6") + .put("DESCRIPTION_1", "create") + .put("FEE_1", "24") + .put("DESCRIPTION_2", "Early Access Period") + .put("FEE_2", "55") + .put("DESCRIPTION_3", "Early Access Period") + .put("FEE_3", "45") + .build()); + persistContactsAndHosts(); + setEapForTld("tld"); + doSuccessfulTest("tld", "domain_create_response_eap_fee.xml", FEE_06_MAP); + } + + @Test + void testFailure_eapFee_totalAmountNotMatched_v06() { + setEppInput( + "domain_create_extra_fees.xml", + new ImmutableMap.Builder() + .put("FEE_VERSION", "fee-0.6") + .put("DESCRIPTION_1", "create") + .put("FEE_1", "24") + .put("DESCRIPTION_2", "Early Access Period") + .put("FEE_2", "100") + .put("DESCRIPTION_3", "renew") + .put("FEE_3", "55") + .build()); + persistContactsAndHosts(); + setEapForTld("tld"); + EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); + assertThat(thrown).hasMessageThat().contains("expected total of USD 124.00"); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + + @Test + void testSuccess_premiumAndEap_v06() throws Exception { + createTld("example"); + setEppInput("domain_create_premium_eap.xml", FEE_06_MAP); + persistContactsAndHosts("net"); + persistResource( + Tld.get("example") + .asBuilder() + .setEapFeeSchedule( + ImmutableSortedMap.of( + START_OF_TIME, + Money.of(USD, 0), + clock.nowUtc().minusDays(1), + Money.of(USD, 100), + clock.nowUtc().plusDays(1), + Money.of(USD, 0))) + .build()); + assertMutatingFlow(true); + runFlowAssertResponse( + CommitMode.LIVE, + UserPrivileges.NORMAL, + loadFile("domain_create_response_premium_eap.xml", FEE_06_MAP)); + assertSuccessfulCreate("example", ImmutableSet.of(), 200); + assertNoLordn(); + } + + @Test + void testFailure_premiumBlocked_v06() { + createTld("example"); + setEppInput("domain_create_premium.xml", FEE_06_MAP); + persistContactsAndHosts("net"); + // 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_allocationToken_singleYearDiscount_worksForPremiums_v06() throws Exception { + createTld("example"); + persistContactsAndHosts(); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setDomainName("rich.example") + .setDiscountFraction(0.95555) + .setDiscountPremiums(true) + .setTokenStatusTransitions( + ImmutableSortedMap.naturalOrder() + .put(START_OF_TIME, TokenStatus.NOT_STARTED) + .put(clock.nowUtc().plusMillis(1), TokenStatus.VALID) + .put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED) + .build()) + .build()); + clock.advanceOneMilli(); + setEppInput( + "domain_create_premium_allocationtoken.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "3", "FEE", "204.44")); + runFlowAssertResponse( + loadFile( + "domain_create_response_premium.xml", + ImmutableMap.of( + "FEE_VERSION", "fee-0.6", "EXDATE", "2002-04-03T22:00:00.0Z", "FEE", "204.44"))); + BillingEvent billingEvent = + Iterables.getOnlyElement(DatabaseHelper.loadAllOf(BillingEvent.class)); + assertThat(billingEvent.getTargetId()).isEqualTo("rich.example"); + // 2yrs @ $100 + 1yr @ $100 * (1 - 0.95555) = $204.44 + assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, 204.44)); + } + + @Test + void testTieredPricingPromo_registrarIncluded_noTokenActive_v12() throws Exception { + sessionMetadata.setRegistrarId("NewRegistrar"); + persistActiveDomain("example1.tld"); + + persistResource( + setupDefaultTokenWithDiscount("NewRegistrar") + .asBuilder() + .setTokenStatusTransitions( + ImmutableSortedMap.of( + START_OF_TIME, + TokenStatus.NOT_STARTED, + clock.nowUtc().plusDays(1), + TokenStatus.VALID)) + .build()); + + setEppInput("domain_create_fee.xml", FEE_12_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", "fee-0.12", "FEE", "24.00"))); + assertThat(Iterables.getOnlyElement(loadAllOf(BillingEvent.class)).getCost()) + .isEqualTo(Money.of(USD, 24)); + } + + @Test + void testTieredPricingPromo_registrarNotIncluded_standardResponse_v12() throws Exception { + setupDefaultTokenWithDiscount("NewRegistrar"); + setEppInput("domain_create_fee.xml", FEE_12_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", "fee-0.12", "FEE", "24.00"))); + assertThat(Iterables.getOnlyElement(loadAllOf(BillingEvent.class)).getCost()) + .isEqualTo(Money.of(USD, 24)); + } + + @Test + void testSuccess_nonAnchorTenant_nonPremiumRenewal_v06() throws Exception { + createTld("example"); + AllocationToken token = + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setDomainName("rich.example") + .setRenewalPriceBehavior(NONPREMIUM) + .build()); + persistContactsAndHosts(); + // Creation is still $100 but it'll create a NONPREMIUM renewal + setEppInput( + "domain_create_premium_allocationtoken.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "2", "FEE", "111.00")); + runFlow(); + assertSuccessfulCreate("example", ImmutableSet.of(), token, 111); + } + + @Test + void testSuccess_specifiedRenewalPriceToken_specifiedRecurrencePrice_v06() throws Exception { + createTld("example"); + AllocationToken token = + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(SINGLE_USE) + .setDomainName("rich.example") + .setRenewalPriceBehavior(SPECIFIED) + .setRenewalPrice(Money.of(USD, 1)) + .build()); + persistContactsAndHosts(); + // Creation is still $100 but it'll create a $1 renewal + setEppInput( + "domain_create_premium_allocationtoken.xml", + ImmutableMap.of("FEE_VERSION", "fee-0.6", "YEARS", "2", "FEE", "101.00")); + runFlow(); + assertSuccessfulCreate("example", ImmutableSet.of(), token, 101, 1); + } + private AllocationToken setupDefaultTokenWithDiscount() { return setupDefaultTokenWithDiscount("TheRegistrar"); } diff --git a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowOldFeeExtensionsTest.java b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowOldFeeExtensionsTest.java deleted file mode 100644 index 04b4dc900..000000000 --- a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowOldFeeExtensionsTest.java +++ /dev/null @@ -1,440 +0,0 @@ -// 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 { - - 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 FEE_06_MAP = - ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee"); - private static final ImmutableMap FEE_11_MAP = - ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11"); - private static final ImmutableMap 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 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 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); - } -} diff --git a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java index 8f83d520c..d8e767040 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainDeleteFlowTest.java @@ -86,6 +86,7 @@ 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; @@ -121,6 +122,12 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase FEE_06_MAP = + ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee"); + private static final ImmutableMap FEE_11_MAP = + ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11"); + private static final ImmutableMap FEE_12_MAP = + ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12"); private static final ImmutableMap FEE_STD_1_0_MAP = ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00"); @@ -1233,4 +1240,144 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase { - - /** - * The domain_info_fee.xml default substitutions common to most tests. - * - *

It doesn't set a default value to the COMMAND and PERIOD keys, because they are different in - * every test. - */ - private static final ImmutableMap 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 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(); - } -} diff --git a/core/src/test/java/google/registry/flows/domain/DomainInfoFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainInfoFlowTest.java index 2f6d2ed47..fc8103e5b 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainInfoFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainInfoFlowTest.java @@ -17,12 +17,15 @@ 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; @@ -38,9 +41,15 @@ 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; @@ -663,4 +672,353 @@ class DomainInfoFlowTest extends ResourceFlowTestCase { setEppInput("domain_info_bulk.xml"); doSuccessfulTest("domain_info_response_unauthorized.xml", false); } + + // The fee extension is no longer supported in domain:info commands as of version 1.0. + // For now, we still support old versions. + @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(); + } } diff --git a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowOldFeeExtensionsTest.java b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowOldFeeExtensionsTest.java deleted file mode 100644 index dedb43016..000000000 --- a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowOldFeeExtensionsTest.java +++ /dev/null @@ -1,827 +0,0 @@ -// 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 { - - private static final ImmutableMap 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 FEE_06_MAP = - updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.6", "FEE_NS", "fee"); - private static final ImmutableMap FEE_11_MAP = - updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.11", "FEE_NS", "fee11"); - private static final ImmutableMap 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 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 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 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 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 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 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 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)); - } -} diff --git a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java index 6e1e4dbe4..8b2f3a4e3 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java @@ -119,6 +119,12 @@ class DomainRenewFlowTest extends ResourceFlowTestCase "FEE", "55.00", "CURRENCY", "USD"); + private static final ImmutableMap FEE_06_MAP = + updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.6", "FEE_NS", "fee"); + private static final ImmutableMap FEE_11_MAP = + updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.11", "FEE_NS", "fee11"); + private static final ImmutableMap FEE_12_MAP = + updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "fee-0.12", "FEE_NS", "fee12"); private static final ImmutableMap FEE_STD_1_0_MAP = updateSubstitutions(FEE_BASE_MAP, "FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00"); @@ -1561,4 +1567,538 @@ class DomainRenewFlowTest extends ResourceFlowTestCase EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow); assertAboutEppExceptions().that(thrown).marshalsToXml(); } + + @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 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 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 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 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)); + } } diff --git a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowOldFeeExtensionsTest.java b/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowOldFeeExtensionsTest.java deleted file mode 100644 index 970067721..000000000 --- a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowOldFeeExtensionsTest.java +++ /dev/null @@ -1,351 +0,0 @@ -// 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 { - - private static final ImmutableMap FEE_06_MAP = - ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee", "CURRENCY", "USD"); - private static final ImmutableMap FEE_11_MAP = - ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11", "CURRENCY", "USD"); - private static final ImmutableMap 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 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 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; - } -} diff --git a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java index 8ba178d3d..69c28ee89 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java @@ -86,6 +86,12 @@ import org.junit.jupiter.api.Test; /** Unit tests for {@link DomainRestoreRequestFlow}. */ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase { + private static final ImmutableMap FEE_06_MAP = + ImmutableMap.of("FEE_VERSION", "fee-0.6", "FEE_NS", "fee", "CURRENCY", "USD"); + private static final ImmutableMap FEE_11_MAP = + ImmutableMap.of("FEE_VERSION", "fee-0.11", "FEE_NS", "fee11", "CURRENCY", "USD"); + private static final ImmutableMap FEE_12_MAP = + ImmutableMap.of("FEE_VERSION", "fee-0.12", "FEE_NS", "fee12", "CURRENCY", "USD"); private static final ImmutableMap FEE_STD_1_0_MAP = ImmutableMap.of("FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00", "CURRENCY", "USD"); @@ -677,4 +683,222 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase 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(); + } } diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowOldFeeExtensionsTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowOldFeeExtensionsTest.java deleted file mode 100644 index a2dfc7cfb..000000000 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowOldFeeExtensionsTest.java +++ /dev/null @@ -1,897 +0,0 @@ -// 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 { - - private static final ImmutableMap BASE_FEE_MAP = - new ImmutableMap.Builder() - .put("DOMAIN", "example.tld") - .put("YEARS", "1") - .put("AMOUNT", "11.00") - .put("CURRENCY", "USD") - .build(); - private static final ImmutableMap FEE_06_MAP = - new ImmutableMap.Builder() - .putAll(BASE_FEE_MAP) - .put("FEE_VERSION", "fee-0.6") - .put("FEE_NS", "fee") - .build(); - private static final ImmutableMap FEE_11_MAP = - new ImmutableMap.Builder() - .putAll(BASE_FEE_MAP) - .put("FEE_VERSION", "fee-0.11") - .put("FEE_NS", "fee11") - .build(); - private static final ImmutableMap FEE_12_MAP = - new ImmutableMap.Builder() - .putAll(BASE_FEE_MAP) - .put("FEE_VERSION", "fee-0.12") - .put("FEE_NS", "fee12") - .build(); - private static final ImmutableMap RICH_DOMAIN_MAP = - ImmutableMap.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() - .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() - .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 substitutions) { - Map 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 substitutions, - Optional transferCost, - BillingCancellation.Builder... extraExpectedBillingEvents) - throws Exception { - setEppInput(commandFilename, substitutions); - ImmutableSet 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 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 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 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 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 transferCost, - ImmutableSet 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 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 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 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 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()); - } -} diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java index 32f03c844..9aaf221d6 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java @@ -147,6 +147,24 @@ class DomainTransferRequestFlowTest .put("AMOUNT", "11.00") .put("CURRENCY", "USD") .build(); + private static final ImmutableMap FEE_06_MAP = + new ImmutableMap.Builder() + .putAll(BASE_FEE_MAP) + .put("FEE_VERSION", "fee-0.6") + .put("FEE_NS", "fee") + .build(); + private static final ImmutableMap FEE_11_MAP = + new ImmutableMap.Builder() + .putAll(BASE_FEE_MAP) + .put("FEE_VERSION", "fee-0.11") + .put("FEE_NS", "fee11") + .build(); + private static final ImmutableMap FEE_12_MAP = + new ImmutableMap.Builder() + .putAll(BASE_FEE_MAP) + .put("FEE_VERSION", "fee-0.12") + .put("FEE_NS", "fee12") + .build(); private static final ImmutableMap FEE_STD_1_0_MAP = updateSubstitutions(BASE_FEE_MAP, "FEE_VERSION", "epp:fee-1.0", "FEE_NS", "fee1_00"); private static final ImmutableMap RICH_DOMAIN_MAP = @@ -1826,4 +1844,250 @@ class DomainTransferRequestFlowTest assertThrows(AlreadyRedeemedAllocationTokenException.class, this::runFlow); assertAboutEppExceptions().that(thrown).marshalsToXml(); } + + @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() + .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() + .putAll(FEE_12_MAP) + .put("PERIOD", "0") + .put("AUTOMATIC_TRANSFER_LENGTH", "5") + .build())); + } } diff --git a/core/src/test/java/google/registry/flows/domain/ProductionSimulatingFeeExtensionsTest.java b/core/src/test/java/google/registry/flows/domain/ProductionSimulatingFeeExtensionsTest.java index 96d8d7f62..baed836a5 100644 --- a/core/src/test/java/google/registry/flows/domain/ProductionSimulatingFeeExtensionsTest.java +++ b/core/src/test/java/google/registry/flows/domain/ProductionSimulatingFeeExtensionsTest.java @@ -14,43 +14,62 @@ 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 static com.google.common.truth.Truth.assertThat; + import google.registry.model.eppcommon.ProtocolDefinition; import google.registry.util.RegistryEnvironment; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -/** - * Abstract class for tests that require old versions of the fee extension (0.6, 0.11, 0.12). - * - *

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 - extends ResourceFlowTestCase { +/** Class for testing the XML extension definitions loaded in the prod environment. */ +public class ProductionSimulatingFeeExtensionsTest { private RegistryEnvironment previousEnvironment; @BeforeEach void beforeEach() { previousEnvironment = RegistryEnvironment.get(); - RegistryEnvironment.PRODUCTION.setup(); - reloadServiceExtensionUris(); } @AfterEach void afterEach() { previousEnvironment.setup(); - reloadServiceExtensionUris(); + ProtocolDefinition.reloadServiceExtensionUris(); } - void reloadServiceExtensionUris() { + @Test + void testNonProdEnvironments() { + for (RegistryEnvironment env : RegistryEnvironment.values()) { + if (env.equals(RegistryEnvironment.PRODUCTION)) { + continue; + } + env.setup(); + ProtocolDefinition.reloadServiceExtensionUris(); + assertThat(ProtocolDefinition.getVisibleServiceExtensionUris()) + .containsExactly( + "urn:ietf:params:xml:ns:launch-1.0", + "urn:ietf:params:xml:ns:rgp-1.0", + "urn:ietf:params:xml:ns:secDNS-1.1", + "urn:ietf:params:xml:ns:fee-0.6", + "urn:ietf:params:xml:ns:fee-0.11", + "urn:ietf:params:xml:ns:fee-0.12", + "urn:ietf:params:xml:ns:epp:fee-1.0"); + } + } + + @Test + void testProdEnvironment() { + RegistryEnvironment.PRODUCTION.setup(); ProtocolDefinition.reloadServiceExtensionUris(); - sessionMetadata.setServiceExtensionUris(ProtocolDefinition.getVisibleServiceExtensionUris()); - EppXmlTransformer.reloadTransformers(); + // prod shouldn't have the fee extension version 1.0 + assertThat(ProtocolDefinition.getVisibleServiceExtensionUris()) + .containsExactly( + "urn:ietf:params:xml:ns:launch-1.0", + "urn:ietf:params:xml:ns:rgp-1.0", + "urn:ietf:params:xml:ns:secDNS-1.1", + "urn:ietf:params:xml:ns:fee-0.6", + "urn:ietf:params:xml:ns:fee-0.11", + "urn:ietf:params:xml:ns:fee-0.12"); } } diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_premium_v06_transfer_only.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_premium_stdv1_transfer_only.xml similarity index 62% rename from core/src/test/resources/google/registry/flows/domain/domain_check_fee_premium_v06_transfer_only.xml rename to core/src/test/resources/google/registry/flows/domain/domain_check_fee_premium_stdv1_transfer_only.xml index 9d560d42d..343c410f9 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_premium_v06_transfer_only.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_premium_stdv1_transfer_only.xml @@ -6,11 +6,8 @@ - - - rich.example - transfer - + + ABC-12345 diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1.xml new file mode 100644 index 000000000..1563d0e4a --- /dev/null +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1.xml @@ -0,0 +1,60 @@ + + + + Command completed successfully + + + + + rich.example + In use + + + + + + USD + + rich.example + premium + + 1 + 100.00 + + + + rich.example + + 1 + %RENEWPRICE% + + + + rich.example + + 1 + %RENEWPRICE% + + + + rich.example + + 1 + 17.00 + + + + rich.example + + 1 + 0.00 + + + + + + ABC-12345 + server-trid + + + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06_renew_only.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1_renew_only.xml similarity index 62% rename from core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06_renew_only.xml rename to core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1_renew_only.xml index 827249604..6a3323e68 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06_renew_only.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1_renew_only.xml @@ -12,13 +12,14 @@ - - - rich.example - USD - renew - 1 - %RENEWPRICE% + + USD + + rich.example + + 1 + %RENEWPRICE% + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06_transfer_only.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1_transfer_only.xml similarity index 62% rename from core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06_transfer_only.xml rename to core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1_transfer_only.xml index c1255db77..9249a9575 100644 --- a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06_transfer_only.xml +++ b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_stdv1_transfer_only.xml @@ -12,13 +12,14 @@ - - - rich.example - USD - transfer - 1 - %RENEWPRICE% + + USD + + rich.example + + 1 + %RENEWPRICE% + diff --git a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06.xml b/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06.xml deleted file mode 100644 index f52b0b616..000000000 --- a/core/src/test/resources/google/registry/flows/domain/domain_check_fee_response_domain_exists_v06.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - Command completed successfully - - - - - rich.example - In use - - - - - - - rich.example - USD - create - 1 - 100.00 - premium - - - rich.example - USD - renew - 1 - %RENEWPRICE% - - - rich.example - USD - transfer - 1 - %RENEWPRICE% - - - rich.example - USD - restore - 1 - 17.00 - - - - - ABC-12345 - server-trid - - - diff --git a/core/src/test/resources/google/registry/flows/greeting.xml b/core/src/test/resources/google/registry/flows/greeting.xml index a23cf6b6e..f039bfca7 100644 --- a/core/src/test/resources/google/registry/flows/greeting.xml +++ b/core/src/test/resources/google/registry/flows/greeting.xml @@ -12,6 +12,9 @@ urn:ietf:params:xml:ns:launch-1.0 urn:ietf:params:xml:ns:rgp-1.0 urn:ietf:params:xml:ns:secDNS-1.1 + urn:ietf:params:xml:ns:fee-0.6 + urn:ietf:params:xml:ns:fee-0.11 + urn:ietf:params:xml:ns:fee-0.12 urn:ietf:params:xml:ns:epp:fee-1.0 diff --git a/core/src/test/resources/google/registry/flows/session/greeting.xml b/core/src/test/resources/google/registry/flows/session/greeting.xml index a23cf6b6e..f039bfca7 100644 --- a/core/src/test/resources/google/registry/flows/session/greeting.xml +++ b/core/src/test/resources/google/registry/flows/session/greeting.xml @@ -12,6 +12,9 @@ urn:ietf:params:xml:ns:launch-1.0 urn:ietf:params:xml:ns:rgp-1.0 urn:ietf:params:xml:ns:secDNS-1.1 + urn:ietf:params:xml:ns:fee-0.6 + urn:ietf:params:xml:ns:fee-0.11 + urn:ietf:params:xml:ns:fee-0.12 urn:ietf:params:xml:ns:epp:fee-1.0