1
0
mirror of https://github.com/google/nomulus synced 2026-07-18 22:12:24 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
sarahcaseybot 4f1ca920a7 Use the createBillingCostTransitions map to get the create cost for a domain (#2390)
* Use the createBillingCostTransitions map to get the create cost for a domain

* Add comment

* Add some TODOs

* use streams to check currency unit
2024-04-05 21:27:55 +00:00
Weimin Yu 96e33f5b4f Check for missing BSA unblockable domains (#2394)
* Check for missing BSA unblockable domains

All unblockable domains created before the last refresh run should be
reported as unblockable (registered).

All reserved domains that are not registered should be reported as
unblockable (reserved). Note that transient errors may be reported for
newly added reserved domains since we do not maintain update time for
when a reserved label is associated with a TLD. However, this scenario
is extremely rare in operations.

* Addressing review
2024-04-03 00:44:05 +00:00
27 changed files with 453 additions and 166 deletions
@@ -17,12 +17,20 @@ package google.registry.bsa;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Throwables.getStackTraceAsString;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.bsa.BsaTransactions.bsaQuery;
import static google.registry.bsa.ReservedDomainsUtils.getAllReservedNames;
import static google.registry.bsa.ReservedDomainsUtils.isReservedDomain;
import static google.registry.bsa.persistence.Queries.batchReadBsaLabelText;
import static google.registry.bsa.persistence.Queries.queryMissedRegisteredUnblockables;
import static google.registry.bsa.persistence.Queries.queryUnblockableDomainByLabels;
import static google.registry.model.tld.Tld.isEnrolledWithBsa;
import static google.registry.model.tld.Tlds.getTldEntitiesOfType;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.POST;
import static google.registry.util.BatchedStreams.toBatches;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.common.base.Joiner;
@@ -38,9 +46,12 @@ import google.registry.bsa.api.UnblockableDomain;
import google.registry.bsa.api.UnblockableDomain.Reason;
import google.registry.bsa.persistence.DownloadScheduler;
import google.registry.bsa.persistence.Queries;
import google.registry.bsa.persistence.Queries.DomainLifeSpan;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.ForeignKeyUtils;
import google.registry.model.domain.Domain;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldType;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Response;
@@ -124,7 +135,8 @@ public class BsaValidateAction implements Runnable {
ImmutableList.Builder<String> errorsBuilder = new ImmutableList.Builder<>();
errorsBuilder.addAll(checkBsaLabels(downloadJobName.get()));
errorsBuilder.addAll(checkUnblockableDomains());
errorsBuilder.addAll(checkWronglyReportedUnblockableDomains());
errorsBuilder.addAll(checkMissingUnblockableDomains());
ImmutableList<String> errors = errorsBuilder.build();
@@ -173,7 +185,7 @@ public class BsaValidateAction implements Runnable {
return errors.build();
}
ImmutableList<String> checkUnblockableDomains() {
ImmutableList<String> checkWronglyReportedUnblockableDomains() {
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
Optional<UnblockableDomain> lastRead = Optional.empty();
ImmutableList<UnblockableDomain> batch;
@@ -263,6 +275,77 @@ public class BsaValidateAction implements Runnable {
return labelsBuilder.build();
}
ImmutableList<String> checkMissingUnblockableDomains() {
DateTime now = clock.nowUtc();
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
errors.addAll(checkForMissingReservedUnblockables(now));
errors.addAll(checkForMissingRegisteredUnblockables(now));
return errors.build();
}
ImmutableList<String> checkForMissingRegisteredUnblockables(DateTime now) {
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
ImmutableList<Tld> bsaEnabledTlds =
getTldEntitiesOfType(TldType.REAL).stream()
.filter(tld -> isEnrolledWithBsa(tld, now))
.collect(toImmutableList());
DateTime stalenessThreshold = now.minus(maxStaleness);
bsaEnabledTlds.stream()
.map(Tld::getTldStr)
.map(tld -> bsaQuery(() -> queryMissedRegisteredUnblockables(tld, now)))
.flatMap(ImmutableList::stream)
.filter(domainLifeSpan -> domainLifeSpan.creationTime().isBefore(stalenessThreshold))
.map(DomainLifeSpan::domainName)
.forEach(
domainName ->
errors.add(
String.format(
"Registered domain %s missing or not recorded as REGISTERED", domainName)));
return errors.build();
}
ImmutableList<String> checkForMissingReservedUnblockables(DateTime now) {
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
try (Stream<ImmutableList<String>> reservedNames =
toBatches(getAllReservedNames(now), transactionBatchSize)) {
reservedNames
.map(this::checkOneBatchReservedDomainsForMissingUnblockables)
.forEach(errors::addAll);
}
return errors.build();
}
ImmutableList<String> checkOneBatchReservedDomainsForMissingUnblockables(
ImmutableList<String> batch) {
ImmutableSet<String> labels =
batch.stream()
.map(InternetDomainName::from)
.map(d -> d.parts().get(0))
.collect(toImmutableSet());
ImmutableMap<String, UnblockableDomain> persistedUnblockables =
bsaQuery(
() ->
queryUnblockableDomainByLabels(labels)
.collect(toImmutableMap(UnblockableDomain::domainName, x -> x)));
ImmutableList.Builder<String> errors = new ImmutableList.Builder<>();
ImmutableSet<UnblockableDomain.Reason> acceptableReasons =
ImmutableSet.of(Reason.REGISTERED, Reason.RESERVED);
for (var domainName : batch) {
if (!persistedUnblockables.containsKey(domainName)) {
errors.add(String.format("Missing unblockable domain: %s is reserved.", domainName));
continue;
}
var unblockable = persistedUnblockables.get(domainName);
if (!acceptableReasons.contains(unblockable.reason())) {
errors.add(
String.format(
"Wrong unblockable reason: %s should be reserved or registered, found %s.",
domainName, unblockable.reason()));
}
}
return errors.build();
}
static String parseBlockListLine(String line) {
int firstComma = line.indexOf(',');
checkArgument(firstComma > 0, "Invalid block list line: %s", line);
@@ -19,12 +19,14 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.bsa.BsaStringUtils.DOMAIN_SPLITTER;
import static google.registry.bsa.BsaTransactions.bsaQuery;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.bsa.api.UnblockableDomain;
import google.registry.model.CreateAutoTimestamp;
import java.sql.Timestamp;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -66,6 +68,11 @@ public final class Queries {
.collect(toImmutableList());
}
public static Stream<UnblockableDomain> queryUnblockableDomainByLabels(
ImmutableCollection<String> labels) {
return queryBsaUnblockableDomainByLabels(labels).map(BsaUnblockableDomain::toUnblockableDomain);
}
static Stream<BsaUnblockableDomain> queryBsaUnblockableDomainByLabels(
ImmutableCollection<String> labels) {
return ((Stream<?>)
@@ -141,4 +148,55 @@ public final class Queries {
.setParameter("tlds", tlds)
.getResultList());
}
/**
* Finds all currently registered domains that match BSA labels but are not recorded as
* unblockable.
*
* @return The missing unblockables and their creation and deletion time.
*/
public static ImmutableList<DomainLifeSpan> queryMissedRegisteredUnblockables(
String tld, DateTime now) {
String sqlTemplate =
"""
SELECT l.domain_name, creation_time, deletion_time
FROM
(SELECT d.domain_name, d.creation_time, d.deletion_time
FROM
"Domain" d
JOIN
(SELECT concat(label, '.', :tld) AS domain_name from "BsaLabel") b
ON b.domain_name = d.domain_name
WHERE deletion_time > ':now') l
LEFT OUTER JOIN
(SELECT concat(label, '.', tld) as domain_name
FROM "BsaUnblockableDomain"
WHERE tld = :tld and reason = 'REGISTERED') r
ON l.domain_name = r.domain_name
WHERE r.domain_name is null;
""";
// Native query: Hibernate's setParameter wrongly converts DateTime to bytea
String sql = sqlTemplate.replace(":now", now.toString());
return ((Stream<?>)
tm().getEntityManager()
.createNativeQuery(sql)
.setParameter("tld", tld)
.getResultStream())
.map(Object[].class::cast)
.map(
row ->
new DomainLifeSpan(
(String) row[0],
toDateTime((Timestamp) row[1]),
toDateTime((Timestamp) row[2])))
.collect(toImmutableList());
}
// For testing convenience: 'assertEquals' fails between `new DateTime(timestamp)` and below.
static DateTime toDateTime(Timestamp timestamp) {
return new DateTime(timestamp.getTime(), UTC);
}
public record DomainLifeSpan(String domainName, DateTime creationTime, DateTime deletionTime) {}
}
@@ -42,7 +42,7 @@ public final class StaticPremiumListPricingEngine implements PremiumPricingEngin
tld.getPremiumListName().flatMap(pl -> PremiumListDao.getPremiumPrice(pl, label));
return DomainPrices.create(
premiumPrice.isPresent(),
premiumPrice.orElse(tld.getCreateBillingCost()),
premiumPrice.orElse(tld.getCreateBillingCost(priceTime)),
premiumPrice.orElse(tld.getStandardRenewCost(priceTime)));
}
}
@@ -74,7 +74,6 @@ import google.registry.tldconfig.idn.IdnTableEnum;
import google.registry.util.Idn;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
@@ -138,10 +137,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
try {
String thisYaml = mapper.writeValueAsString(this);
String otherYaml = mapper.writeValueAsString(tldToCompare);
// Since Jackson uses getters and not field values to construct the YAML representation, an
// explicit check of the createBillingCostTransitions is necessary since this field is
// auto-populated in the getter when the field is set to null.
return thisYaml.equals(otherYaml) && createBillingCostTransitionsEqual(tldToCompare);
return thisYaml.equals(otherYaml);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
@@ -465,6 +461,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
// TODO(sarahbot@): Remove this field and make createBillingCostTransitions not-null once all TLDs
// are populated with a create cost transition map
/** The per-year billing cost for registering a new domain name. */
@Deprecated
@Type(type = JodaMoneyType.TYPE_NAME)
@Columns(
columns = {
@@ -473,11 +470,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
})
Money createBillingCost = DEFAULT_CREATE_BILLING_COST;
// TODO(sarahbot@): Make this field not null and add a default value once field is populated on
// all existing TLDs
// TODO(sarahbot@): Make this field not null in the database
// TODO(sarahbot@): Rename this field to createBillingCost once the old createBillingCost has been
// removed
/** A property that transitions to different create billing costs at different times. */
@JsonDeserialize(using = TimedTransitionPropertyMoneyDeserializer.class)
TimedTransitionProperty<Money> createBillingCostTransitions;
TimedTransitionProperty<Money> createBillingCostTransitions =
TimedTransitionProperty.withInitialValue(DEFAULT_CREATE_BILLING_COST);
/** The one-time billing cost for restoring a domain name from the redemption grace period. */
@Type(type = JodaMoneyType.TYPE_NAME)
@@ -684,24 +683,20 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* domain create.
*/
@VisibleForTesting
public Money getCreateBillingCost(DateTime now) {
return createBillingCostTransitions.getValueAtTime(now);
}
// This getter is still necessary for the Jackson deserialization in the ConfigureTldCommand
// TODO(sarahbot@): Remove this getter once the deprecated createBillingCost field is removed from
// the schema
@Deprecated
public Money getCreateBillingCost() {
return createBillingCost;
}
public ImmutableSortedMap<DateTime, Money> getCreateBillingCostTransitions() {
return Objects.requireNonNullElseGet(
createBillingCostTransitions,
() -> TimedTransitionProperty.withInitialValue(getCreateBillingCost()))
.toValueMap();
}
public boolean createBillingCostTransitionsEqual(Tld newTld) {
if (createBillingCostTransitions == null) {
return false;
}
return createBillingCostTransitions
.toValueMap()
.equals(newTld.getCreateBillingCostTransitions());
return createBillingCostTransitions.toValueMap();
}
/**
@@ -1171,16 +1166,14 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
// and cloned it into a new builder, to block re-building a Tld in an invalid state.
instance.tldStateTransitions.checkValidity();
// TODO(sarahbot@): Remove null check when createBillingCostTransitions field is made not-null
if (instance.createBillingCostTransitions != null) {
instance.createBillingCostTransitions.checkValidity();
}
checkArgumentNotNull(
instance.getCreateBillingCostTransitions(),
"CreateBillingCostTransitions cannot be null");
instance.createBillingCostTransitions.checkValidity();
instance.renewBillingCostTransitions.checkValidity();
instance.eapFeeSchedule.checkValidity();
// All costs must be in the expected currency.
checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
checkArgument(
instance.getCreateBillingCost().getCurrencyUnit().equals(instance.currency),
"Create cost must be in the tld's currency");
checkArgument(
instance.getRestoreBillingCost().getCurrencyUnit().equals(instance.currency),
"Restore cost must be in the TLD's currency");
@@ -1195,6 +1188,9 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
checkArgument(
instance.getRenewBillingCostTransitions().values().stream().allMatch(currencyCheck),
"Renew cost must be in the TLD's currency");
checkArgument(
instance.getCreateBillingCostTransitions().values().stream().allMatch(currencyCheck),
"Create cost must be in the TLD's currency");
checkArgument(
instance.eapFeeSchedule.toValueMap().values().stream().allMatch(currencyCheck),
"All EAP fees must be in the TLD's currency");
@@ -25,11 +25,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import com.google.common.flogger.FluentLogger;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.tld.Tld;
import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumListDao;
@@ -43,11 +41,9 @@ import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Named;
import org.joda.money.CurrencyUnit;
@@ -123,9 +119,7 @@ public class ConfigureTldCommand extends MutatingCommand {
checkName(name, tldData);
Tld oldTld = getTlds().contains(name) ? Tld.get(name) : null;
checkForMissingFields(
Stream.concat(tldData.keySet().stream(), Stream.of("createBillingCostTransitions"))
.collect(toImmutableSet()));
checkForMissingFields(tldData.keySet().stream().collect(toImmutableSet()));
Tld newTld = mapper.readValue(inputFile.toFile(), Tld.class);
if (oldTld != null) {
oldTldInBreakGlass = oldTld.getBreakglassMode();
@@ -156,14 +150,6 @@ public class ConfigureTldCommand extends MutatingCommand {
checkPremiumList(newTld);
checkDnsWriters(newTld);
checkCurrency(newTld);
// TODO(sarahbot@): Remove this once the createBillingCost field is removed
checkArgument(
Objects.equals(
TimedTransitionProperty.fromValueMap(newTld.getCreateBillingCostTransitions())
.getValueAtTime(clock.nowUtc()),
newTld.getCreateBillingCost()),
"The createBillingCostTransitions map must have the same current cost as the"
+ " createBillingCost field");
// bsaEnrollStartTime only exists in DB. Need to carry it over to the updated copy. See Tld.java
// for more information.
Optional<DateTime> bsaEnrollTime =
@@ -259,9 +245,6 @@ public class ConfigureTldCommand extends MutatingCommand {
private void checkCurrency(Tld newTld) {
CurrencyUnit currencyUnit = newTld.getCurrency();
checkArgument(
currencyUnit.equals(newTld.getCreateBillingCost().getCurrencyUnit()),
"createBillingCost must use the same currency as the TLD");
checkArgument(
currencyUnit.equals(newTld.getRestoreBillingCost().getCurrencyUnit()),
"restoreBillingCost must use the same currency as the TLD");
@@ -271,19 +254,38 @@ public class ConfigureTldCommand extends MutatingCommand {
checkArgument(
currencyUnit.equals(newTld.getRegistryLockOrUnlockBillingCost().getCurrencyUnit()),
"registryLockOrUnlockBillingCost must use the same currency as the TLD");
ImmutableSortedMap<DateTime, Money> renewBillingCostTransitions =
newTld.getRenewBillingCostTransitions();
for (Money renewBillingCost : renewBillingCostTransitions.values()) {
checkArgument(
renewBillingCost.getCurrencyUnit().equals(currencyUnit),
"All Money values in the renewBillingCostTransitions map must use the TLD's currency"
+ " unit");
}
ImmutableSortedMap<DateTime, Money> eapFeeSchedule = newTld.getEapFeeScheduleAsMap();
for (Money eapFee : eapFeeSchedule.values()) {
checkArgument(
eapFee.getCurrencyUnit().equals(currencyUnit),
"All Money values in the eapFeeSchedule map must use the TLD's currency unit");
}
ImmutableSet<CurrencyUnit> currencies =
newTld.getRenewBillingCostTransitions().values().stream()
.map(Money::getCurrencyUnit)
.distinct()
.collect(toImmutableSet());
checkArgument(
currencies.size() == 1 && currencies.contains(currencyUnit),
"All Money values in the renewBillingCostTransitions map must use the TLD's currency unit"
+ " %s. Found %s currency unit(s) in the renewBillingCostTransitionsMap",
currencyUnit,
currencies);
ImmutableSet<CurrencyUnit> createCurrencies =
newTld.getCreateBillingCostTransitions().values().stream()
.map(Money::getCurrencyUnit)
.distinct()
.collect(toImmutableSet());
checkArgument(
createCurrencies.size() == 1 && createCurrencies.contains(currencyUnit),
"All Money values in the createBillingCostTransitions map must use the TLD's currency unit"
+ " %s. Found %s currency unit(s) in the createBillingCostTransitionsMap",
currencyUnit,
createCurrencies);
ImmutableSet<CurrencyUnit> eapCurrencies =
newTld.getEapFeeScheduleAsMap().values().stream()
.map(Money::getCurrencyUnit)
.distinct()
.collect(toImmutableSet());
checkArgument(
eapCurrencies.size() == 1 && eapCurrencies.contains(currencyUnit),
"All Money values in the eapFeeSchedule map must use the TLD's currency unit"
+ " %s. Found %s currency unit(s) in the eapFeeSchedule",
currencyUnit,
eapCurrencies);
}
}
@@ -15,13 +15,18 @@
package google.registry.bsa;
import static com.google.common.base.Throwables.getStackTraceAsString;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.bsa.ReservedDomainsTestingUtils.addReservedListsToTld;
import static google.registry.bsa.ReservedDomainsTestingUtils.createReservedList;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.bsa.persistence.BsaTestingUtils.persistDownloadSchedule;
import static google.registry.bsa.persistence.BsaTestingUtils.persistUnblockableDomain;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.startsWith;
@@ -43,13 +48,16 @@ import google.registry.bsa.persistence.BsaTestingUtils;
import google.registry.gcs.GcsUtils;
import google.registry.groups.GmailClient;
import google.registry.model.domain.Domain;
import google.registry.model.tld.label.ReservationType;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.request.Response;
import google.registry.testing.FakeClock;
import google.registry.tldconfig.idn.IdnTableEnum;
import google.registry.util.EmailMessage;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.stream.Stream;
import javax.mail.internet.InternetAddress;
import org.joda.time.DateTime;
import org.joda.time.Duration;
@@ -269,7 +277,7 @@ public class BsaValidateActionTest {
persistUnblockableDomain(UnblockableDomain.of("label", "app", Reason.REGISTERED));
when(idnChecker.getAllValidIdns(anyString())).thenReturn(ImmutableSet.of(IdnTableEnum.JA));
assertThat(action.checkUnblockableDomains()).isEmpty();
assertThat(action.checkWronglyReportedUnblockableDomains()).isEmpty();
}
@Test
@@ -280,10 +288,75 @@ public class BsaValidateActionTest {
persistUnblockableDomain(UnblockableDomain.of("label", "app", Reason.RESERVED));
when(idnChecker.getAllValidIdns(anyString())).thenReturn(ImmutableSet.of(IdnTableEnum.JA));
assertThat(action.checkUnblockableDomains())
assertThat(action.checkWronglyReportedUnblockableDomains())
.containsExactly("label.app: should be REGISTERED, found RESERVED");
}
@Test
void checkForMissingReservedUnblockables_success() {
persistResource(
createTld("app").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
persistResource(
createTld("dev").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
persistBsaLabel("registered-reserved");
persistBsaLabel("reserved-only");
persistBsaLabel("reserved-missing");
persistBsaLabel("invalid-in-app");
persistUnblockableDomain(UnblockableDomain.of("registered-reserved", "app", Reason.REGISTERED));
persistUnblockableDomain(UnblockableDomain.of("reserved-only", "app", Reason.RESERVED));
persistUnblockableDomain(UnblockableDomain.of("invalid-in-app", "dev", Reason.RESERVED));
createReservedList(
"rl",
Stream.of("registered-reserved", "reserved-only", "reserved-missing")
.collect(toImmutableMap(x -> x, x -> ReservationType.RESERVED_FOR_SPECIFIC_USE)));
addReservedListsToTld("app", ImmutableList.of("rl"));
ImmutableList<String> errors = action.checkForMissingReservedUnblockables(fakeClock.nowUtc());
assertThat(errors)
.containsExactly("Missing unblockable domain: reserved-missing.app is reserved.");
}
@Test
void checkForMissingReservedUnblockablesInOneTld_success() {
persistResource(
createTld("app").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
persistResource(
createTld("dev").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
persistBsaLabel("reserved-missing-in-app");
persistUnblockableDomain(
UnblockableDomain.of("reserved-missing-in-app", "dev", Reason.REGISTERED));
createReservedList(
"rl",
Stream.of("reserved-missing-in-app")
.collect(toImmutableMap(x -> x, x -> ReservationType.RESERVED_FOR_SPECIFIC_USE)));
addReservedListsToTld("app", ImmutableList.of("rl"));
addReservedListsToTld("dev", ImmutableList.of("rl"));
ImmutableList<String> errors = action.checkForMissingReservedUnblockables(fakeClock.nowUtc());
assertThat(errors)
.containsExactly("Missing unblockable domain: reserved-missing-in-app.app is reserved.");
}
@Test
void checkForMissingRegisteredUnblockables_success() {
persistResource(
createTld("app").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
persistBsaLabel("registered");
persistBsaLabel("registered-missing");
persistUnblockableDomain(UnblockableDomain.of("registered", "app", Reason.REGISTERED));
persistUnblockableDomain(UnblockableDomain.of("registered-missing", "app", Reason.RESERVED));
persistActiveDomain("registered.app");
persistActiveDomain("registered-missing.app");
ImmutableList<String> errors = action.checkForMissingRegisteredUnblockables(fakeClock.nowUtc());
assertThat(errors)
.containsExactly(
"Registered domain registered-missing.app missing or not recorded as REGISTERED");
}
@Test
void notificationSent_cannotAcquireLock() {
when(bsaLock.executeWithLock(any())).thenReturn(false);
@@ -21,18 +21,22 @@ import static google.registry.bsa.persistence.Queries.batchReadBsaLabelText;
import static google.registry.bsa.persistence.Queries.deleteBsaLabelByLabels;
import static google.registry.bsa.persistence.Queries.queryBsaLabelByLabels;
import static google.registry.bsa.persistence.Queries.queryBsaUnblockableDomainByLabels;
import static google.registry.bsa.persistence.Queries.queryMissedRegisteredUnblockables;
import static google.registry.bsa.persistence.Queries.queryNewlyCreatedDomains;
import static google.registry.bsa.persistence.Queries.queryUnblockablesByNames;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.DatabaseHelper.newDomain;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.bsa.api.UnblockableDomain;
import google.registry.bsa.persistence.BsaUnblockableDomain.Reason;
import google.registry.bsa.persistence.Queries.DomainLifeSpan;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.FakeClock;
@@ -200,16 +204,14 @@ class QueriesTest {
createTlds("tld");
persistNewRegistrar("TheRegistrar");
// time 0:
persistResource(
newDomain("d1.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build());
persistActiveDomain("d1.tld", fakeClock.nowUtc());
// time 0, deletion time 1
persistDomainAsDeleted(
newDomain("will-delete.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build(),
fakeClock.nowUtc().plusMillis(1));
fakeClock.advanceOneMilli();
// time 1
persistResource(
newDomain("d2.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build());
persistActiveDomain("d2.tld", fakeClock.nowUtc());
fakeClock.advanceOneMilli();
// Now is time 2
assertThat(
@@ -226,16 +228,14 @@ class QueriesTest {
createTlds("tld");
persistNewRegistrar("TheRegistrar");
// time 0:
persistResource(
newDomain("d1.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build());
persistActiveDomain("d1.tld", fakeClock.nowUtc());
// time 0, deletion time 1
persistDomainAsDeleted(
newDomain("will-delete.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build(),
fakeClock.nowUtc().plusMillis(1));
fakeClock.advanceOneMilli();
// time 1
persistResource(
newDomain("d2.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build());
persistActiveDomain("d2.tld", fakeClock.nowUtc());
fakeClock.advanceOneMilli();
// Now is time 2, ask for domains created since time 1
assertThat(
@@ -251,10 +251,8 @@ class QueriesTest {
DateTime testStartTime = fakeClock.nowUtc();
createTlds("tld", "tld2");
persistNewRegistrar("TheRegistrar");
persistResource(
newDomain("d1.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build());
persistResource(
newDomain("d2.tld2").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build());
persistActiveDomain("d1.tld", fakeClock.nowUtc());
persistActiveDomain("d2.tld2", fakeClock.nowUtc());
fakeClock.advanceOneMilli();
assertThat(
bsaQuery(
@@ -263,4 +261,38 @@ class QueriesTest {
ImmutableList.of("tld"), testStartTime, fakeClock.nowUtc())))
.containsExactly("d1.tld");
}
@Test
void queryMissedRegisteredUnblockables_success() {
createTlds("tld", "tld2");
persistNewRegistrar("TheRegistrar");
DateTime time1 = fakeClock.nowUtc();
persistActiveDomain("unblocked1.tld", fakeClock.nowUtc());
persistActiveDomain("unblocked2.tld2", fakeClock.nowUtc());
persistActiveDomain("label1.tld", fakeClock.nowUtc());
persistActiveDomain("label2.tld2", fakeClock.nowUtc());
fakeClock.advanceOneMilli();
DateTime time2 = fakeClock.nowUtc();
persistDomainAsDeleted(
newDomain("label3.tld").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build(),
fakeClock.nowUtc().plusMillis(1));
// Deleted in the future
persistDomainAsDeleted(
newDomain("label3.tld2").asBuilder().setCreationTimeForTest(fakeClock.nowUtc()).build(),
fakeClock.nowUtc().plusHours(1));
fakeClock.advanceOneMilli();
assertThat(bsaQuery(() -> queryMissedRegisteredUnblockables("tld", fakeClock.nowUtc())))
.containsExactly(new DomainLifeSpan("label1.tld", time1, END_OF_TIME));
assertThat(bsaQuery(() -> queryMissedRegisteredUnblockables("tld2", fakeClock.nowUtc())))
.containsExactly(
new DomainLifeSpan("label2.tld2", time1, END_OF_TIME),
new DomainLifeSpan("label3.tld2", time2, time2.plusHours(1)));
BsaTestingUtils.persistUnblockableDomain(
UnblockableDomain.of("label2", "tld2", UnblockableDomain.Reason.REGISTERED));
BsaTestingUtils.persistUnblockableDomain(
UnblockableDomain.of("label3", "tld2", UnblockableDomain.Reason.RESERVED));
assertThat(bsaQuery(() -> queryMissedRegisteredUnblockables("tld2", fakeClock.nowUtc())))
.containsExactly(new DomainLifeSpan("label3.tld2", time2, time2.plusHours(1)));
}
}
@@ -751,7 +751,8 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
Tld.get("tld")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.ofMajor(JPY, 800))
.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)))
@@ -1344,7 +1345,12 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
// 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().setCreateBillingCost(Money.of(USD, 11.1)).build());
persistResource(
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 11.1)))
.build());
setEppInput("domain_check_fee_fractional.xml");
runFlowAssertResponse(loadFile("domain_check_fee_fractional_response.xml"));
}
@@ -134,7 +134,8 @@ public class DomainClaimsCheckFlowTest extends ResourceFlowTestCase<DomainClaims
Tld.get("tld")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.ofMajor(JPY, 800))
.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)))
@@ -1086,7 +1086,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_wrongFeeAmount_v06() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
persistResource(Tld.get("tld").asBuilder().setCreateBillingCost(Money.of(USD, 20)).build());
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();
@@ -1107,7 +1112,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.setCreateBillingCost(Money.of(USD, 8))
.setCreateBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 8)))
.build());
// Expects fee of $26
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
@@ -1134,7 +1139,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.setCreateBillingCost(Money.of(USD, 100))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 100)))
.build());
// Expects fee of $26
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
@@ -1146,7 +1152,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_wrongFeeAmount_v11() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
persistResource(Tld.get("tld").asBuilder().setCreateBillingCost(Money.of(USD, 20)).build());
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();
@@ -1167,7 +1178,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.setCreateBillingCost(Money.of(USD, 8))
.setCreateBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 8)))
.build());
// Expects fee of $26
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
@@ -1194,7 +1205,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.setCreateBillingCost(Money.of(USD, 100))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 100)))
.build());
// Expects fee of $26
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
@@ -1206,7 +1218,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFailure_wrongFeeAmount_v12() {
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
persistResource(Tld.get("tld").asBuilder().setCreateBillingCost(Money.of(USD, 20)).build());
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();
@@ -1227,7 +1244,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.setCreateBillingCost(Money.of(USD, 8))
.setCreateBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 8)))
.build());
// Expects fee of $26
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
@@ -1254,7 +1271,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Tld.get("tld")
.asBuilder()
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
.setCreateBillingCost(Money.of(USD, 100))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 100)))
.build());
// Expects fee of $26
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
@@ -2981,7 +2999,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
Tld.get("tld")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.ofMajor(JPY, 800))
.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)))
@@ -186,7 +186,8 @@ class DomainFlowUtilsTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
.asBuilder()
.setTldType(tldType)
.setCurrency(CHF)
.setCreateBillingCost(Money.ofMajor(CHF, 800))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(CHF, 800)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(CHF, 800)))
.setRenewBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.ofMajor(CHF, 800)))
@@ -1033,7 +1033,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCost(Money.of(EUR, 13))
.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)))
@@ -1052,7 +1053,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCost(Money.of(EUR, 13))
.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)))
@@ -1071,7 +1073,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCost(Money.of(EUR, 13))
.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)))
@@ -1114,7 +1117,8 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
Tld.get("tld")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.ofMajor(JPY, 800))
.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)))
@@ -559,7 +559,8 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
Tld.get("tld")
.asBuilder()
.setCurrency(EUR)
.setCreateBillingCost(Money.of(EUR, 13))
.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)))
@@ -702,7 +703,8 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
Tld.get("tld")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.ofMajor(JPY, 800))
.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)))
@@ -1070,7 +1070,8 @@ class DomainTransferRequestFlowTest
Tld.get("tld")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.ofMajor(JPY, 800))
.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)))
@@ -65,7 +65,8 @@ class RegistrarTest extends EntityTestCase {
newTld("xn--q9jyb4c", "MINNA")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.of(JPY, new BigDecimal(1300)))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1300))))
.setRestoreBillingCost(Money.of(JPY, new BigDecimal(1700)))
.setServerStatusChangeBillingCost(Money.of(JPY, new BigDecimal(1900)))
.setRegistryLockOrUnlockBillingCost(Money.of(JPY, new BigDecimal(2700)))
@@ -223,14 +223,6 @@ public final class TldTest extends EntityTestCase {
.isFalse();
}
@Test
void testSettingCreateBillingCost() {
Tld registry = Tld.get("tld").asBuilder().setCreateBillingCost(Money.of(USD, 42)).build();
assertThat(registry.getCreateBillingCost()).isEqualTo(Money.of(USD, 42));
// The default value of 17 is set in createTld().
assertThat(registry.getRestoreBillingCost()).isEqualTo(Money.of(USD, 17));
}
@Test
void testSetCreateBillingCostTransitions() {
ImmutableSortedMap<DateTime, Money> createCostTransitions =
@@ -246,6 +238,10 @@ public final class TldTest extends EntityTestCase {
Tld registry =
Tld.get("tld").asBuilder().setCreateBillingCostTransitions(createCostTransitions).build();
assertThat(registry.getCreateBillingCostTransitions()).isEqualTo(createCostTransitions);
assertThat(registry.getCreateBillingCost(fakeClock.nowUtc().minus(Duration.standardDays(5))))
.isEqualTo(Money.of(USD, 8));
assertThat(registry.getCreateBillingCost(fakeClock.nowUtc().plusMonths(8)))
.isEqualTo(Money.of(USD, 3));
}
@Test
@@ -275,7 +271,7 @@ public final class TldTest extends EntityTestCase {
void testSettingRestoreBillingCost() {
Tld registry = Tld.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 42)).build();
// The default value of 13 is set in createTld().
assertThat(registry.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(registry.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
assertThat(registry.getRestoreBillingCost()).isEqualTo(Money.of(USD, 42));
}
@@ -595,7 +591,13 @@ public final class TldTest extends EntityTestCase {
void testFailure_pricingEngineIsRequired() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class, () -> new Tld.Builder().setTldStr("invalid").build());
IllegalArgumentException.class,
() ->
new Tld.Builder()
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 13)))
.setTldStr("invalid")
.build());
assertThat(thrown)
.hasMessageThat()
.contains("All registries must have a configured pricing engine");
@@ -680,8 +682,13 @@ public final class TldTest extends EntityTestCase {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> Tld.get("tld").asBuilder().setCreateBillingCost(Money.of(EUR, 42)).build());
assertThat(thrown).hasMessageThat().contains("cost must be in the tld's currency");
() ->
Tld.get("tld")
.asBuilder()
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(EUR, 42)))
.build());
assertThat(thrown).hasMessageThat().contains("cost must be in the TLD's currency");
}
@Test
@@ -244,6 +244,7 @@ public final class DatabaseHelper {
.setRenewBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 11)))
.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(USD)))
.setCreateBillingCost(Money.of(USD, 13))
.setCreateBillingCostTransitions(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 13)))
.setRestoreBillingCost(Money.of(USD, 17))
.setServerStatusChangeBillingCost(Money.of(USD, 19))
// Always set a default premium list. Tests that don't want it can delete it.
@@ -89,7 +89,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
Tld tld = Tld.get("tld");
assertThat(tld).isNotNull();
assertThat(tld.getDriveFolderId()).isEqualTo("driveFolder");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
testTldConfiguredSuccessfully(tld, "tld.yaml");
assertThat(tld.getBreakglassMode()).isFalse();
}
@@ -101,7 +101,8 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
runCommandForced("--input=" + tldFile);
Tld tld = Tld.get("jpy");
assertThat(tld).isNotNull();
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(JPY, new BigDecimal("250")));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc()))
.isEqualTo(Money.of(JPY, new BigDecimal("250")));
assertThat(tld.getEapFeeFor(DateTime.now(UTC)).getCost()).isEqualTo(new BigDecimal(0));
testTldConfiguredSuccessfully(tld, "jpy.yaml");
}
@@ -109,12 +110,12 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
@Test
void testSuccess_updateTld() throws Exception {
Tld tld = createTld("tld");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
File tldFile = tmpDir.resolve("tld.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
runCommandForced("--input=" + tldFile);
Tld updatedTld = Tld.get("tld");
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
testTldConfiguredSuccessfully(updatedTld, "tld.yaml");
assertThat(updatedTld.getBreakglassMode()).isFalse();
assertThat(tld.getBsaEnrollStartTime()).isEmpty();
@@ -139,8 +140,6 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
tld.asBuilder()
.setIdnTables(ImmutableSet.of(JA, UNCONFUSABLE_LATIN, EXTENDED_LATIN))
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("zeta", "alpha", "gamma", "beta"))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 13)))
.build());
File tldFile = tmpDir.resolve("idns.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "idns.yaml"));
@@ -150,22 +149,6 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
.hasLogAtLevelWithMessage(INFO, "TLD YAML file contains no new changes");
}
@Test
void testSuccess_addCreateCostTransitions_hasDiff() throws Exception {
Tld tld = createTld("idns");
persistResource(
tld.asBuilder()
.setIdnTables(ImmutableSet.of(JA, UNCONFUSABLE_LATIN, EXTENDED_LATIN))
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("zeta", "alpha", "gamma", "beta"))
.build());
File tldFile = tmpDir.resolve("idns.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "idns.yaml"));
runCommandForced("--input=" + tldFile);
Tld updatedTld = Tld.get("idns");
testTldConfiguredSuccessfully(updatedTld, "idns.yaml");
assertThat(tld.createBillingCostTransitionsEqual(updatedTld)).isFalse();
}
@Test
void testSuccess_outOfOrderFieldsOnCreate() throws Exception {
File tldFile = tmpDir.resolve("outoforderfields.yaml").toFile();
@@ -176,21 +159,21 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
// TLD's YAML will contain the fields in the correct order
assertThat(tld).isNotNull();
assertThat(tld.getDriveFolderId()).isEqualTo("driveFolder");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
assertThat(tld.getPremiumListName().get()).isEqualTo("test");
}
@Test
void testSuccess_outOfOrderFieldsOnUpdate() throws Exception {
Tld tld = createTld("outoforderfields");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
File tldFile = tmpDir.resolve("outoforderfields.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "outoforderfields.yaml"));
runCommandForced("--input=" + tldFile);
Tld updatedTld = Tld.get("outoforderfields");
// Cannot test that created TLD converted to YAML is equal to original YAML since the created
// TLD's YAML will contain the fields in the correct order
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
}
@Test
@@ -231,19 +214,6 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
.isEqualTo(ImmutableSortedMap.of(START_OF_TIME, Money.of(USD, 25)));
}
@Test
void testFailure_billingCostTransitionsDoesNotMatchCreateCost() throws Exception {
createTld("diffcostmap");
File tldFile = tmpDir.resolve("diffcostmap.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "diffcostmap.yaml"));
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> runCommandForced("--input=" + tldFile));
assertThat(thrown.getMessage())
.isEqualTo(
"The createBillingCostTransitions map must have the same current cost as the"
+ " createBillingCost field");
}
@Test
void testFailure_fileMissingNullableFieldsOnCreate() throws Exception {
File tldFile = tmpDir.resolve("missingnullablefields.yaml").toFile();
@@ -296,7 +266,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
Tld tld = Tld.get("nullablefieldsallnull");
assertThat(tld).isNotNull();
assertThat(tld.getDriveFolderId()).isEqualTo(null);
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
// cannot test that created TLD converted to YAML is equal to original YAML since the created
// TLD's YAML will contain empty sets for some of the null fields
assertThat(tld.getIdnTables()).isEmpty();
@@ -314,7 +284,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
Tld updatedTld = Tld.get("nullablefieldsallnull");
assertThat(updatedTld).isNotNull();
assertThat(updatedTld.getDriveFolderId()).isEqualTo(null);
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
// cannot test that created TLD converted to YAML is equal to original YAML since the created
// TLD's YAML will contain empty sets for some of the null fields
assertThat(updatedTld.getIdnTables()).isEmpty();
@@ -387,7 +357,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
Tld tld = Tld.get(name);
assertThat(tld).isNotNull();
assertThat(tld.getDriveFolderId()).isEqualTo("driveFolder");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
String yaml = objectMapper.writeValueAsString(tld);
assertThat(yaml).isEqualTo(fileContents);
}
@@ -505,7 +475,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
assertThat(thrown.getMessage())
.isEqualTo(
"All Money values in the renewBillingCostTransitions map must use the TLD's currency"
+ " unit");
+ " unit USD. Found [EUR] currency unit(s) in the renewBillingCostTransitionsMap");
}
@Test
@@ -534,7 +504,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
assertThat(thrown.getMessage())
.isEqualTo(
"All Money values in the renewBillingCostTransitions map must use the TLD's currency"
+ " unit");
+ " unit USD. Found [EUR] currency unit(s) in the renewBillingCostTransitionsMap");
}
@Test
@@ -614,12 +584,12 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
@Test
void testSuccess_breakGlassFlag_startsBreakGlassMode() throws Exception {
Tld tld = createTld("tld");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
File tldFile = tmpDir.resolve("tld.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
runCommandForced("--input=" + tldFile, "--break_glass=true");
Tld updatedTld = Tld.get("tld");
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
testTldConfiguredSuccessfully(updatedTld, "tld.yaml");
assertThat(updatedTld.getBreakglassMode()).isTrue();
}
@@ -627,13 +597,13 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
@Test
void testSuccess_breakGlassFlag_continuesBreakGlassMode() throws Exception {
Tld tld = createTld("tld");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
persistResource(tld.asBuilder().setBreakglassMode(true).build());
File tldFile = tmpDir.resolve("tld.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
runCommandForced("--input=" + tldFile, "--break_glass=true");
Tld updatedTld = Tld.get("tld");
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
testTldConfiguredSuccessfully(updatedTld, "tld.yaml");
assertThat(updatedTld.getBreakglassMode()).isTrue();
}
@@ -712,13 +682,13 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
@Test
void testSuccess_breakGlassFlagFalse_endsBreakGlassMode() throws Exception {
Tld tld = createTld("tld");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
persistResource(tld.asBuilder().setBreakglassMode(true).build());
File tldFile = tmpDir.resolve("tld.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
runCommandForced("--break_glass=false", "--input=" + tldFile);
Tld updatedTld = Tld.get("tld");
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
testTldConfiguredSuccessfully(updatedTld, "tld.yaml");
assertThat(updatedTld.getBreakglassMode()).isFalse();
}
@@ -755,12 +725,12 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
@Test
void testSuccess_dryRunOnUpdate_noChanges() throws Exception {
Tld tld = createTld("tld");
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(tld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
File tldFile = tmpDir.resolve("tld.yaml").toFile();
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
runCommandForced("--input=" + tldFile, "-d");
Tld notUpdatedTld = Tld.get("tld");
assertThat(notUpdatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
assertThat(notUpdatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 13));
}
@Test
@@ -787,7 +757,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
runCommandInEnvironment(
RegistryToolEnvironment.PRODUCTION, "--input=" + tldFile, "--break_glass=true", "-f");
Tld updatedTld = Tld.get("tld");
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
testTldConfiguredSuccessfully(updatedTld, "tld.yaml");
assertThat(updatedTld.getBreakglassMode()).isTrue();
}
@@ -800,7 +770,7 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
runCommandInEnvironment(
RegistryToolEnvironment.PRODUCTION, "--input=" + tldFile, "--build_environment", "-f");
Tld updatedTld = Tld.get("tld");
assertThat(updatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 25));
assertThat(updatedTld.getCreateBillingCost(fakeClock.nowUtc())).isEqualTo(Money.of(USD, 25));
testTldConfiguredSuccessfully(updatedTld, "tld.yaml");
assertThat(updatedTld.getBreakglassMode()).isFalse();
}
@@ -593,7 +593,8 @@ class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarCommand>
newTld("foo", "FOO")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.of(JPY, new BigDecimal(1300)))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1300))))
.setRestoreBillingCost(Money.of(JPY, new BigDecimal(1700)))
.setServerStatusChangeBillingCost(Money.of(JPY, new BigDecimal(1900)))
.setRegistryLockOrUnlockBillingCost(Money.of(JPY, new BigDecimal(2700)))
@@ -449,7 +449,8 @@ class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarCommand>
newTld("foo", "FOO")
.asBuilder()
.setCurrency(JPY)
.setCreateBillingCost(Money.of(JPY, new BigDecimal(1300)))
.setCreateBillingCostTransitions(
ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1300))))
.setRestoreBillingCost(Money.of(JPY, new BigDecimal(1700)))
.setServerStatusChangeBillingCost(Money.of(JPY, new BigDecimal(1900)))
.setRegistryLockOrUnlockBillingCost(Money.of(JPY, new BigDecimal(2700)))
@@ -8,6 +8,10 @@ claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
createBillingCost:
currency: "USD"
amount: 25.00
createBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 25.00
creationTime: "2022-09-01T00:00:00.000Z"
currency: "USD"
defaultPromoTokens: []
@@ -8,6 +8,10 @@ claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
createBillingCost:
currency: "USD"
amount: 25.00
createBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 25.00
creationTime: "2022-09-01T00:00:00.000Z"
currency: "USD"
defaultPromoTokens: []
@@ -8,6 +8,10 @@ claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
createBillingCost:
currency: "USD"
amount: 25.00
createBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 25.00
creationTime: "2022-09-01T00:00:00.000Z"
defaultPromoTokens: []
dnsAPlusAaaaTtl: null
@@ -8,6 +8,10 @@ claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
createBillingCost:
currency: "USD"
amount: 25.00
createBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 25.00
creationTime: "2022-09-01T00:00:00.000Z"
currency: "USD"
defaultPromoTokens: []
@@ -30,6 +30,10 @@ currency: "USD"
createBillingCost:
currency: "USD"
amount: 25.00
createBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 25.00
restoreBillingCost:
currency: "USD"
amount: 17.00
@@ -53,3 +53,7 @@ eapFeeSchedule:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 0.00
createBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 25.00
@@ -30,6 +30,10 @@ currency: "USD"
createBillingCost:
currency: "USD"
amount: 25.00
createBillingCostTransitions:
"1970-01-01T00:00:00.000Z":
currency: "USD"
amount: 25.00
restoreBillingCost:
currency: %RESTORECURRENCY%
amount: 70.00