diff --git a/core/src/main/java/google/registry/model/OteAccountBuilder.java b/core/src/main/java/google/registry/model/OteAccountBuilder.java
index d4d2389fa..8d9008ecf 100644
--- a/core/src/main/java/google/registry/model/OteAccountBuilder.java
+++ b/core/src/main/java/google/registry/model/OteAccountBuilder.java
@@ -38,6 +38,7 @@ import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.tld.Registry;
import google.registry.model.tld.Registry.TldState;
+import google.registry.model.tld.Registry.TldType;
import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumListDao;
import google.registry.persistence.VKey;
@@ -111,6 +112,17 @@ public final class OteAccountBuilder {
DateTime.parse("2030-03-01T00:00:00Z"),
Money.of(CurrencyUnit.USD, 0));
+ /**
+ * The default billing account map applied to all OT&E registrars.
+ *
+ *
This contains dummy values for USD and JPY so that OT&E registrars can be granted access
+ * to all existing TLDs in sandbox. Note that OT&E is only on sandbox and thus these dummy
+ * values will never be used in production (the only environment where real invoicing takes
+ * place).
+ */
+ public static final ImmutableMap DEFAULT_BILLING_ACCOUNT_MAP =
+ ImmutableMap.of(CurrencyUnit.USD, "123", CurrencyUnit.JPY, "456");
+
private final ImmutableMap registrarIdToTld;
private final Registry sunriseTld;
private final Registry gaTld;
@@ -305,6 +317,7 @@ public final class OteAccountBuilder {
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, initialTldState))
.setDnsWriters(ImmutableSet.of("VoidDnsWriter"))
.setPremiumList(premiumList.get())
+ .setTldType(TldType.TEST)
.setRoidSuffix(
String.format(
"%S%X",
@@ -334,6 +347,7 @@ public final class OteAccountBuilder {
.setPhoneNumber("+1.2125550100")
.setIcannReferralEmail("nightmare@registrar.test")
.setState(Registrar.State.ACTIVE)
+ .setBillingAccountMap(DEFAULT_BILLING_ACCOUNT_MAP)
.build();
}
diff --git a/core/src/main/java/google/registry/model/registrar/Registrar.java b/core/src/main/java/google/registry/model/registrar/Registrar.java
index 69e71ac44..d06c9a233 100644
--- a/core/src/main/java/google/registry/model/registrar/Registrar.java
+++ b/core/src/main/java/google/registry/model/registrar/Registrar.java
@@ -81,6 +81,7 @@ import google.registry.model.common.EntityGroupRoot;
import google.registry.model.registrar.Registrar.BillingAccountEntry.CurrencyMapper;
import google.registry.model.replay.DatastoreAndSqlEntity;
import google.registry.model.tld.Registry;
+import google.registry.model.tld.Registry.TldType;
import google.registry.persistence.VKey;
import google.registry.util.CidrAddressBlock;
import java.security.cert.CertificateParsingException;
@@ -798,13 +799,9 @@ public class Registrar extends ImmutableObject
}
public Builder setBillingAccountMap(@Nullable Map billingAccountMap) {
- if (billingAccountMap == null) {
- getInstance().billingAccountMap = null;
- } else {
- getInstance().billingAccountMap =
- billingAccountMap.entrySet().stream()
- .collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new));
- }
+ getInstance().billingAccountMap =
+ nullToEmptyImmutableCopy(billingAccountMap).entrySet().stream()
+ .collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new));
return this;
}
@@ -1015,6 +1012,20 @@ public class Registrar extends ImmutableObject
String.format(
"Supplied IANA ID is not valid for %s registrar type: %s",
getInstance().type, getInstance().ianaIdentifier));
+
+ // In order to grant access to real TLDs, the registrar must have a corresponding billing
+ // account ID for that TLD's billing currency.
+ ImmutableSet nonBillableTlds =
+ Registry.get(getInstance().getAllowedTlds()).stream()
+ .filter(r -> r.getTldType() == TldType.REAL)
+ .filter(r -> !getInstance().getBillingAccountMap().containsKey(r.getCurrency()))
+ .map(Registry::getTldStr)
+ .collect(toImmutableSet());
+ checkArgument(
+ nonBillableTlds.isEmpty(),
+ "Cannot set these allowed, real TLDs because their currency is missing "
+ + "from the billing account map: %s",
+ nonBillableTlds);
return cloneEmptyToNull(super.build());
}
}
diff --git a/core/src/main/java/google/registry/model/tld/Registries.java b/core/src/main/java/google/registry/model/tld/Registries.java
index 40cadae87..385810dd1 100644
--- a/core/src/main/java/google/registry/model/tld/Registries.java
+++ b/core/src/main/java/google/registry/model/tld/Registries.java
@@ -72,7 +72,7 @@ public final class Registries {
.stream()
.map(Key::getName)
.collect(toImmutableSet());
- return Registry.getAll(tlds).stream()
+ return Registry.get(tlds).stream()
.map(e -> Maps.immutableEntry(e.getTldStr(), e.getTldType()))
.collect(entriesToImmutableMap());
} else {
@@ -105,7 +105,7 @@ public final class Registries {
/** Returns the Registry entities themselves of the given type loaded fresh from Datastore. */
public static ImmutableSet getTldEntitiesOfType(TldType type) {
- return Registry.getAll(filterValues(cache.get(), equalTo(type)).keySet());
+ return Registry.get(filterValues(cache.get(), equalTo(type)).keySet());
}
/** Pass-through check that the specified TLD exists, otherwise throw an IAE. */
diff --git a/core/src/main/java/google/registry/model/tld/Registry.java b/core/src/main/java/google/registry/model/tld/Registry.java
index e695c6028..4a947ca2a 100644
--- a/core/src/main/java/google/registry/model/tld/Registry.java
+++ b/core/src/main/java/google/registry/model/tld/Registry.java
@@ -142,10 +142,16 @@ public class Registry extends ImmutableObject
/** The type of TLD, which determines things like backups and escrow policy. */
public enum TldType {
- /** A real, official TLD. */
+ /**
+ * A real, official TLD (but not necessarily only on production).
+ *
+ * Note that, to avoid unnecessary costly DB writes, {@link
+ * google.registry.model.reporting.DomainTransactionRecord}s are only written out for REAL TLDs
+ * (these transaction records are only used for ICANN reporting purposes).
+ */
REAL,
- /** A test TLD, for the prober. */
+ /** A test TLD, for the prober, OT&E, and other testing purposes. */
TEST
}
@@ -232,7 +238,7 @@ public class Registry extends ImmutableObject
}
/** Returns the registry entities for the given TLD strings, throwing if any don't exist. */
- static ImmutableSet getAll(Set tlds) {
+ public static ImmutableSet get(Set tlds) {
try {
ImmutableMap> registries = CACHE.getAll(tlds);
ImmutableSet missingRegistries =
@@ -1000,7 +1006,7 @@ public class Registry extends ImmutableObject
instance.renewBillingCostTransitions.checkValidity();
instance.eapFeeSchedule.checkValidity();
// All costs must be in the expected currency.
- // TODO(b/21854155): When we move PremiumList into Datastore, verify its currency too.
+ checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
checkArgument(
instance.getStandardCreateCost().getCurrencyUnit().equals(instance.currency),
"Create cost must be in the registry's currency");
diff --git a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
index 65863fbce..dc592a5bb 100644
--- a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
+++ b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
@@ -24,14 +24,11 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.joda.time.DateTimeZone.UTC;
import com.beust.jcommander.Parameter;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
import google.registry.flows.certs.CertificateChecker;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
-import google.registry.model.tld.Registry;
import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap;
import google.registry.tools.params.OptionalLongParameter;
import google.registry.tools.params.OptionalPhoneNumberParameter;
@@ -46,7 +43,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.money.CurrencyUnit;
@@ -433,22 +429,6 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
// Require a phone passcode.
checkArgument(
newRegistrar.getPhonePasscode() != null, "--passcode is required for REAL registrars.");
- // Check if registrar has billing account IDs for the currency of the TLDs that it is
- // allowed to register.
- ImmutableSet tldCurrencies =
- newRegistrar
- .getAllowedTlds()
- .stream()
- .map(tld -> Registry.get(tld).getCurrency())
- .collect(toImmutableSet());
- Set currenciesWithoutBillingAccountId =
- newRegistrar.getBillingAccountMap() == null
- ? tldCurrencies
- : Sets.difference(tldCurrencies, newRegistrar.getBillingAccountMap().keySet());
- checkArgument(
- currenciesWithoutBillingAccountId.isEmpty(),
- "Need billing account map entries for currencies: %s",
- Joiner.on(' ').join(currenciesWithoutBillingAccountId));
}
stageEntityChange(oldRegistrar, newRegistrar);
diff --git a/core/src/main/java/google/registry/tools/RegistryTool.java b/core/src/main/java/google/registry/tools/RegistryTool.java
index 54e64e459..c24261423 100644
--- a/core/src/main/java/google/registry/tools/RegistryTool.java
+++ b/core/src/main/java/google/registry/tools/RegistryTool.java
@@ -15,6 +15,7 @@
package google.registry.tools;
import com.google.common.collect.ImmutableMap;
+import google.registry.tools.javascrap.BackfillRegistrarBillingAccountsCommand;
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
import google.registry.tools.javascrap.HardDeleteHostCommand;
@@ -30,6 +31,7 @@ public final class RegistryTool {
public static final ImmutableMap> COMMAND_MAP =
new ImmutableMap.Builder>()
.put("ack_poll_messages", AckPollMessagesCommand.class)
+ .put("backfill_registrar_billing_accounts", BackfillRegistrarBillingAccountsCommand.class)
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
.put("check_domain", CheckDomainCommand.class)
.put("check_domain_claims", CheckDomainClaimsCommand.class)
diff --git a/core/src/main/java/google/registry/tools/RegistryToolComponent.java b/core/src/main/java/google/registry/tools/RegistryToolComponent.java
index d882505a2..3945d356b 100644
--- a/core/src/main/java/google/registry/tools/RegistryToolComponent.java
+++ b/core/src/main/java/google/registry/tools/RegistryToolComponent.java
@@ -43,6 +43,7 @@ import google.registry.request.Modules.UrlConnectionServiceModule;
import google.registry.request.Modules.UrlFetchServiceModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.tools.AuthModule.LocalCredentialModule;
+import google.registry.tools.javascrap.BackfillRegistrarBillingAccountsCommand;
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
import google.registry.tools.javascrap.HardDeleteHostCommand;
import google.registry.util.UtilsModule;
@@ -90,6 +91,8 @@ import javax.inject.Singleton;
interface RegistryToolComponent {
void inject(AckPollMessagesCommand command);
+ void inject(BackfillRegistrarBillingAccountsCommand command);
+
void inject(CheckDomainClaimsCommand command);
void inject(CheckDomainCommand command);
diff --git a/core/src/main/java/google/registry/tools/javascrap/BackfillRegistrarBillingAccountsCommand.java b/core/src/main/java/google/registry/tools/javascrap/BackfillRegistrarBillingAccountsCommand.java
new file mode 100644
index 000000000..24afd47f1
--- /dev/null
+++ b/core/src/main/java/google/registry/tools/javascrap/BackfillRegistrarBillingAccountsCommand.java
@@ -0,0 +1,56 @@
+// Copyright 2021 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.tools.javascrap;
+
+import static com.google.common.base.Preconditions.checkState;
+import static google.registry.model.OteAccountBuilder.DEFAULT_BILLING_ACCOUNT_MAP;
+import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
+
+import com.beust.jcommander.Parameters;
+import google.registry.config.RegistryEnvironment;
+import google.registry.model.registrar.Registrar;
+import google.registry.tools.CommandWithRemoteApi;
+
+/**
+ * Backfills the billing account maps on all Registrars that don't have any set.
+ *
+ * This should not (and cannot) be used on production. Its purpose is to backfill these values on
+ * sandbox, where most registrars have an empty billing account map, including all that were created
+ * for OT&E. The same default billing account map is used for all registrars, and includes
+ * values for USD and JPY. The actual values here don't matter as we don't do invoicing on sandbox
+ * anyway.
+ */
+@Parameters(separators = " =", commandDescription = "Backfill registrar billing account maps.")
+public class BackfillRegistrarBillingAccountsCommand implements CommandWithRemoteApi {
+
+ @Override
+ public void run() throws Exception {
+ checkState(
+ RegistryEnvironment.get() != RegistryEnvironment.PRODUCTION,
+ "Do not run this on production");
+ System.out.println("Populating billing account maps on all registrars missing them ...");
+ tm().transact(
+ () ->
+ tm().loadAllOfStream(Registrar.class)
+ .filter(r -> r.getBillingAccountMap().isEmpty())
+ .forEach(
+ r ->
+ tm().update(
+ r.asBuilder()
+ .setBillingAccountMap(DEFAULT_BILLING_ACCOUNT_MAP)
+ .build())));
+ System.out.println("Done!");
+ }
+}
diff --git a/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java b/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java
index 03149667c..9934d15d4 100644
--- a/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java
+++ b/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java
@@ -336,7 +336,11 @@ public class SyncRegistrarsSheetTest {
@TestOfyAndSql
void testRun_missingValues_stillWorks() throws Exception {
- persistNewRegistrar("SomeRegistrar", "Some Registrar", Registrar.Type.REAL, 8L);
+ persistResource(
+ persistNewRegistrar("SomeRegistrar", "Some Registrar", Registrar.Type.REAL, 8L)
+ .asBuilder()
+ .setBillingAccountMap(ImmutableMap.of())
+ .build());
newSyncRegistrarsSheet().run("foobar");
diff --git a/core/src/test/java/google/registry/model/registrar/RegistrarTest.java b/core/src/test/java/google/registry/model/registrar/RegistrarTest.java
index 09cf69081..599eb9b85 100644
--- a/core/src/test/java/google/registry/model/registrar/RegistrarTest.java
+++ b/core/src/test/java/google/registry/model/registrar/RegistrarTest.java
@@ -30,11 +30,14 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
+import static org.joda.money.CurrencyUnit.JPY;
+import static org.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.googlecode.objectify.Key;
import google.registry.config.RegistryConfig;
@@ -42,13 +45,17 @@ import google.registry.model.EntityTestCase;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.Registrar.Type;
import google.registry.model.tld.Registries;
+import google.registry.model.tld.Registry;
+import google.registry.model.tld.Registry.TldType;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import google.registry.util.CidrAddressBlock;
import google.registry.util.SerializeUtils;
+import java.math.BigDecimal;
import org.joda.money.CurrencyUnit;
+import org.joda.money.Money;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link Registrar}. */
@@ -60,7 +67,20 @@ class RegistrarTest extends EntityTestCase {
@BeforeEach
void setUp() {
- createTld("xn--q9jyb4c");
+ createTld("tld");
+ persistResource(
+ newRegistry("xn--q9jyb4c", "MINNA")
+ .asBuilder()
+ .setCurrency(JPY)
+ .setCreateBillingCost(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)))
+ .setRenewBillingCostTransitions(
+ ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1100))))
+ .setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(JPY)))
+ .setPremiumList(null)
+ .build());
// Set up a new persisted registrar entity.
registrar =
cloneAndSetAutoTimestamps(
@@ -251,8 +271,10 @@ class RegistrarTest extends EntityTestCase {
}
@TestOfyAndSql
- void testSuccess_clearingBillingAccountMap() {
- registrar = registrar.asBuilder().setBillingAccountMap(null).build();
+ void testSuccess_clearingBillingAccountMapAndAllowedTlds() {
+ registrar =
+ registrar.asBuilder().setAllowedTlds(ImmutableSet.of()).setBillingAccountMap(null).build();
+ assertThat(registrar.getAllowedTlds()).isEmpty();
assertThat(registrar.getBillingAccountMap()).isEmpty();
}
@@ -677,4 +699,48 @@ class RegistrarTest extends EntityTestCase {
assertThrows(IllegalArgumentException.class, () -> Registrar.loadByRegistrarIdCached(""));
assertThat(thrown).hasMessageThat().contains("registrarId must be specified");
}
+
+ @TestOfyAndSql
+ void testFailure_missingCurrenciesFromBillingMap() {
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ registrar
+ .asBuilder()
+ .setBillingAccountMap(null)
+ .setAllowedTlds(ImmutableSet.of("tld", "xn--q9jyb4c"))
+ .build());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("their currency is missing from the billing account map: [tld, xn--q9jyb4c]");
+ }
+
+ @TestOfyAndSql
+ void testFailure_missingCurrencyFromBillingMap() {
+ IllegalArgumentException thrown =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ registrar
+ .asBuilder()
+ .setBillingAccountMap(ImmutableMap.of(USD, "abc123"))
+ .setAllowedTlds(ImmutableSet.of("tld", "xn--q9jyb4c"))
+ .build());
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("their currency is missing from the billing account map: [xn--q9jyb4c]");
+ }
+
+ @TestOfyAndSql
+ void testSuccess_nonRealTldDoesntNeedEntryInBillingMap() {
+ persistResource(Registry.get("xn--q9jyb4c").asBuilder().setTldType(TldType.TEST).build());
+ // xn--q9jyb4c bills in JPY and we don't have a JPY entry in this billing account map, but it
+ // should succeed without throwing an error because xn--q9jyb4c is set to be a TEST TLD.
+ registrar
+ .asBuilder()
+ .setBillingAccountMap(ImmutableMap.of(USD, "abc123"))
+ .setAllowedTlds(ImmutableSet.of("tld", "xn--q9jyb4c"))
+ .build();
+ }
}
diff --git a/core/src/test/java/google/registry/model/tld/RegistryTest.java b/core/src/test/java/google/registry/model/tld/RegistryTest.java
index 38b6d3a6e..5db5f8607 100644
--- a/core/src/test/java/google/registry/model/tld/RegistryTest.java
+++ b/core/src/test/java/google/registry/model/tld/RegistryTest.java
@@ -190,7 +190,7 @@ public final class RegistryTest extends EntityTestCase {
@TestOfyAndSql
void testGetAll() {
createTld("foo");
- assertThat(Registry.getAll(ImmutableSet.of("foo", "tld")))
+ assertThat(Registry.get(ImmutableSet.of("foo", "tld")))
.containsExactlyElementsIn(
tm().transact(
() ->
diff --git a/core/src/test/java/google/registry/testing/AppEngineExtension.java b/core/src/test/java/google/registry/testing/AppEngineExtension.java
index 0b46155a9..086159828 100644
--- a/core/src/test/java/google/registry/testing/AppEngineExtension.java
+++ b/core/src/test/java/google/registry/testing/AppEngineExtension.java
@@ -66,6 +66,7 @@ import java.util.Set;
import java.util.logging.LogManager;
import java.util.stream.Stream;
import javax.annotation.Nullable;
+import org.joda.money.CurrencyUnit;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -289,6 +290,7 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
.build())
.setPhoneNumber("+1.3334445555")
.setPhonePasscode("12345")
+ .setBillingAccountMap(ImmutableMap.of(CurrencyUnit.USD, "abc123"))
.setContactsRequireSyncing(true);
}
diff --git a/core/src/test/java/google/registry/testing/DatabaseHelper.java b/core/src/test/java/google/registry/testing/DatabaseHelper.java
index 2e6c9a7b9..9f26de4b1 100644
--- a/core/src/test/java/google/registry/testing/DatabaseHelper.java
+++ b/core/src/test/java/google/registry/testing/DatabaseHelper.java
@@ -762,6 +762,7 @@ public class DatabaseHelper {
.setType(type)
.setState(State.ACTIVE)
.setIanaIdentifier(ianaIdentifier)
+ .setBillingAccountMap(ImmutableMap.of(USD, "abc123"))
.setLocalizedAddress(
new RegistrarAddress.Builder()
.setStreet(ImmutableList.of("123 Fake St"))
diff --git a/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java b/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java
index 62cb93dce..ba479e71e 100644
--- a/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java
+++ b/core/src/test/java/google/registry/tools/CreateRegistrarCommandTest.java
@@ -21,8 +21,11 @@ import static google.registry.testing.CertificateSamples.SAMPLE_CERT;
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3;
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3_HASH;
import static google.registry.testing.DatabaseHelper.createTlds;
+import static google.registry.testing.DatabaseHelper.newRegistry;
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
+import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
+import static org.joda.money.CurrencyUnit.JPY;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -43,8 +46,10 @@ import google.registry.testing.DualDatabaseTest;
import google.registry.testing.InjectExtension;
import google.registry.testing.TestOfyAndSql;
import java.io.IOException;
+import java.math.BigDecimal;
import java.util.Optional;
import org.joda.money.CurrencyUnit;
+import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -604,11 +609,11 @@ class CreateRegistrarCommandTest extends CommandTestCase
Optional registrar = Registrar.loadByRegistrarId("clientz");
assertThat(registrar).isPresent();
assertThat(registrar.get().getBillingAccountMap())
- .containsExactly(CurrencyUnit.USD, "abc123", CurrencyUnit.JPY, "789xyz");
+ .containsExactly(CurrencyUnit.USD, "abc123", JPY, "789xyz");
}
@TestOfyAndSql
- void testFailure_billingAccountMap_doesNotContainEntryForTldAllowed() {
+ void testFailure_billingAccountMap_doesNotContainEntryForAllowedTld() {
createTlds("foo");
IllegalArgumentException thrown =
@@ -632,12 +637,26 @@ class CreateRegistrarCommandTest extends CommandTestCase
"--cc US",
"--force",
"clientz"));
- assertThat(thrown).hasMessageThat().contains("USD");
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("their currency is missing from the billing account map: [foo]");
}
@TestOfyAndSql
void testSuccess_billingAccountMap_onlyAppliesToRealRegistrar() throws Exception {
- createTlds("foo");
+ persistResource(
+ newRegistry("foo", "FOO")
+ .asBuilder()
+ .setCurrency(JPY)
+ .setCreateBillingCost(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)))
+ .setRenewBillingCostTransitions(
+ ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1100))))
+ .setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(JPY)))
+ .setPremiumList(null)
+ .build());
runCommandForced(
"--name=blobio",
@@ -656,7 +675,7 @@ class CreateRegistrarCommandTest extends CommandTestCase
Optional registrar = Registrar.loadByRegistrarId("clientz");
assertThat(registrar).isPresent();
- assertThat(registrar.get().getBillingAccountMap()).containsExactly(CurrencyUnit.JPY, "789xyz");
+ assertThat(registrar.get().getBillingAccountMap()).containsExactly(JPY, "789xyz");
}
@TestOfyAndSql
diff --git a/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java b/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java
index 4cb894f44..b149cb6db 100644
--- a/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java
+++ b/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java
@@ -21,8 +21,10 @@ import static google.registry.testing.CertificateSamples.SAMPLE_CERT3;
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3_HASH;
import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
+import static google.registry.testing.DatabaseHelper.newRegistry;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
+import static org.joda.money.CurrencyUnit.JPY;
import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -38,8 +40,10 @@ import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.Registrar.Type;
import google.registry.testing.AppEngineExtension;
import google.registry.util.CidrAddressBlock;
+import java.math.BigDecimal;
import java.util.Optional;
import org.joda.money.CurrencyUnit;
+import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -359,6 +363,8 @@ class UpdateRegistrarCommandTest extends CommandTestCase
@Test
void testSuccess_billingAccountMap() throws Exception {
+ persistResource(
+ loadRegistrar("NewRegistrar").asBuilder().setBillingAccountMap(ImmutableMap.of()).build());
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap()).isEmpty();
runCommand("--billing_account_map=USD=abc123,JPY=789xyz", "--force", "NewRegistrar");
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap())
@@ -366,8 +372,14 @@ class UpdateRegistrarCommandTest extends CommandTestCase
}
@Test
- void testFailure_billingAccountMap_doesNotContainEntryForTldAllowed() {
+ void testFailure_billingAccountMap_doesNotContainEntryForAllowedTld() {
createTlds("foo");
+ persistResource(
+ loadRegistrar("NewRegistrar")
+ .asBuilder()
+ .setAllowedTlds(ImmutableSet.of())
+ .setBillingAccountMap(ImmutableMap.of())
+ .build());
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap()).isEmpty();
IllegalArgumentException thrown =
assertThrows(
@@ -379,12 +391,28 @@ class UpdateRegistrarCommandTest extends CommandTestCase
"--force",
"--registrar_type=REAL",
"NewRegistrar"));
- assertThat(thrown).hasMessageThat().contains("USD");
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("their currency is missing from the billing account map: [foo]");
}
@Test
void testSuccess_billingAccountMap_onlyAppliesToRealRegistrar() throws Exception {
- createTlds("foo");
+ persistResource(
+ newRegistry("foo", "FOO")
+ .asBuilder()
+ .setCurrency(JPY)
+ .setCreateBillingCost(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)))
+ .setRenewBillingCostTransitions(
+ ImmutableSortedMap.of(START_OF_TIME, Money.of(JPY, new BigDecimal(1100))))
+ .setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(JPY)))
+ .setPremiumList(null)
+ .build());
+ persistResource(
+ loadRegistrar("NewRegistrar").asBuilder().setBillingAccountMap(ImmutableMap.of()).build());
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap()).isEmpty();
runCommand("--billing_account_map=JPY=789xyz", "--allowed_tlds=foo", "--force", "NewRegistrar");
assertThat(loadRegistrar("NewRegistrar").getBillingAccountMap())