diff --git a/java/google/registry/flows/domain/DomainCheckFlow.java b/java/google/registry/flows/domain/DomainCheckFlow.java index fb909ebd6..1aa3b1ff0 100644 --- a/java/google/registry/flows/domain/DomainCheckFlow.java +++ b/java/google/registry/flows/domain/DomainCheckFlow.java @@ -44,6 +44,7 @@ import google.registry.flows.annotations.ReportingSpec; import google.registry.flows.custom.DomainCheckFlowCustomLogic; import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseParameters; import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData; +import google.registry.flows.domain.token.AllocationTokenDomainCheckResults; import google.registry.flows.domain.token.AllocationTokenFlowUtils; import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainCommand.Check; @@ -51,6 +52,7 @@ import google.registry.model.domain.fee.FeeCheckCommandExtension; import google.registry.model.domain.fee.FeeCheckCommandExtensionItem; import google.registry.model.domain.fee.FeeCheckResponseExtensionItem; import google.registry.model.domain.launch.LaunchCheckExtension; +import google.registry.model.domain.token.AllocationToken; import google.registry.model.domain.token.AllocationTokenExtension; import google.registry.model.eppinput.EppInput; import google.registry.model.eppinput.ResourceCommand; @@ -150,27 +152,38 @@ public final class DomainCheckFlow implements Flow { Set existingIds = checkResourcesExist(DomainBase.class, targetIds, now); Optional allocationTokenExtension = eppInput.getSingleExtension(AllocationTokenExtension.class); - ImmutableMap tokenCheckResults = - allocationTokenExtension.isPresent() - ? allocationTokenFlowUtils.checkDomainsWithToken( - ImmutableList.copyOf(domainNames.values()), - allocationTokenExtension.get().getAllocationToken(), - clientId, - now) - : ImmutableMap.of(); + Optional tokenDomainCheckResults = + allocationTokenExtension.map( + tokenExtension -> + allocationTokenFlowUtils.checkDomainsWithToken( + ImmutableList.copyOf(domainNames.values()), + tokenExtension.getAllocationToken(), + clientId, + now)); + ImmutableList.Builder checks = new ImmutableList.Builder<>(); ImmutableMap tldStates = Maps.toMap(seenTlds, tld -> Registry.get(tld).getTldState(now)); + ImmutableMap domainCheckResults = + tokenDomainCheckResults + .map(AllocationTokenDomainCheckResults::domainCheckResults) + .orElse(ImmutableMap.of()); for (String targetId : targetIds) { Optional message = - getMessageForCheck(domainNames.get(targetId), existingIds, tokenCheckResults, tldStates); + getMessageForCheck( + domainNames.get(targetId), + existingIds, + domainCheckResults, + tldStates); checks.add(DomainCheck.create(!message.isPresent(), targetId, message.orElse(null))); } + Optional allocationToken = + tokenDomainCheckResults.flatMap(AllocationTokenDomainCheckResults::token); BeforeResponseReturnData responseData = flowCustomLogic.beforeResponse( BeforeResponseParameters.newBuilder() .setDomainChecks(checks.build()) - .setResponseExtensions(getResponseExtensions(domainNames, now)) + .setResponseExtensions(getResponseExtensions(domainNames, now, allocationToken)) .setAsOfDate(now) .build()); return responseBuilder @@ -199,11 +212,14 @@ public final class DomainCheckFlow implements Flow { /** Handle the fee check extension. */ private ImmutableList getResponseExtensions( - ImmutableMap domainNames, DateTime now) throws EppException { + ImmutableMap domainNames, + DateTime now, + Optional allocationToken) + throws EppException { Optional feeCheckOpt = eppInput.getSingleExtension(FeeCheckCommandExtension.class); if (!feeCheckOpt.isPresent()) { - return ImmutableList.of(); // No fee checks were requested. + return ImmutableList.of(); // No fee checks were requested. } FeeCheckCommandExtension feeCheck = feeCheckOpt.get(); ImmutableList.Builder responseItems = @@ -217,7 +233,8 @@ public final class DomainCheckFlow implements Flow { domainNames.get(domainName), feeCheck.getCurrency(), now, - pricingLogic); + pricingLogic, + allocationToken); responseItems.add(builder.setDomainNameIfSupported(domainName).build()); } } diff --git a/java/google/registry/flows/domain/DomainCreateFlow.java b/java/google/registry/flows/domain/DomainCreateFlow.java index 638b0c245..8ccb92527 100644 --- a/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/java/google/registry/flows/domain/DomainCreateFlow.java @@ -277,7 +277,8 @@ public class DomainCreateFlow implements TransactionalFlow { Optional feeCreate = eppInput.getSingleExtension(FeeCreateCommandExtension.class); FeesAndCredits feesAndCredits = - pricingLogic.getCreatePrice(registry, targetId, now, years, isAnchorTenant); + pricingLogic.getCreatePrice( + registry, targetId, now, years, isAnchorTenant, allocationToken); validateFeeChallenge(targetId, now, feeCreate, feesAndCredits); Optional secDnsCreate = validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); @@ -444,7 +445,7 @@ public class DomainCreateFlow implements TransactionalFlow { eppInput.getSingleExtension(AllocationTokenExtension.class); return Optional.ofNullable( extension.isPresent() - ? allocationTokenFlowUtils.verifyToken( + ? allocationTokenFlowUtils.loadAndVerifyToken( command, extension.get().getAllocationToken(), registry, clientId, now) : null); } diff --git a/java/google/registry/flows/domain/DomainFlowUtils.java b/java/google/registry/flows/domain/DomainFlowUtils.java index bf3424dda..b6b85bced 100644 --- a/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/java/google/registry/flows/domain/DomainFlowUtils.java @@ -550,7 +550,8 @@ public class DomainFlowUtils { InternetDomainName domain, @Nullable CurrencyUnit topLevelCurrency, DateTime currentDate, - DomainPricingLogic pricingLogic) + DomainPricingLogic pricingLogic, + Optional allocationToken) throws EppException { DateTime now = currentDate; // Use the custom effective date specified in the fee check request, if there is one. @@ -588,10 +589,10 @@ public class DomainFlowUtils { builder.setReasonIfSupported("reserved"); } else { builder.setAvailIfSupported(true); - // TODO(b/117145844): Once allocation token support for domain check flow is implemented, - // we should be able to calculate the correct price here. fees = - pricingLogic.getCreatePrice(registry, domainNameString, now, years, false).getFees(); + pricingLogic + .getCreatePrice(registry, domainNameString, now, years, false, allocationToken) + .getFees(); } break; case RENEW: diff --git a/java/google/registry/flows/domain/DomainInfoFlow.java b/java/google/registry/flows/domain/DomainInfoFlow.java index ad7431071..3e20f9717 100644 --- a/java/google/registry/flows/domain/DomainInfoFlow.java +++ b/java/google/registry/flows/domain/DomainInfoFlow.java @@ -163,7 +163,8 @@ public final class DomainInfoFlow implements Flow { InternetDomainName.from(targetId), null, now, - pricingLogic); + pricingLogic, + Optional.empty()); extensions.add(builder.build()); } return extensions.build(); diff --git a/java/google/registry/flows/domain/DomainPricingLogic.java b/java/google/registry/flows/domain/DomainPricingLogic.java index 5e81e04a1..d20f2608f 100644 --- a/java/google/registry/flows/domain/DomainPricingLogic.java +++ b/java/google/registry/flows/domain/DomainPricingLogic.java @@ -14,7 +14,7 @@ package google.registry.flows.domain; -import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost; +import static com.google.common.base.Preconditions.checkArgument; import static google.registry.pricing.PricingEngineProxy.getDomainFeeClass; import static google.registry.pricing.PricingEngineProxy.getDomainRenewCost; @@ -30,8 +30,12 @@ import google.registry.flows.custom.DomainPricingCustomLogic.UpdatePriceParamete import google.registry.model.domain.fee.BaseFee; import google.registry.model.domain.fee.BaseFee.FeeType; import google.registry.model.domain.fee.Fee; +import google.registry.model.domain.token.AllocationToken; +import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; import google.registry.model.registry.Registry; +import google.registry.pricing.PricingEngineProxy; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Optional; import javax.inject.Inject; import org.joda.money.CurrencyUnit; @@ -51,16 +55,27 @@ public final class DomainPricingLogic { @Inject DomainPricingLogic() {} - /** Returns a new create price for the pricer. */ + /** + * Returns a new create price for the pricer. + * + *

If {@code allocationToken} is present and the domain is non-premium, that discount will be + * applied to the first year. + */ public FeesAndCredits getCreatePrice( - Registry registry, String domainName, DateTime date, int years, boolean isAnchorTenant) + Registry registry, + String domainName, + DateTime date, + int years, + boolean isAnchorTenant, + Optional allocationToken) throws EppException { CurrencyUnit currency = registry.getCurrency(); - - // Get the vanilla create cost, or 0 for anchor tenants. - BigDecimal domainCreateCost = - isAnchorTenant ? BigDecimal.ZERO : getDomainCreateCost(domainName, date, years).getAmount(); - BaseFee createFeeOrCredit = Fee.create(domainCreateCost, FeeType.CREATE); + // Domain create cost is always zero for anchor tenants + Money domainCreateCost = + isAnchorTenant + ? Money.of(currency, BigDecimal.ZERO) + : getDomainCreateCostWithDiscount(domainName, date, years, allocationToken); + BaseFee createFeeOrCredit = Fee.create(domainCreateCost.getAmount(), FeeType.CREATE); // Create fees for the cost and the EAP fee, if any. Fee eapFee = registry.getEapFeeFor(date); @@ -80,7 +95,6 @@ public final class DomainPricingLogic { .setAsOfDate(date) .setYears(years) .build()); - } /** Returns a new renew price for the pricer. */ @@ -154,7 +168,7 @@ public final class DomainPricingLogic { .setFeesAndCredits( new FeesAndCredits.Builder() .setCurrency(currency) - .addFeeOrCredit(feeOrCredit) + .setFeesAndCredits(feeOrCredit) .build()) .setRegistry(registry) .setDomainName(InternetDomainName.from(domainName)) @@ -166,4 +180,24 @@ public final class DomainPricingLogic { public Optional getFeeClass(String domainName, DateTime date) { return getDomainFeeClass(domainName, date); } + + private Money getDomainCreateCostWithDiscount( + String domainName, DateTime date, int years, Optional allocationToken) { + DomainPrices domainPrices = PricingEngineProxy.getPricesForDomainName(domainName, date); + checkArgument( + !allocationToken.isPresent() + || allocationToken.get().getDiscountFraction() == 0.0 + || !domainPrices.isPremium(), + "A nonzero discount code cannot be applied to premium domains"); + Money oneYearCreateCost = domainPrices.getCreateCost(); + Money totalDomainCreateCost = oneYearCreateCost.multipliedBy(years); + // If a discount is applicable, apply it only to the first year + if (allocationToken.isPresent()) { + Money discount = + oneYearCreateCost.multipliedBy( + allocationToken.get().getDiscountFraction(), RoundingMode.HALF_UP); + totalDomainCreateCost = totalDomainCreateCost.minus(discount); + } + return totalDomainCreateCost; + } } diff --git a/java/google/registry/flows/domain/token/AllocationTokenCustomLogic.java b/java/google/registry/flows/domain/token/AllocationTokenCustomLogic.java index 20cb18ac2..10f36f792 100644 --- a/java/google/registry/flows/domain/token/AllocationTokenCustomLogic.java +++ b/java/google/registry/flows/domain/token/AllocationTokenCustomLogic.java @@ -14,12 +14,14 @@ package google.registry.flows.domain.token; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.net.InternetDomainName; import google.registry.flows.EppException; import google.registry.model.domain.DomainCommand; import google.registry.model.domain.token.AllocationToken; import google.registry.model.registry.Registry; +import java.util.function.Function; import org.joda.time.DateTime; /** @@ -43,11 +45,12 @@ public class AllocationTokenCustomLogic { /** Performs additional custom logic for performing domain checks using a token. */ public ImmutableMap checkDomainsWithToken( - ImmutableMap checkResults, + ImmutableList domainNames, AllocationToken token, String clientId, DateTime now) { // Do nothing. - return checkResults; + return domainNames.stream() + .collect(ImmutableMap.toImmutableMap(Function.identity(), ignored -> "")); } } diff --git a/java/google/registry/flows/domain/token/AllocationTokenDomainCheckResults.java b/java/google/registry/flows/domain/token/AllocationTokenDomainCheckResults.java new file mode 100644 index 000000000..9b695d16b --- /dev/null +++ b/java/google/registry/flows/domain/token/AllocationTokenDomainCheckResults.java @@ -0,0 +1,36 @@ +// Copyright 2019 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.token; + +import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableMap; +import com.google.common.net.InternetDomainName; +import google.registry.model.domain.token.AllocationToken; +import java.util.Optional; + +/** Value class to represent the result of loading a token and checking domains with it. */ +@AutoValue +public abstract class AllocationTokenDomainCheckResults { + + public abstract Optional token(); + + public abstract ImmutableMap domainCheckResults(); + + public static AllocationTokenDomainCheckResults create( + Optional allocationToken, + ImmutableMap domainCheckResults) { + return new AutoValue_AllocationTokenDomainCheckResults(allocationToken, domainCheckResults); + } +} diff --git a/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java b/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java index 75cf84d62..c85c8906c 100644 --- a/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java +++ b/java/google/registry/flows/domain/token/AllocationTokenFlowUtils.java @@ -17,7 +17,9 @@ package google.registry.flows.domain.token; import static com.google.common.base.Preconditions.checkArgument; import static google.registry.model.ofy.ObjectifyService.ofy; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import com.google.common.net.InternetDomainName; import com.googlecode.objectify.Key; import google.registry.flows.EppException; @@ -29,7 +31,7 @@ import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; import java.util.List; -import java.util.function.Function; +import java.util.Optional; import javax.inject.Inject; import org.joda.time.DateTime; @@ -49,16 +51,10 @@ public class AllocationTokenFlowUtils { * @return the loaded {@link AllocationToken} for that string. * @throws InvalidAllocationTokenException if the token doesn't exist. */ - public AllocationToken verifyToken( + public AllocationToken loadAndVerifyToken( DomainCommand.Create command, String token, Registry registry, String clientId, DateTime now) throws EppException { - AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now(); - if (tokenEntity == null) { - throw new InvalidAllocationTokenException(); - } - if (tokenEntity.isRedeemed()) { - throw new AlreadyRedeemedAllocationTokenException(); - } + AllocationToken tokenEntity = loadToken(token); return tokenCustomLogic.verifyToken(command, tokenEntity, registry, clientId, now); } @@ -69,26 +65,21 @@ public class AllocationTokenFlowUtils { * for a a given domain then it does not validate with this allocation token; domains that do * validate have blank messages (i.e. no error). */ - public ImmutableMap checkDomainsWithToken( + public AllocationTokenDomainCheckResults checkDomainsWithToken( List domainNames, String token, String clientId, DateTime now) { - AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now(); - String globalResult; - if (tokenEntity == null) { - globalResult = new InvalidAllocationTokenException().getMessage(); - } else if (tokenEntity.isRedeemed()) { - globalResult = AlreadyRedeemedAllocationTokenException.ERROR_MSG_SHORT; - } else { - globalResult = ""; + try { + AllocationToken tokenEntity = loadToken(token); + // Only call custom logic if there wasn't a global allocation token error that applies to all + // check results. The custom logic can only add errors, not override existing errors. + return AllocationTokenDomainCheckResults.create( + Optional.of(tokenEntity), + tokenCustomLogic.checkDomainsWithToken( + ImmutableList.copyOf(domainNames), tokenEntity, clientId, now)); + } catch (EppException e) { + return AllocationTokenDomainCheckResults.create( + Optional.empty(), + ImmutableMap.copyOf(Maps.toMap(domainNames, ignored -> e.getMessage()))); } - ImmutableMap checkResults = - domainNames - .stream() - .collect(ImmutableMap.toImmutableMap(Function.identity(), domainName -> globalResult)); - // Only call custom logic if there wasn't a global allocation token error that applies to all - // check results. The custom logic can only add errors, not override existing errors. - return globalResult.isEmpty() - ? tokenCustomLogic.checkDomainsWithToken(checkResults, tokenEntity, clientId, now) - : checkResults; } /** @@ -102,17 +93,22 @@ public class AllocationTokenFlowUtils { return token.asBuilder().setRedemptionHistoryEntry(redemptionHistoryEntry).build(); } + private AllocationToken loadToken(String token) throws EppException { + AllocationToken tokenEntity = ofy().load().key(Key.create(AllocationToken.class, token)).now(); + if (tokenEntity == null) { + throw new InvalidAllocationTokenException(); + } + if (tokenEntity.isRedeemed()) { + throw new AlreadyRedeemedAllocationTokenException(); + } + return tokenEntity; + } + /** The allocation token was already redeemed. */ public static class AlreadyRedeemedAllocationTokenException extends AssociationProhibitsOperationException { - - public static final String ERROR_MSG_LONG = "The allocation token was already redeemed"; - - /** A short error message fitting within 32 characters for use in domain check responses. */ - public static final String ERROR_MSG_SHORT = "Alloc token was already redeemed"; - public AlreadyRedeemedAllocationTokenException() { - super(ERROR_MSG_LONG); + super("Alloc token was already redeemed"); } } diff --git a/javatests/google/registry/flows/domain/DomainCheckFlowTest.java b/javatests/google/registry/flows/domain/DomainCheckFlowTest.java index 112d902c3..28e46ffcc 100644 --- a/javatests/google/registry/flows/domain/DomainCheckFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainCheckFlowTest.java @@ -15,6 +15,7 @@ package google.registry.flows.domain; 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.registry.Registry.TldState.PREDELEGATION; import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE; @@ -63,6 +64,7 @@ import google.registry.flows.domain.DomainFlowUtils.UnknownFeeCommandException; import google.registry.flows.exceptions.TooManyResourceChecksException; import google.registry.model.domain.DomainBase; import google.registry.model.domain.token.AllocationToken; +import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.registry.Registry; import google.registry.model.registry.Registry.TldState; import google.registry.model.registry.label.ReservedList; @@ -182,6 +184,24 @@ public class DomainCheckFlowTest create(false, "premiumcollision.tld", "Cannot be delegated")); } + @Test + public void testSuccess_allocationTokenPromotion() throws Exception { + 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().plusMillis(1), TokenStatus.VALID) + .put(clock.nowUtc().plusSeconds(1), TokenStatus.ENDED) + .build()) + .build()); + setEppInput("domain_check_allocationtoken_fee.xml"); + runFlowAssertResponse(loadFile("domain_check_allocationtoken_fee_response.xml")); + } + @Test public void testSuccess_oneReservedInSunrise() throws Exception { createTld("tld", START_DATE_SUNRISE); diff --git a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java index 6eae42145..1c529d909 100644 --- a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java @@ -65,6 +65,7 @@ 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.googlecode.objectify.Key; import google.registry.config.RegistryConfig; import google.registry.flows.EppException; @@ -140,6 +141,8 @@ import google.registry.model.domain.launch.LaunchNotice; import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.domain.token.AllocationToken; +import google.registry.model.domain.token.AllocationToken.TokenStatus; +import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; @@ -151,6 +154,7 @@ import google.registry.model.reporting.DomainTransactionRecord.TransactionReport import google.registry.model.reporting.HistoryEntry; import google.registry.monitoring.whitebox.EppMetric; import google.registry.testing.TaskQueueHelper.TaskMatcher; +import java.math.BigDecimal; import java.util.Map; import javax.annotation.Nullable; import org.joda.money.Money; @@ -1101,6 +1105,57 @@ public class DomainCreateFlowTest extends ResourceFlowTestCasenaturalOrder() + .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_allocationtoken.xml", ImmutableMap.of("DOMAIN", "example.tld")); + runFlowAssertResponse( + loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld"))); + BillingEvent.OneTime billingEvent = + Iterables.getOnlyElement(ofy().load().type(BillingEvent.OneTime.class)); + assertThat(billingEvent.getTargetId()).isEqualTo("example.tld"); + assertThat(billingEvent.getCost()).isEqualTo(Money.of(USD, BigDecimal.valueOf(19.5))); + } + + @Test + public void testSuccess_promotionDoesNotApplyToPremiumPrice() { + // At the moment, discounts cannot apply to premium domains + createTld("example"); + persistContactsAndHosts(); + persistResource( + new AllocationToken.Builder() + .setToken("abc123") + .setTokenType(TokenType.UNLIMITED_USE) + .setDiscountFraction(0.5) + .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"); + assertThat(assertThrows(IllegalArgumentException.class, this::runFlow)) + .hasMessageThat() + .isEqualTo("A nonzero discount code cannot be applied to premium domains"); + } + @Test public void testSuccess_superuserReserved() throws Exception { setEppInput("domain_create_reserved.xml"); diff --git a/javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee.xml b/javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee.xml new file mode 100644 index 000000000..f1c6b5e49 --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee.xml @@ -0,0 +1,41 @@ + + + + + example1.tld + example2.tld + reserved.tld + + + + + abc123 + + + + example1.tld + USD + create + 1 + + + example2.tld + USD + create + 1 + + + reserved.tld + USD + create + 1 + + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee_response.xml b/javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee_response.xml new file mode 100644 index 000000000..c1ac63e0f --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_check_allocationtoken_fee_response.xml @@ -0,0 +1,51 @@ + + + + + Command completed successfully + + + + + example1.tld + + + example2.tld + + + reserved.tld + Reserved + + + + + + + example2.tld + USD + create + 1 + 6.50 + + + example1.tld + USD + create + 1 + 6.50 + + + reserved.tld + USD + create + 1 + reserved + + + + + ABC-12345 + server-trid + + + diff --git a/javatests/google/registry/flows/domain/testdata/domain_create_premium_allocationtoken.xml b/javatests/google/registry/flows/domain/testdata/domain_create_premium_allocationtoken.xml new file mode 100644 index 000000000..80ccae97e --- /dev/null +++ b/javatests/google/registry/flows/domain/testdata/domain_create_premium_allocationtoken.xml @@ -0,0 +1,33 @@ + + + + + rich.example + 2 + + ns1.example.net + ns2.example.net + + jd1234 + sh8013 + sh8013 + + 2fooBAR + + + + + + abc123 + + + USD + 193.5 + + + ABC-12345 + + diff --git a/javatests/google/registry/flows/domain/token/AllocationTokenFlowUtilsTest.java b/javatests/google/registry/flows/domain/token/AllocationTokenFlowUtilsTest.java index 8edeec846..3ea6eb107 100644 --- a/javatests/google/registry/flows/domain/token/AllocationTokenFlowUtilsTest.java +++ b/javatests/google/registry/flows/domain/token/AllocationTokenFlowUtilsTest.java @@ -36,7 +36,7 @@ import google.registry.model.registry.Registry; import google.registry.model.reporting.HistoryEntry; import google.registry.testing.AppEngineRule; import google.registry.testing.ShardableTestCase; -import java.util.Map; +import java.util.function.Function; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Rule; @@ -63,7 +63,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { AllocationTokenFlowUtils flowUtils = new AllocationTokenFlowUtils(new AllocationTokenCustomLogic()); assertThat( - flowUtils.verifyToken( + flowUtils.loadAndVerifyToken( createCommand("blah.tld"), "tokeN", Registry.get("tld"), @@ -80,7 +80,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { assertThrows( InvalidAllocationTokenException.class, () -> - flowUtils.verifyToken( + flowUtils.loadAndVerifyToken( createCommand("blah.tld"), "tokeN", Registry.get("tld"), @@ -99,7 +99,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { assertThrows( IllegalStateException.class, () -> - flowUtils.verifyToken( + flowUtils.loadAndVerifyToken( createCommand("blah.tld"), "tokeN", Registry.get("tld"), @@ -115,12 +115,14 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { AllocationTokenFlowUtils flowUtils = new AllocationTokenFlowUtils(new AllocationTokenCustomLogic()); assertThat( - flowUtils.checkDomainsWithToken( - ImmutableList.of( - InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")), - "tokeN", - "TheRegistrar", - DateTime.now(UTC))) + flowUtils + .checkDomainsWithToken( + ImmutableList.of( + InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")), + "tokeN", + "TheRegistrar", + DateTime.now(UTC)) + .domainCheckResults()) .containsExactlyEntriesIn( ImmutableMap.of( InternetDomainName.from("blah.tld"), "", InternetDomainName.from("blah2.tld"), "")) @@ -138,12 +140,14 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { AllocationTokenFlowUtils flowUtils = new AllocationTokenFlowUtils(new AllocationTokenCustomLogic()); assertThat( - flowUtils.checkDomainsWithToken( - ImmutableList.of( - InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")), - "tokeN", - "TheRegistrar", - DateTime.now(UTC))) + flowUtils + .checkDomainsWithToken( + ImmutableList.of( + InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")), + "tokeN", + "TheRegistrar", + DateTime.now(UTC)) + .domainCheckResults()) .containsExactlyEntriesIn( ImmutableMap.of( InternetDomainName.from("blah.tld"), @@ -179,12 +183,14 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { AllocationTokenFlowUtils flowUtils = new AllocationTokenFlowUtils(new CustomResultAllocationTokenCustomLogic()); assertThat( - flowUtils.checkDomainsWithToken( - ImmutableList.of( - InternetDomainName.from("blah.tld"), InternetDomainName.from("bunny.tld")), - "tokeN", - "TheRegistrar", - DateTime.now(UTC))) + flowUtils + .checkDomainsWithToken( + ImmutableList.of( + InternetDomainName.from("blah.tld"), InternetDomainName.from("bunny.tld")), + "tokeN", + "TheRegistrar", + DateTime.now(UTC)) + .domainCheckResults()) .containsExactlyEntriesIn( ImmutableMap.of( InternetDomainName.from("blah.tld"), @@ -215,7 +221,7 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { @Override public ImmutableMap checkDomainsWithToken( - ImmutableMap checkResults, + ImmutableList domainNames, AllocationToken tokenEntity, String clientId, DateTime now) { @@ -228,18 +234,15 @@ public class AllocationTokenFlowUtilsTest extends ShardableTestCase { @Override public ImmutableMap checkDomainsWithToken( - ImmutableMap checkResults, + ImmutableList domainNames, AllocationToken tokenEntity, String clientId, DateTime now) { - return checkResults - .entrySet() - .stream() + return domainNames.stream() .collect( ImmutableMap.toImmutableMap( - Map.Entry::getKey, - entry -> - entry.getKey().toString().contains("bunny") ? "fufu" : entry.getValue())); + Function.identity(), + domainName -> domainName.toString().contains("bunny") ? "fufu" : "")); } } }