diff --git a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java index 1b9da10d5..e845ade31 100644 --- a/java/google/registry/flows/domain/DomainApplicationCreateFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationCreateFlow.java @@ -234,7 +234,7 @@ public final class DomainApplicationCreateFlow implements TransactionalFlow { } Optional feeCreate = eppInput.getSingleExtension(FeeCreateCommandExtension.class); - validateFeeChallenge(targetId, tld, now, feeCreate, feesAndCredits); + validateFeeChallenge(targetId, tld, clientId, now, feeCreate, feesAndCredits); Optional secDnsCreate = validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); flowCustomLogic.afterValidation( diff --git a/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java b/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java index a321b7cd4..5a0a1d933 100644 --- a/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainApplicationUpdateFlow.java @@ -30,7 +30,7 @@ import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReference import static google.registry.flows.domain.DomainFlowUtils.updateDsData; import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes; import static google.registry.flows.domain.DomainFlowUtils.validateDsData; -import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge; +import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnDomain; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld; @@ -57,7 +57,6 @@ import google.registry.flows.FlowModule.Superuser; import google.registry.flows.FlowModule.TargetId; import google.registry.flows.TransactionalFlow; import google.registry.flows.annotations.ReportingSpec; -import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeOperationException; import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainApplication; import google.registry.model.domain.DomainCommand.Update; @@ -191,15 +190,7 @@ public class DomainApplicationUpdateFlow implements TransactionalFlow { pricingLogic.getApplicationUpdatePrice(registry, existingApplication, now); Optional feeUpdate = eppInput.getSingleExtension(FeeUpdateCommandExtension.class); - // If the fee extension is present, validate it (even if the cost is zero, to check for price - // mismatches). Don't rely on the the validateFeeChallenge check for feeUpdate nullness, because - // it throws an error if the name is premium, and we don't want to do that here. - if (feeUpdate.isPresent()) { - validateFeeChallenge(targetId, tld, now, feeUpdate, feesAndCredits); - } else if (!feesAndCredits.getTotalCost().isZero()) { - // If it's not present but the cost is not zero, throw an exception. - throw new FeesRequiredForNonFreeOperationException(feesAndCredits.getTotalCost()); - } + validateFeesAckedIfPresent(feeUpdate, feesAndCredits); verifyNotInPendingDelete( add.getContacts(), command.getInnerChange().getRegistrant(), diff --git a/java/google/registry/flows/domain/DomainCreateFlow.java b/java/google/registry/flows/domain/DomainCreateFlow.java index 7ddd3af4c..d2114a7af 100644 --- a/java/google/registry/flows/domain/DomainCreateFlow.java +++ b/java/google/registry/flows/domain/DomainCreateFlow.java @@ -271,7 +271,7 @@ public class DomainCreateFlow implements TransactionalFlow { Optional feeCreate = eppInput.getSingleExtension(FeeCreateCommandExtension.class); FeesAndCredits feesAndCredits = pricingLogic.getCreatePrice(registry, targetId, now, years); - validateFeeChallenge(targetId, registry.getTldStr(), now, feeCreate, feesAndCredits); + validateFeeChallenge(targetId, registry.getTldStr(), clientId, now, feeCreate, feesAndCredits); Optional secDnsCreate = validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class)); String repoId = createDomainRepoId(ObjectifyService.allocateId(), registry.getTldStr()); diff --git a/java/google/registry/flows/domain/DomainFlowUtils.java b/java/google/registry/flows/domain/DomainFlowUtils.java index 6a0113f27..7f3339975 100644 --- a/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/java/google/registry/flows/domain/DomainFlowUtils.java @@ -613,20 +613,44 @@ public class DomainFlowUtils { } } + /** + * Validates that fees are acked and match if they are required (typically for premium domains). + * + *

This is used by domain operations that have an implicit cost, e.g. domain create or renew + * (both of which add one or more years' worth of registration). Depending on registry and/or + * registrar settings, explicit price acking using the fee extension may be required for premium + * domain names. + */ public static void validateFeeChallenge( String domainName, String tld, + String clientId, DateTime priceTime, final Optional feeCommand, FeesAndCredits feesAndCredits) throws EppException { + Registry registry = Registry.get(tld); - if (registry.getPremiumPriceAckRequired() - && isDomainPremium(domainName, priceTime) - && !feeCommand.isPresent()) { + Registrar registrar = Registrar.loadByClientIdCached(clientId).get(); + boolean premiumAckRequired = + registry.getPremiumPriceAckRequired() || registrar.getPremiumPriceAckRequired(); + if (premiumAckRequired && isDomainPremium(domainName, priceTime) && !feeCommand.isPresent()) { throw new FeesRequiredForPremiumNameException(); } + validateFeesAckedIfPresent(feeCommand, feesAndCredits); + } + /** + * Validates that non-zero fees are acked (i.e. they are specified and the amount matches). + * + *

This is used directly by update operations, i.e. those that otherwise don't have implicit + * costs, and is also used as a helper method to validate if fees are required for operations that + * do have implicit costs, e.g. creates and renews. + */ + public static void validateFeesAckedIfPresent( + final Optional feeCommand, + FeesAndCredits feesAndCredits) + throws EppException { // Check for the case where a fee command extension was required but not provided. // This only happens when the total fees are non-zero and include custom fees requiring the // extension. @@ -657,7 +681,7 @@ public class DomainFlowUtils { total = total.add(credit.getCost()); } - Money feeTotal = null; + Money feeTotal; try { feeTotal = Money.of(feeCommand.get().getCurrency(), total); } catch (ArithmeticException e) { diff --git a/java/google/registry/flows/domain/DomainRenewFlow.java b/java/google/registry/flows/domain/DomainRenewFlow.java index 322d100aa..e4bce5c94 100644 --- a/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/java/google/registry/flows/domain/DomainRenewFlow.java @@ -146,7 +146,8 @@ public final class DomainRenewFlow implements TransactionalFlow { eppInput.getSingleExtension(FeeRenewCommandExtension.class); FeesAndCredits feesAndCredits = pricingLogic.getRenewPrice(Registry.get(existingDomain.getTld()), targetId, now, years); - validateFeeChallenge(targetId, existingDomain.getTld(), now, feeRenew, feesAndCredits); + validateFeeChallenge( + targetId, existingDomain.getTld(), clientId, now, feeRenew, feesAndCredits); flowCustomLogic.afterValidation( AfterValidationParameters.newBuilder() .setExistingDomain(existingDomain) diff --git a/java/google/registry/flows/domain/DomainRestoreRequestFlow.java b/java/google/registry/flows/domain/DomainRestoreRequestFlow.java index 4b240ee6d..a516127ce 100644 --- a/java/google/registry/flows/domain/DomainRestoreRequestFlow.java +++ b/java/google/registry/flows/domain/DomainRestoreRequestFlow.java @@ -204,7 +204,8 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow { if (!existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.REDEMPTION)) { throw new DomainNotEligibleForRestoreException(); } - validateFeeChallenge(targetId, existingDomain.getTld(), now, feeUpdate, feesAndCredits); + validateFeeChallenge( + targetId, existingDomain.getTld(), clientId, now, feeUpdate, feesAndCredits); } private ImmutableSet createRestoreAndRenewBillingEvents( diff --git a/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/java/google/registry/flows/domain/DomainTransferRequestFlow.java index 0293f825f..3d85389a8 100644 --- a/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -162,7 +162,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow { ? Optional.empty() : Optional.of(pricingLogic.getTransferPrice(registry, targetId, now)); if (feesAndCredits.isPresent()) { - validateFeeChallenge(targetId, tld, now, feeTransfer, feesAndCredits.get()); + validateFeeChallenge(targetId, tld, gainingClientId, now, feeTransfer, feesAndCredits.get()); } HistoryEntry historyEntry = buildHistoryEntry(existingDomain, registry, now, period); DateTime automaticTransferTime = diff --git a/java/google/registry/flows/domain/DomainUpdateFlow.java b/java/google/registry/flows/domain/DomainUpdateFlow.java index 5c5c5f587..70feded16 100644 --- a/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -31,7 +31,7 @@ import static google.registry.flows.domain.DomainFlowUtils.updateDsData; import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes; import static google.registry.flows.domain.DomainFlowUtils.validateDomainAllowedOnCreateRestrictedTld; import static google.registry.flows.domain.DomainFlowUtils.validateDsData; -import static google.registry.flows.domain.DomainFlowUtils.validateFeeChallenge; +import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnDomain; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld; @@ -59,7 +59,6 @@ import google.registry.flows.custom.DomainUpdateFlowCustomLogic; import google.registry.flows.custom.DomainUpdateFlowCustomLogic.AfterValidationParameters; import google.registry.flows.custom.DomainUpdateFlowCustomLogic.BeforeSaveParameters; import google.registry.flows.custom.EntityChanges; -import google.registry.flows.domain.DomainFlowUtils.FeesRequiredForNonFreeOperationException; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingEvent; import google.registry.model.billing.BillingEvent.Reason; @@ -223,16 +222,8 @@ public final class DomainUpdateFlow implements TransactionalFlow { Registry registry = Registry.get(tld); Optional feeUpdate = eppInput.getSingleExtension(FeeUpdateCommandExtension.class); - // If the fee extension is present, validate it (even if the cost is zero, to check for price - // mismatches). Don't rely on the the validateFeeChallenge check for feeUpdate nullness, because - // it throws an error if the name is premium, and we don't want to do that here. FeesAndCredits feesAndCredits = pricingLogic.getUpdatePrice(registry, targetId, now); - if (feeUpdate.isPresent()) { - validateFeeChallenge(targetId, existingDomain.getTld(), now, feeUpdate, feesAndCredits); - } else if (!feesAndCredits.getTotalCost().isZero()) { - // If it's not present but the cost is not zero, throw an exception. - throw new FeesRequiredForNonFreeOperationException(feesAndCredits.getTotalCost()); - } + validateFeesAckedIfPresent(feeUpdate, feesAndCredits); verifyNotInPendingDelete( add.getContacts(), command.getInnerChange().getRegistrant(), diff --git a/java/google/registry/model/registrar/Registrar.java b/java/google/registry/model/registrar/Registrar.java index 2f06b4bcd..279ab2c18 100644 --- a/java/google/registry/model/registrar/Registrar.java +++ b/java/google/registry/model/registrar/Registrar.java @@ -412,6 +412,13 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable */ BillingMethod billingMethod; + /** Whether the registrar must acknowledge the price to register non-standard-priced domains. */ + boolean premiumPriceAckRequired; + + public boolean getPremiumPriceAckRequired() { + return premiumPriceAckRequired; + } + @NonFinalForTesting private static Supplier saltSupplier = () -> { @@ -864,6 +871,11 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable return this; } + public Builder setPremiumPriceAckRequired(boolean premiumPriceAckRequired) { + getInstance().premiumPriceAckRequired = premiumPriceAckRequired; + return this; + } + /** Build the registrar, nullifying empty fields. */ @Override public Registrar build() { diff --git a/java/google/registry/model/registry/Registry.java b/java/google/registry/model/registry/Registry.java index 4d7214538..efac520ae 100644 --- a/java/google/registry/model/registry/Registry.java +++ b/java/google/registry/model/registry/Registry.java @@ -318,7 +318,7 @@ public class Registry extends ImmutableObject implements Buildable { /** Whether the pull queue that writes to authoritative DNS is paused for this TLD. */ boolean dnsPaused = DEFAULT_DNS_PAUSED; - /** Whether the price must be acknowledged to register premiun names on this TLD. */ + /** Whether the price must be acknowledged to register premium names on this TLD. */ boolean premiumPriceAckRequired = true; /** diff --git a/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java b/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java index 23da14fe4..074074470 100644 --- a/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java +++ b/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java @@ -259,6 +259,13 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand { description = "Hostname of registrar WHOIS server. (Default: whois.nic.google)") String whoisServer; + @Nullable + @Parameter( + names = "--premium_price_ack_required", + description = "Whether operations on premium domains require explicit ack of prices", + arity = 1) + private Boolean premiumPriceAckRequired; + /** Returns the existing registrar (for update) or null (for creates). */ @Nullable abstract Registrar getOldRegistrar(String clientId); @@ -389,21 +396,12 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand { .setCountryCode(countryCode) .build()); } - if (blockPremiumNames != null) { - builder.setBlockPremiumNames(blockPremiumNames); - } - if (contactsRequireSyncing != null) { - builder.setContactsRequireSyncing(contactsRequireSyncing); - } - if (phonePasscode != null) { - builder.setPhonePasscode(phonePasscode); - } - if (icannReferralEmail != null) { - builder.setIcannReferralEmail(icannReferralEmail); - } - if (whoisServer != null) { - builder.setWhoisServer(whoisServer); - } + Optional.ofNullable(blockPremiumNames).ifPresent(builder::setBlockPremiumNames); + Optional.ofNullable(contactsRequireSyncing).ifPresent(builder::setContactsRequireSyncing); + Optional.ofNullable(phonePasscode).ifPresent(builder::setPhonePasscode); + Optional.ofNullable(icannReferralEmail).ifPresent(builder::setIcannReferralEmail); + Optional.ofNullable(whoisServer).ifPresent(builder::setWhoisServer); + Optional.ofNullable(premiumPriceAckRequired).ifPresent(builder::setPremiumPriceAckRequired); // If the registrarName is being set, verify that it is either null or it normalizes uniquely. String oldRegistrarName = (oldRegistrar == null) ? null : oldRegistrar.getRegistrarName(); diff --git a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java index d79b6c925..5c060b967 100644 --- a/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainApplicationCreateFlowTest.java @@ -433,7 +433,8 @@ public class DomainApplicationCreateFlowTest } @Test - public void testFailure_landrushFeeNotProvidedOnPremiumName() throws Exception { + public void testFailure_landrushFeeNotProvidedOnPremiumName_whenRegistryRequiresFeeAcking() + throws Exception { createTld("example", TldState.SUNRUSH); setEppInput("domain_create_landrush_premium.xml"); persistContactsAndHosts(); @@ -442,6 +443,20 @@ public class DomainApplicationCreateFlowTest assertAboutEppExceptions().that(thrown).marshalsToXml(); } + @Test + public void testFailure_landrushFeeNotProvidedOnPremiumName_whenRegistrarRequiresFeeAcking() + throws Exception { + createTld("example", TldState.SUNRUSH); + persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build()); + persistResource( + loadRegistrar("TheRegistrar").asBuilder().setPremiumPriceAckRequired(true).build()); + setEppInput("domain_create_landrush_premium.xml"); + persistContactsAndHosts(); + clock.advanceOneMilli(); + EppException thrown = expectThrows(FeesRequiredForPremiumNameException.class, this::runFlow); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + @Test public void testSuccess_landrushWithClaimsInSunrush() throws Exception { createTld("tld", TldState.SUNRUSH); diff --git a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java index 7b90451e9..616515692 100644 --- a/javatests/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainCreateFlowTest.java @@ -1453,8 +1453,33 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase substitutions, - Optional transferCost, - BillingEvent.Cancellation.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. - Registry registry = Registry.get(domain.getTld()); - DateTime implicitTransferTime = clock.nowUtc().plus(registry.getAutomaticTransferLength()); - // Setup done; run the test. - assertTransactionalFlow(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); - final HistoryEntry historyEntryTransferRequest = - getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST); - assertAboutHistoryEntries() - .that(historyEntryTransferRequest) - .hasPeriodYears(1) - .and() - .hasOtherClientId("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)); - } - /** Implements the missing Optional.stream function that is added in Java 9. */ private static Stream optionalToStream(Optional optional) { return optional.map(Stream::of).orElseGet(Stream::empty); @@ -484,6 +426,64 @@ public class DomainTransferRequestFlowTest assertThat(afterGracePeriod.getGracePeriods()).isEmpty(); } + /** + * 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, + BillingEvent.Cancellation.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. + Registry registry = Registry.get(domain.getTld()); + DateTime implicitTransferTime = clock.nowUtc().plus(registry.getAutomaticTransferLength()); + // Setup done; run the test. + assertTransactionalFlow(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); + final HistoryEntry historyEntryTransferRequest = + getOnlyHistoryEntryOfType(domain, DOMAIN_TRANSFER_REQUEST); + assertAboutHistoryEntries() + .that(historyEntryTransferRequest) + .hasPeriodYears(1) + .and() + .hasOtherClientId("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)); + } + private void doSuccessfulTest( String commandFilename, String expectedXmlFilename, @@ -1202,7 +1202,7 @@ public class DomainTransferRequestFlowTest } @Test - public void testFailure_feeNotProvidedOnPremiumName() throws Exception { + public void testFailure_registryRequiresAcking_feeNotProvidedOnPremiumName() throws Exception { setupDomain("rich", "example"); EppException thrown = expectThrows( @@ -1211,6 +1211,20 @@ public class DomainTransferRequestFlowTest assertAboutEppExceptions().that(thrown).marshalsToXml(); } + @Test + public void testFailure_registrarRequiresAcking_feeNotProvidedOnPremiumName() throws Exception { + setupDomain("rich", "example"); + persistResource(Registry.get("example").asBuilder().setPremiumPriceAckRequired(false).build()); + persistResource( + loadRegistrar("NewRegistrar").asBuilder().setPremiumPriceAckRequired(true).build()); + clock.advanceOneMilli(); + EppException thrown = + expectThrows( + FeesRequiredForPremiumNameException.class, + () -> doFailingTest("domain_transfer_request_premium.xml")); + assertAboutEppExceptions().that(thrown).marshalsToXml(); + } + @Test public void testFailure_noAuthInfo() throws Exception { setupDomain("example", "tld"); diff --git a/javatests/google/registry/model/registrar/RegistrarTest.java b/javatests/google/registry/model/registrar/RegistrarTest.java index 4be0a3ad7..b203adf38 100644 --- a/javatests/google/registry/model/registrar/RegistrarTest.java +++ b/javatests/google/registry/model/registrar/RegistrarTest.java @@ -97,6 +97,7 @@ public class RegistrarTest extends EntityTestCase { .setBillingAccountMap( ImmutableMap.of(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz")) .setPhonePasscode("01234") + .setPremiumPriceAckRequired(true) .build()); persistResource(registrar); abuseAdminContact = diff --git a/javatests/google/registry/model/schema.txt b/javatests/google/registry/model/schema.txt index 65785d797..8a137f73a 100644 --- a/javatests/google/registry/model/schema.txt +++ b/javatests/google/registry/model/schema.txt @@ -587,6 +587,7 @@ class google.registry.model.registrar.Registrar { @Parent com.googlecode.objectify.Key parent; boolean blockPremiumNames; boolean contactsRequireSyncing; + boolean premiumPriceAckRequired; google.registry.model.CreateAutoTimestamp creationTime; google.registry.model.UpdateAutoTimestamp lastUpdateTime; google.registry.model.registrar.Registrar$BillingMethod billingMethod; diff --git a/javatests/google/registry/tools/CreateRegistrarCommandTest.java b/javatests/google/registry/tools/CreateRegistrarCommandTest.java index 0c8f2a986..f3b7d54ba 100644 --- a/javatests/google/registry/tools/CreateRegistrarCommandTest.java +++ b/javatests/google/registry/tools/CreateRegistrarCommandTest.java @@ -91,6 +91,7 @@ public class CreateRegistrarCommandTest extends CommandTestCase registrar = Registrar.loadByClientId("clientz"); + assertThat(registrar).isPresent(); + assertThat(registrar.get().getPremiumPriceAckRequired()).isTrue(); + } + @Test public void testFailure_missingRegistrarType() throws Exception { IllegalArgumentException thrown = diff --git a/javatests/google/registry/tools/UpdateRegistrarCommandTest.java b/javatests/google/registry/tools/UpdateRegistrarCommandTest.java index 1e81255c7..7eccae86d 100644 --- a/javatests/google/registry/tools/UpdateRegistrarCommandTest.java +++ b/javatests/google/registry/tools/UpdateRegistrarCommandTest.java @@ -314,31 +314,55 @@ public class UpdateRegistrarCommandTest extends CommandTestCase