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